diff --git a/.bazelignore b/.bazelignore index c55c9439ca..afaf6167ff 100644 --- a/.bazelignore +++ b/.bazelignore @@ -17,6 +17,7 @@ integration/bazel-schematics/demo # All integration test node_modules folders integration/bazel/node_modules integration/bazel-schematics/node_modules +integration/cli-elements-universal/node_modules integration/cli-hello-world/node_modules integration/cli-hello-world-ivy-compat/node_modules integration/cli-hello-world-ivy-i18n/node_modules @@ -44,6 +45,7 @@ integration/typings_test_ts37/node_modules # All integration test .yarn_local_cache folders integration/bazel/.yarn_local_cache integration/bazel-schematics/.yarn_local_cache +integration/cli-elements-universal/.yarn_local_cache integration/cli-hello-world/.yarn_local_cache integration/cli-hello-world-ivy-compat/.yarn_local_cache integration/cli-hello-world-ivy-i18n/.yarn_local_cache @@ -71,6 +73,7 @@ integration/typings_test_ts37/.yarn_local_cache # All integration test NPM_PACKAGE_MANIFEST.json folders integration/bazel/NPM_PACKAGE_MANIFEST.json integration/bazel-schematics/NPM_PACKAGE_MANIFEST.json +integration/cli-elements-universal/NPM_PACKAGE_MANIFEST.json integration/cli-hello-world/NPM_PACKAGE_MANIFEST.json integration/cli-hello-world-ivy-compat/NPM_PACKAGE_MANIFEST.json integration/cli-hello-world-ivy-i18n/NPM_PACKAGE_MANIFEST.json diff --git a/.bazelrc b/.bazelrc index 1f07b2fb6d..6f7a24ff89 100644 --- a/.bazelrc +++ b/.bazelrc @@ -47,9 +47,13 @@ build --nobuild_runfile_links # Releases should always be stamped with version control info # This command assumes node on the path and is a workaround for # https://github.com/bazelbuild/bazel/issues/4802 -build:release --workspace_status_command="yarn -s ng-dev release build-env-stamp" +build:release --workspace_status_command="yarn -s ng-dev release build-env-stamp --mode=release" build:release --stamp +# Snapshots should also be stamped with version control information. +build:snapshot --workspace_status_command="yarn -s ng-dev release build-env-stamp --mode=snapshot" +build:snapshot --stamp + ############################### # Output # ############################### @@ -136,6 +140,13 @@ build:remote --remote_executor=remotebuildexecution.googleapis.com # retry mechanism and we do not want to retry unnecessarily if Karma already tried multiple times. test:saucelabs --flaky_test_attempts=1 +################ +# Flag Aliases # +################ + +# --ng_perf will ask the Ivy compiler to produce performance results for each build. +build --flag_alias=ng_perf=//packages/compiler-cli:ng_perf + #################################################### # User bazel configuration # NOTE: This needs to be the *last* entry in the config. diff --git a/.bazelversion b/.bazelversion index a57c344a29..21e00cfe03 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1,3 +1,3 @@ -3.6.0 +4.0.0 # [NB: this comment has to be after the first line, see https://github.com/bazelbuild/bazelisk/issues/117] # When updating the Bazel version you also need to update the RBE toolchains version in package.bzl diff --git a/.circleci/bazel.linux.rc b/.circleci/bazel.linux.rc index 9bbc339b20..4f6ebc4e43 100644 --- a/.circleci/bazel.linux.rc +++ b/.circleci/bazel.linux.rc @@ -20,11 +20,4 @@ build --local_ram_resources=32768 # All build executed remotely should be done using our RBE configuration. build:remote --google_default_credentials -# Upload to GCP's Build Status viewer to allow for us to have better viewing of execution/build -# logs. This is only done on CI as the BES (GCP's Build Status viewer) API requires credentials -# from service accounts, rather than end user accounts. -build:remote --bes_backend=buildeventservice.googleapis.com -build:remote --bes_timeout=30s -build:remote --bes_results_url="https://source.cloud.google.com/results/invocations/" - build --config=remote diff --git a/.circleci/config.yml b/.circleci/config.yml index ed6f77d700..a9b3a88a49 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -261,7 +261,7 @@ jobs: - run: yarn -s ts-circular-deps:check - run: yarn -s ng-dev pullapprove verify - run: yarn -s ng-dev ngbot verify - - run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE + - run: yarn -s ng-dev commit-message validate-range $CI_GIT_BASE_REVISION $CI_GIT_REVISION test: executor: @@ -447,7 +447,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.viewengine >>--viewengine<> --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} # This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`. aio_preview: @@ -770,7 +770,7 @@ jobs: no_output_timeout: 15m - run: name: Test all windows CI targets - command: yarn bazel test --config=ivy --build_tag_filters=-no-windows --test_tag_filters="-no-ivy-aot,-no-windows,-fixme-ivy-aot,-browser:chromium-local" //packages/compiler-cli/... //tools/ts-api-guardian/... + command: yarn bazel test --config=ivy --build_tag_filters=-no-windows --test_tag_filters="-no-ivy-aot,-no-windows,-fixme-ivy-aot,-browser:chromium-local" //packages/compiler-cli/... //tools/ts-api-guardian/... //packages/localize/... no_output_timeout: 15m diff --git a/.circleci/env.sh b/.circleci/env.sh index 93b4130587..3eaa031794 100755 --- a/.circleci/env.sh +++ b/.circleci/env.sh @@ -107,7 +107,7 @@ echo "export CI_SECRET_PAYLOAD_FIREBASE_TOKEN=\"${CI_SECRET_PAYLOAD_FIREBASE_TOK #################################################################################################### #################################################################################################### ## Source `$BASH_ENV` to make the variables available immediately. ## -## ***NOTE: This must remain the the last action in this script*** ## +## ***NOTE: This must remain the last action in this script*** ## #################################################################################################### #################################################################################################### source $BASH_ENV; diff --git a/.circleci/rebase-pr.js b/.circleci/rebase-pr.js index 085ab4398d..29d19acb04 100644 --- a/.circleci/rebase-pr.js +++ b/.circleci/rebase-pr.js @@ -92,7 +92,7 @@ async function _main() { * likely correct branch will be the first one encountered in the list. */ function getRefFromBranchList(gitOutput) { - const branches = gitOutput.split('\n').map(b => b.split('/').slice(1).join('').trim()); + const branches = gitOutput.split('\n').map(b => b.split('/').slice(1).join('/').trim()); return branches.sort((a, b) => { if (a === 'master') { return -1; @@ -152,7 +152,7 @@ function getCommonAncestorSha(sha1, sha2) { */ function addAndFetchRemote(owner, name) { const remoteName = `${owner}_${name}`; - exec(`git remote add ${remoteName} https://github.com/${owner}/${name}.git`, false); + exec(`git remote add ${remoteName} https://github.com/${owner}/${name}.git`, true); exec(`git fetch ${remoteName}`); return remoteName; } @@ -194,14 +194,14 @@ function getRefsAndShasForChange() { * * Return the trimmed stdout as a string, with an added attribute of the exit code. */ -function exec(command, allowStderr = true) { - let output = new String(); - output.code = 0; +function exec(command, ignoreError = false) { try { - output += execSync(command, {stdio: ['pipe', 'pipe', 'pipe']}).toString().trim(); + return execSync(command, {stdio: 'pipe'}).toString().trim(); } catch (err) { - allowStderr && console.error(err.stderr.toString()); - output.code = err.status; + if (ignoreError) { + return ''; + } + + throw err; } - return output; } diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 79c5759fb1..6c93540407 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,4 +1,4 @@ -# VSCode Remote Development - Developing inside a Containers +# VSCode Remote Development - Developing inside a Container This folder contains configuration files that can be used to opt into working on this repository in a [Docker container](https://www.docker.com/resources/what-container) via [VSCode](https://code.visualstudio.com/)'s Remote Development feature (see below). diff --git a/.devcontainer/recommended-Dockerfile b/.devcontainer/recommended-Dockerfile index 251fc54224..87379c1e06 100644 --- a/.devcontainer/recommended-Dockerfile +++ b/.devcontainer/recommended-Dockerfile @@ -1,6 +1,8 @@ # Image metadata and config. -FROM circleci/node:10-browsers # Ideally, the image version should be what we use on CI. - # See `executors > browsers-executor` in `.circleci/config.yml`. +# Ideally, the image version should be what we use on CI. +# See `executors > browsers-executor` in `.circleci/config.yml`. +FROM circleci/node:10-browsers + LABEL name="Angular dev environment" \ description="This image can be used to create a dev environment for building Angular." \ @@ -16,8 +18,10 @@ USER root # Configure `Node.js`/`npm` and install utilities. RUN npm config --global set user root -RUN npm install --global yarn@latest # Ideally, the version should be what we use on CI. - # See `commands > overwrite_yarn` in `.circleci/config.yml`. + +# Ideally, the version should be what we use on CI. +# See `commands > overwrite_yarn` in `.circleci/config.yml`. +RUN npm install --global yarn@latest # Go! (And keep going.) diff --git a/.github/ISSUE_TEMPLATE/1-bug-report.md b/.github/ISSUE_TEMPLATE/1-bug-report.md index 4785c8fb39..8cf9b32bb1 100644 --- a/.github/ISSUE_TEMPLATE/1-bug-report.md +++ b/.github/ISSUE_TEMPLATE/1-bug-report.md @@ -43,7 +43,7 @@ Share the link to the repo below along with step-by-step instructions to reprodu Issues that don't have enough info and can't be reproduced will be closed. -You can read more about issue submission guidelines here: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-submitting-an-issue +You can read more about issue submission guidelines here: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#submit-issue --> ## 🔥 Exception or Error diff --git a/.github/angular-robot.yml b/.github/angular-robot.yml index e8298b0485..2a8fe4d8f0 100644 --- a/.github/angular-robot.yml +++ b/.github/angular-robot.yml @@ -187,8 +187,6 @@ triagePR: # arrays of labels that determine if a PR has been fully triaged l2TriageLabels: - - - "effort*" - - "risk*" - "comp: *" # options for rerunning CI diff --git a/.github/workflows/lock-closed.yml b/.github/workflows/lock-closed.yml index d986088b6d..69f2aab2f9 100644 --- a/.github/workflows/lock-closed.yml +++ b/.github/workflows/lock-closed.yml @@ -10,6 +10,6 @@ jobs: if: github.repository == 'angular/angular' runs-on: ubuntu-latest steps: - - uses: angular/dev-infra/github-actions/lock-closed@0fc6f4d839e93312ed0dd9a2be88d4c11e947a0b + - uses: angular/dev-infra/github-actions/lock-closed@7679cff885633cd33bf5ac7922a5304e8971a5a6 with: lock-bot-key: ${{ secrets.LOCK_BOT_PRIVATE_KEY }} diff --git a/.gitignore b/.gitignore index 87f88c1c59..7a597d9a6c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ pubspec.lock .vscode/settings.json .vscode/tasks.json *.swo +*.swp modules/.settings modules/.vscode .vimrc diff --git a/.ng-dev/commit-message.ts b/.ng-dev/commit-message.ts index 2e18e9eaad..4a74d244d7 100644 --- a/.ng-dev/commit-message.ts +++ b/.ng-dev/commit-message.ts @@ -4,7 +4,7 @@ import {CommitMessageConfig} from '../dev-infra/commit-message/config'; * The configuration for `ng-dev commit-message` commands. */ export const commitMessage: CommitMessageConfig = { - maxLineLength: 120, + maxLineLength: Infinity, minBodyLength: 20, minBodyLengthTypeExcludes: ['docs'], scopes: [ diff --git a/.ng-dev/format.ts b/.ng-dev/format.ts index e6328eca9c..e33f60d4af 100644 --- a/.ng-dev/format.ts +++ b/.ng-dev/format.ts @@ -18,6 +18,7 @@ export const format: FormatConfig = { '!**/*.d.ts', // Do not format generated ng-dev script '!dev-infra/ng-dev.js', + '!dev-infra/build-worker.js', // Do not format compliance test-cases since they must match generated code '!packages/compiler-cli/test/compliance/test_cases/**/*.js', ] diff --git a/.ng-dev/release.ts b/.ng-dev/release.ts index 1867eab1b3..912b281d6a 100644 --- a/.ng-dev/release.ts +++ b/.ng-dev/release.ts @@ -4,6 +4,7 @@ import {ReleaseConfig} from '../dev-infra/release/config'; /** Configuration for the `ng-dev release` command. */ export const release: ReleaseConfig = { + publishRegistry: 'https://wombat-dressing-room.appspot.com', npmPackages: [ '@angular/animations', '@angular/bazel', @@ -26,10 +27,11 @@ export const release: ReleaseConfig = { // The buildTargetPackages function is loaded at runtime as the loading the script causes an // invocation of bazel. const {buildTargetPackages} = require(join(__dirname, '../scripts/build/package-builder')); - return buildTargetPackages('dist/release-output', false, 'Release'); + return buildTargetPackages('dist/release-output', false, 'Release', true); }, // TODO: This can be removed once there is an org-wide tool for changelog generation. generateReleaseNotesForHead: async () => { exec('yarn -s gulp changelog', {cwd: join(__dirname, '../')}); }, + releasePrLabels: ['comp: build & ci', 'action: merge', 'PullApprove: disable'], }; diff --git a/.pullapprove.yml b/.pullapprove.yml index bd6855be8c..879dafe001 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -172,6 +172,7 @@ groups: request: 0 # Do not request any reviews from the reviewer group required: 1 # Require that all PRs have approval from at least one of the users in the group author_value: 0 # The author of the PR cannot provide an approval for themself + reviewed_for: ignored # All reviews apply to this group whether noted via Reviewed-for or not reviewers: users: - aikidave # Dave Shevitz @@ -207,7 +208,8 @@ groups: - petebacondarwin # Pete Bacon Darwin - pkozlowski-opensource # Pawel Kozlowski - Splaktar # Michael Prentice - - StephenFluin # Stephen Fluin + - twerske # Emma Twersky + - zarend # Zach Arend # ========================================================= # Framework: Animations @@ -291,7 +293,7 @@ groups: users: - alxhub - crisbeto - # OOO as of 2020-09-28 - devversion + - devversion # ========================================================= @@ -306,11 +308,9 @@ groups: contains_any_globs(files.exclude("packages/core/schematics/**"), [ 'packages/core/**', 'packages/examples/core/**', - 'packages/common/**', 'packages/platform-browser/**', 'packages/examples/platform-browser/**', 'packages/platform-browser-dynamic/**', - 'packages/examples/common/**', 'packages/docs/**', 'aio/content/guide/accessibility.md', 'aio/content/examples/accessibility/**', @@ -435,6 +435,30 @@ groups: # OOO as of 2020-09-28 - pkozlowski-opensource + # ========================================================= + # Framework: Common + # ========================================================= + fw-common: + <<: *defaults + conditions: + - *can-be-global-approved + - *can-be-global-docs-approved + - > + contains_any_globs(files.exclude("packages/core/schematics/**"), [ + 'packages/common/**', + 'packages/examples/common/**', + ]) + reviewers: + users: + - alxhub + - AndrewKushnir + - atscott + - ~kara # do not request reviews from Kara, but allow her to approve PRs + - mhevery + - jessicajaniuk + # OOO as of 2020-09-28 - pkozlowski-opensource + + # ========================================================= # Framework: Http # ========================================================= @@ -455,6 +479,7 @@ groups: users: - alxhub - IgorMinar + - petebacondarwin # ========================================================= @@ -657,7 +682,7 @@ groups: - *can-be-global-approved - *can-be-global-docs-approved - > - contains_any_globs(files.exclude('packages/compiler-cli/**'), [ + contains_any_globs(files.exclude('packages/compiler-cli/**').exclude('packages/language-service/**'), [ 'packages/**/testing/**', 'aio/content/guide/testing.md', 'aio/content/guide/test-debugging.md', @@ -778,8 +803,9 @@ groups: ]) reviewers: users: - - ayazhafiz - kyliau + - atscott + - zarend # ========================================================= @@ -852,6 +878,25 @@ groups: - mhevery + # ========================================================= + # Docs: Contributors + # ========================================================= + docs-contributors: + <<: *defaults + conditions: + - *can-be-global-approved + - *can-be-global-docs-approved + - > + contains_any_globs(files, [ + 'aio/content/marketing/contributors.json', + 'aio/content/images/bios/**', + ]) + reviewers: + users: + - mgechev + - twerske + + # ========================================================= # Docs: Gettings Started & Tutorial # ========================================================= @@ -876,13 +921,15 @@ groups: 'aio/content/examples/getting-started-v0/**', 'aio/content/examples/getting-started/**', 'aio/content/start/**', - 'aio/content/images/guide/start/**' + 'aio/content/images/guide/start/**', + 'aio/content/examples/what-is-angular/**', + 'aio/content/guide/what-is-angular.md' ]) reviewers: users: - aikidave - IgorMinar - - StephenFluin + - jelbourn # ========================================================= @@ -894,10 +941,9 @@ groups: - *can-be-global-approved - *can-be-global-docs-approved - > - contains_any_globs(files, [ + contains_any_globs(files.exclude("aio/content/marketing/contributors.json"), [ 'aio/content/guide/roadmap.md', 'aio/content/marketing/**', - 'aio/content/images/bios/**', 'aio/content/images/marketing/**', 'aio/content/file-not-found.md', 'aio/content/license.md', @@ -907,7 +953,7 @@ groups: users: - aikidave - IgorMinar - - StephenFluin + - mgechev # ========================================================= # Docs: Observables @@ -1093,9 +1139,14 @@ groups: 'aio/src/**', 'aio/tests/**', 'aio/tools/**', + 'aio/content/images/guide/contributors-guide/**', + 'aio/content/guide/contributors-guide-overview.md', 'aio/content/guide/docs-style-guide.md', 'aio/content/examples/docs-style-guide/**', 'aio/content/images/guide/docs-style-guide/**', + 'aio/content/guide/reviewing-content.md', + 'aio/content/guide/updating-content-github-ui.md', + 'aio/content/guide/updating-search-keywords.md', 'aio/content/guide/visual-studio-2015.md' ]) reviewers: @@ -1113,7 +1164,7 @@ groups: conditions: - *can-be-global-approved - > - contains_any_globs(files.exclude("CHANGELOG.md").exclude("packages/compiler-cli/**/BUILD.bazel"), [ + contains_any_globs(files, [ '*', '.circleci/**', '.devcontainer/**', @@ -1123,19 +1174,7 @@ groups: '.vscode/**', '.yarn/**', 'dev-infra/**', - 'docs/BAZEL.md', - 'docs/CARETAKER.md', - 'docs/CODING_STANDARDS.md', - 'docs/COMMITTER.md', - 'docs/DEBUG.md', - 'docs/DEBUG_COMPONENTS_REPO_IVY.md', - 'docs/DEVELOPER.md', - 'docs/FIXUP_COMMITS.md', - 'docs/GITHUB_PROCESS.md', - 'docs/PR_REVIEW.md', - 'docs/SAVED_REPLIES.md', - 'docs/TOOLS.md', - 'docs/TRIAGE_AND_LABELS.md', + 'docs/*.md', 'docs/images/**', 'goldens/*', 'modules/*', @@ -1162,12 +1201,11 @@ groups: 'tools/utils/**', 'tools/yarn/**', 'tools/*', - '**/*.bzl', - '**/*.bazel' + '**/*.bzl' ]) reviewers: users: - # OOO as of 2020-09-28 - devversion + - devversion - filipesilva - gkalpak - IgorMinar @@ -1186,10 +1224,11 @@ groups: - > contains_any_globs(files, [ 'goldens/public-api/**', - 'CHANGELOG.md', 'docs/NAMING.md', + 'aio/content/errors/*.md', 'aio/content/guide/glossary.md', 'aio/content/guide/styleguide.md', + 'aio/content/examples/errors/**', 'aio/content/examples/styleguide/**', 'aio/content/images/guide/styleguide/*' ]) @@ -1201,6 +1240,7 @@ groups: - atscott - jelbourn - petebacondarwin + - jessicajaniuk # OOO as of 2020-09-28 - pkozlowski-opensource reviews: request: 4 # Request reviews from four people @@ -1229,6 +1269,7 @@ groups: - atscott - jelbourn - petebacondarwin + - jessicajaniuk # OOO as of 2020-09-28 - pkozlowski-opensource reviews: request: 4 # Request reviews from four people @@ -1257,6 +1298,7 @@ groups: - atscott - jelbourn - petebacondarwin + - jessicajaniuk # OOO as of 2020-09-28 - pkozlowski-opensource diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a8de13cc60..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: node_js -sudo: false -dist: trusty -node_js: - - 12.14.1 -addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-4.8 - chrome: stable -branches: - except: - - g3 -cache: false -before_install: - - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.21.1 - - export PATH="$HOME/.yarn/bin:$PATH" - - google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost & -script: - - "./aio/deploy-cn.sh" diff --git a/.vscode/recommended-launch.json b/.vscode/recommended-launch.json index cd29cdd0f6..9b08e7c762 100644 --- a/.vscode/recommended-launch.json +++ b/.vscode/recommended-launch.json @@ -16,6 +16,7 @@ "remoteRoot": "${workspaceRoot}", "stopOnEntry": false, "timeout": 600000, + "outFiles": ["${workspaceFolder}/bazel-out/**/angular/**/*.js"], }, { "name": "Attach to bazel test ... --config=debug (no source maps)", @@ -29,6 +30,7 @@ "remoteRoot": "${workspaceRoot}", "stopOnEntry": false, "timeout": 600000, + "outFiles": ["${workspaceFolder}/bazel-out/**/angular/**/*.js"], }, { "name": "IVY:packages/core/test/acceptance", @@ -46,6 +48,7 @@ "restart": true, "sourceMaps": true, "timeout": 600000, + "outFiles": ["${workspaceFolder}/bazel-out/**/angular/**/*.js"], }, { "name": "IVY:packages/core/test/render3", @@ -63,6 +66,7 @@ "restart": true, "sourceMaps": true, "timeout": 600000, + "outFiles": ["${workspaceFolder}/bazel-out/**/angular/**/*.js"], }, { "name": "IVY:packages/core/test", @@ -80,6 +84,7 @@ "restart": true, "sourceMaps": true, "timeout": 600000, + "outFiles": ["${workspaceFolder}/bazel-out/**/angular/**/*.js"], }, ] } diff --git a/.yarn/releases/yarn-1.22.5.js b/.yarn/releases/yarn-1.22.5.js deleted file mode 100755 index d31be0b947..0000000000 --- a/.yarn/releases/yarn-1.22.5.js +++ /dev/null @@ -1,147392 +0,0 @@ -#!/usr/bin/env node -module.exports = -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // identity function for calling harmony imports with the correct context -/******/ __webpack_require__.i = function(value) { return value; }; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { -/******/ configurable: false, -/******/ enumerable: true, -/******/ get: getter -/******/ }); -/******/ } -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 549); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ (function(module, exports) { - -module.exports = require("path"); - -/***/ }), -/* 1 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -/* harmony export (immutable) */ __webpack_exports__["a"] = __extends; -/* unused harmony export __assign */ -/* unused harmony export __rest */ -/* unused harmony export __decorate */ -/* unused harmony export __param */ -/* unused harmony export __metadata */ -/* unused harmony export __awaiter */ -/* unused harmony export __generator */ -/* unused harmony export __exportStar */ -/* unused harmony export __values */ -/* unused harmony export __read */ -/* unused harmony export __spread */ -/* unused harmony export __await */ -/* unused harmony export __asyncGenerator */ -/* unused harmony export __asyncDelegator */ -/* unused harmony export __asyncValues */ -/* unused harmony export __makeTemplateObject */ -/* unused harmony export __importStar */ -/* unused harmony export __importDefault */ -/*! ***************************************************************************** -Copyright (c) Microsoft Corporation. All rights reserved. -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at http://www.apache.org/licenses/LICENSE-2.0 - -THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED -WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, -MERCHANTABLITY OR NON-INFRINGEMENT. - -See the Apache Version 2.0 License for specific language governing permissions -and limitations under the License. -***************************************************************************** */ -/* global Reflect, Promise */ - -var extendStatics = function(d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); -}; - -function __extends(d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); -} - -var __assign = function() { - __assign = Object.assign || function __assign(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - } - return t; - } - return __assign.apply(this, arguments); -} - -function __rest(s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) - t[p[i]] = s[p[i]]; - return t; -} - -function __decorate(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -} - -function __param(paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } -} - -function __metadata(metadataKey, metadataValue) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); -} - -function __awaiter(thisArg, _arguments, P, generator) { - 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); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -} - -function __generator(thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -} - -function __exportStar(m, exports) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -} - -function __values(o) { - var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; - if (m) return m.call(o); - return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; -} - -function __read(o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -} - -function __spread() { - for (var ar = [], i = 0; i < arguments.length; i++) - ar = ar.concat(__read(arguments[i])); - return ar; -} - -function __await(v) { - return this instanceof __await ? (this.v = v, this) : new __await(v); -} - -function __asyncGenerator(thisArg, _arguments, generator) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var g = generator.apply(thisArg, _arguments || []), i, q = []; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; - function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } - function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } - function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } - function fulfill(value) { resume("next", value); } - function reject(value) { resume("throw", value); } - function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } -} - -function __asyncDelegator(o) { - var i, p; - return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; } -} - -function __asyncValues(o) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var m = o[Symbol.asyncIterator], i; - return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); - function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } - function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } -} - -function __makeTemplateObject(cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; - -function __importStar(mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result.default = mod; - return result; -} - -function __importDefault(mod) { - return (mod && mod.__esModule) ? mod : { default: mod }; -} - - -/***/ }), -/* 2 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -exports.__esModule = true; - -var _promise = __webpack_require__(227); - -var _promise2 = _interopRequireDefault(_promise); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -exports.default = function (fn) { - return function () { - var gen = fn.apply(this, arguments); - return new _promise2.default(function (resolve, reject) { - function step(key, arg) { - try { - var info = gen[key](arg); - var value = info.value; - } catch (error) { - reject(error); - return; - } - - if (info.done) { - resolve(value); - } else { - return _promise2.default.resolve(value).then(function (value) { - step("next", value); - }, function (err) { - step("throw", err); - }); - } - } - - return step("next"); - }); - }; -}; - -/***/ }), -/* 3 */ -/***/ (function(module, exports) { - -module.exports = require("util"); - -/***/ }), -/* 4 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.getFirstSuitableFolder = exports.readFirstAvailableStream = exports.makeTempDir = exports.hardlinksWork = exports.writeFilePreservingEol = exports.getFileSizeOnDisk = exports.walk = exports.symlink = exports.find = exports.readJsonAndFile = exports.readJson = exports.readFileAny = exports.hardlinkBulk = exports.copyBulk = exports.unlink = exports.glob = exports.link = exports.chmod = exports.lstat = exports.exists = exports.mkdirp = exports.stat = exports.access = exports.rename = exports.readdir = exports.realpath = exports.readlink = exports.writeFile = exports.open = exports.readFileBuffer = exports.lockQueue = exports.constants = undefined; - -var _asyncToGenerator2; - -function _load_asyncToGenerator() { - return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(2)); -} - -let buildActionsForCopy = (() => { - var _ref = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, events, possibleExtraneous, reporter) { - - // - let build = (() => { - var _ref5 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { - const src = data.src, - dest = data.dest, - type = data.type; - - const onFresh = data.onFresh || noop; - const onDone = data.onDone || noop; - - // TODO https://github.com/yarnpkg/yarn/issues/3751 - // related to bundled dependencies handling - if (files.has(dest.toLowerCase())) { - reporter.verbose(`The case-insensitive file ${dest} shouldn't be copied twice in one bulk copy`); - } else { - files.add(dest.toLowerCase()); - } - - if (type === 'symlink') { - yield mkdirp((_path || _load_path()).default.dirname(dest)); - onFresh(); - actions.symlink.push({ - dest, - linkname: src - }); - onDone(); - return; - } - - if (events.ignoreBasenames.indexOf((_path || _load_path()).default.basename(src)) >= 0) { - // ignored file - return; - } - - const srcStat = yield lstat(src); - let srcFiles; - - if (srcStat.isDirectory()) { - srcFiles = yield readdir(src); - } - - let destStat; - try { - // try accessing the destination - destStat = yield lstat(dest); - } catch (e) { - // proceed if destination doesn't exist, otherwise error - if (e.code !== 'ENOENT') { - throw e; - } - } - - // if destination exists - if (destStat) { - const bothSymlinks = srcStat.isSymbolicLink() && destStat.isSymbolicLink(); - const bothFolders = srcStat.isDirectory() && destStat.isDirectory(); - const bothFiles = srcStat.isFile() && destStat.isFile(); - - // EINVAL access errors sometimes happen which shouldn't because node shouldn't be giving - // us modes that aren't valid. investigate this, it's generally safe to proceed. - - /* if (srcStat.mode !== destStat.mode) { - try { - await access(dest, srcStat.mode); - } catch (err) {} - } */ - - if (bothFiles && artifactFiles.has(dest)) { - // this file gets changed during build, likely by a custom install script. Don't bother checking it. - onDone(); - reporter.verbose(reporter.lang('verboseFileSkipArtifact', src)); - return; - } - - if (bothFiles && srcStat.size === destStat.size && (0, (_fsNormalized || _load_fsNormalized()).fileDatesEqual)(srcStat.mtime, destStat.mtime)) { - // we can safely assume this is the same file - onDone(); - reporter.verbose(reporter.lang('verboseFileSkip', src, dest, srcStat.size, +srcStat.mtime)); - return; - } - - if (bothSymlinks) { - const srcReallink = yield readlink(src); - if (srcReallink === (yield readlink(dest))) { - // if both symlinks are the same then we can continue on - onDone(); - reporter.verbose(reporter.lang('verboseFileSkipSymlink', src, dest, srcReallink)); - return; - } - } - - if (bothFolders) { - // mark files that aren't in this folder as possibly extraneous - const destFiles = yield readdir(dest); - invariant(srcFiles, 'src files not initialised'); - - for (var _iterator4 = destFiles, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { - var _ref6; - - if (_isArray4) { - if (_i4 >= _iterator4.length) break; - _ref6 = _iterator4[_i4++]; - } else { - _i4 = _iterator4.next(); - if (_i4.done) break; - _ref6 = _i4.value; - } - - const file = _ref6; - - if (srcFiles.indexOf(file) < 0) { - const loc = (_path || _load_path()).default.join(dest, file); - possibleExtraneous.add(loc); - - if ((yield lstat(loc)).isDirectory()) { - for (var _iterator5 = yield readdir(loc), _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { - var _ref7; - - if (_isArray5) { - if (_i5 >= _iterator5.length) break; - _ref7 = _iterator5[_i5++]; - } else { - _i5 = _iterator5.next(); - if (_i5.done) break; - _ref7 = _i5.value; - } - - const file = _ref7; - - possibleExtraneous.add((_path || _load_path()).default.join(loc, file)); - } - } - } - } - } - } - - if (destStat && destStat.isSymbolicLink()) { - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dest); - destStat = null; - } - - if (srcStat.isSymbolicLink()) { - onFresh(); - const linkname = yield readlink(src); - actions.symlink.push({ - dest, - linkname - }); - onDone(); - } else if (srcStat.isDirectory()) { - if (!destStat) { - reporter.verbose(reporter.lang('verboseFileFolder', dest)); - yield mkdirp(dest); - } - - const destParts = dest.split((_path || _load_path()).default.sep); - while (destParts.length) { - files.add(destParts.join((_path || _load_path()).default.sep).toLowerCase()); - destParts.pop(); - } - - // push all files to queue - invariant(srcFiles, 'src files not initialised'); - let remaining = srcFiles.length; - if (!remaining) { - onDone(); - } - for (var _iterator6 = srcFiles, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { - var _ref8; - - if (_isArray6) { - if (_i6 >= _iterator6.length) break; - _ref8 = _iterator6[_i6++]; - } else { - _i6 = _iterator6.next(); - if (_i6.done) break; - _ref8 = _i6.value; - } - - const file = _ref8; - - queue.push({ - dest: (_path || _load_path()).default.join(dest, file), - onFresh, - onDone: function (_onDone) { - function onDone() { - return _onDone.apply(this, arguments); - } - - onDone.toString = function () { - return _onDone.toString(); - }; - - return onDone; - }(function () { - if (--remaining === 0) { - onDone(); - } - }), - src: (_path || _load_path()).default.join(src, file) - }); - } - } else if (srcStat.isFile()) { - onFresh(); - actions.file.push({ - src, - dest, - atime: srcStat.atime, - mtime: srcStat.mtime, - mode: srcStat.mode - }); - onDone(); - } else { - throw new Error(`unsure how to copy this: ${src}`); - } - }); - - return function build(_x5) { - return _ref5.apply(this, arguments); - }; - })(); - - const artifactFiles = new Set(events.artifactFiles || []); - const files = new Set(); - - // initialise events - for (var _iterator = queue, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref2; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref2 = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref2 = _i.value; - } - - const item = _ref2; - - const onDone = item.onDone; - item.onDone = function () { - events.onProgress(item.dest); - if (onDone) { - onDone(); - } - }; - } - events.onStart(queue.length); - - // start building actions - const actions = { - file: [], - symlink: [], - link: [] - }; - - // custom concurrency logic as we're always executing stacks of CONCURRENT_QUEUE_ITEMS queue items - // at a time due to the requirement to push items onto the queue - while (queue.length) { - const items = queue.splice(0, CONCURRENT_QUEUE_ITEMS); - yield Promise.all(items.map(build)); - } - - // simulate the existence of some files to prevent considering them extraneous - for (var _iterator2 = artifactFiles, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref3; - - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref3 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref3 = _i2.value; - } - - const file = _ref3; - - if (possibleExtraneous.has(file)) { - reporter.verbose(reporter.lang('verboseFilePhantomExtraneous', file)); - possibleExtraneous.delete(file); - } - } - - for (var _iterator3 = possibleExtraneous, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { - var _ref4; - - if (_isArray3) { - if (_i3 >= _iterator3.length) break; - _ref4 = _iterator3[_i3++]; - } else { - _i3 = _iterator3.next(); - if (_i3.done) break; - _ref4 = _i3.value; - } - - const loc = _ref4; - - if (files.has(loc.toLowerCase())) { - possibleExtraneous.delete(loc); - } - } - - return actions; - }); - - return function buildActionsForCopy(_x, _x2, _x3, _x4) { - return _ref.apply(this, arguments); - }; -})(); - -let buildActionsForHardlink = (() => { - var _ref9 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, events, possibleExtraneous, reporter) { - - // - let build = (() => { - var _ref13 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { - const src = data.src, - dest = data.dest; - - const onFresh = data.onFresh || noop; - const onDone = data.onDone || noop; - if (files.has(dest.toLowerCase())) { - // Fixes issue https://github.com/yarnpkg/yarn/issues/2734 - // When bulk hardlinking we have A -> B structure that we want to hardlink to A1 -> B1, - // package-linker passes that modules A1 and B1 need to be hardlinked, - // the recursive linking algorithm of A1 ends up scheduling files in B1 to be linked twice which will case - // an exception. - onDone(); - return; - } - files.add(dest.toLowerCase()); - - if (events.ignoreBasenames.indexOf((_path || _load_path()).default.basename(src)) >= 0) { - // ignored file - return; - } - - const srcStat = yield lstat(src); - let srcFiles; - - if (srcStat.isDirectory()) { - srcFiles = yield readdir(src); - } - - const destExists = yield exists(dest); - if (destExists) { - const destStat = yield lstat(dest); - - const bothSymlinks = srcStat.isSymbolicLink() && destStat.isSymbolicLink(); - const bothFolders = srcStat.isDirectory() && destStat.isDirectory(); - const bothFiles = srcStat.isFile() && destStat.isFile(); - - if (srcStat.mode !== destStat.mode) { - try { - yield access(dest, srcStat.mode); - } catch (err) { - // EINVAL access errors sometimes happen which shouldn't because node shouldn't be giving - // us modes that aren't valid. investigate this, it's generally safe to proceed. - reporter.verbose(err); - } - } - - if (bothFiles && artifactFiles.has(dest)) { - // this file gets changed during build, likely by a custom install script. Don't bother checking it. - onDone(); - reporter.verbose(reporter.lang('verboseFileSkipArtifact', src)); - return; - } - - // correct hardlink - if (bothFiles && srcStat.ino !== null && srcStat.ino === destStat.ino) { - onDone(); - reporter.verbose(reporter.lang('verboseFileSkip', src, dest, srcStat.ino)); - return; - } - - if (bothSymlinks) { - const srcReallink = yield readlink(src); - if (srcReallink === (yield readlink(dest))) { - // if both symlinks are the same then we can continue on - onDone(); - reporter.verbose(reporter.lang('verboseFileSkipSymlink', src, dest, srcReallink)); - return; - } - } - - if (bothFolders) { - // mark files that aren't in this folder as possibly extraneous - const destFiles = yield readdir(dest); - invariant(srcFiles, 'src files not initialised'); - - for (var _iterator10 = destFiles, _isArray10 = Array.isArray(_iterator10), _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) { - var _ref14; - - if (_isArray10) { - if (_i10 >= _iterator10.length) break; - _ref14 = _iterator10[_i10++]; - } else { - _i10 = _iterator10.next(); - if (_i10.done) break; - _ref14 = _i10.value; - } - - const file = _ref14; - - if (srcFiles.indexOf(file) < 0) { - const loc = (_path || _load_path()).default.join(dest, file); - possibleExtraneous.add(loc); - - if ((yield lstat(loc)).isDirectory()) { - for (var _iterator11 = yield readdir(loc), _isArray11 = Array.isArray(_iterator11), _i11 = 0, _iterator11 = _isArray11 ? _iterator11 : _iterator11[Symbol.iterator]();;) { - var _ref15; - - if (_isArray11) { - if (_i11 >= _iterator11.length) break; - _ref15 = _iterator11[_i11++]; - } else { - _i11 = _iterator11.next(); - if (_i11.done) break; - _ref15 = _i11.value; - } - - const file = _ref15; - - possibleExtraneous.add((_path || _load_path()).default.join(loc, file)); - } - } - } - } - } - } - - if (srcStat.isSymbolicLink()) { - onFresh(); - const linkname = yield readlink(src); - actions.symlink.push({ - dest, - linkname - }); - onDone(); - } else if (srcStat.isDirectory()) { - reporter.verbose(reporter.lang('verboseFileFolder', dest)); - yield mkdirp(dest); - - const destParts = dest.split((_path || _load_path()).default.sep); - while (destParts.length) { - files.add(destParts.join((_path || _load_path()).default.sep).toLowerCase()); - destParts.pop(); - } - - // push all files to queue - invariant(srcFiles, 'src files not initialised'); - let remaining = srcFiles.length; - if (!remaining) { - onDone(); - } - for (var _iterator12 = srcFiles, _isArray12 = Array.isArray(_iterator12), _i12 = 0, _iterator12 = _isArray12 ? _iterator12 : _iterator12[Symbol.iterator]();;) { - var _ref16; - - if (_isArray12) { - if (_i12 >= _iterator12.length) break; - _ref16 = _iterator12[_i12++]; - } else { - _i12 = _iterator12.next(); - if (_i12.done) break; - _ref16 = _i12.value; - } - - const file = _ref16; - - queue.push({ - onFresh, - src: (_path || _load_path()).default.join(src, file), - dest: (_path || _load_path()).default.join(dest, file), - onDone: function (_onDone2) { - function onDone() { - return _onDone2.apply(this, arguments); - } - - onDone.toString = function () { - return _onDone2.toString(); - }; - - return onDone; - }(function () { - if (--remaining === 0) { - onDone(); - } - }) - }); - } - } else if (srcStat.isFile()) { - onFresh(); - actions.link.push({ - src, - dest, - removeDest: destExists - }); - onDone(); - } else { - throw new Error(`unsure how to copy this: ${src}`); - } - }); - - return function build(_x10) { - return _ref13.apply(this, arguments); - }; - })(); - - const artifactFiles = new Set(events.artifactFiles || []); - const files = new Set(); - - // initialise events - for (var _iterator7 = queue, _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { - var _ref10; - - if (_isArray7) { - if (_i7 >= _iterator7.length) break; - _ref10 = _iterator7[_i7++]; - } else { - _i7 = _iterator7.next(); - if (_i7.done) break; - _ref10 = _i7.value; - } - - const item = _ref10; - - const onDone = item.onDone || noop; - item.onDone = function () { - events.onProgress(item.dest); - onDone(); - }; - } - events.onStart(queue.length); - - // start building actions - const actions = { - file: [], - symlink: [], - link: [] - }; - - // custom concurrency logic as we're always executing stacks of CONCURRENT_QUEUE_ITEMS queue items - // at a time due to the requirement to push items onto the queue - while (queue.length) { - const items = queue.splice(0, CONCURRENT_QUEUE_ITEMS); - yield Promise.all(items.map(build)); - } - - // simulate the existence of some files to prevent considering them extraneous - for (var _iterator8 = artifactFiles, _isArray8 = Array.isArray(_iterator8), _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { - var _ref11; - - if (_isArray8) { - if (_i8 >= _iterator8.length) break; - _ref11 = _iterator8[_i8++]; - } else { - _i8 = _iterator8.next(); - if (_i8.done) break; - _ref11 = _i8.value; - } - - const file = _ref11; - - if (possibleExtraneous.has(file)) { - reporter.verbose(reporter.lang('verboseFilePhantomExtraneous', file)); - possibleExtraneous.delete(file); - } - } - - for (var _iterator9 = possibleExtraneous, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { - var _ref12; - - if (_isArray9) { - if (_i9 >= _iterator9.length) break; - _ref12 = _iterator9[_i9++]; - } else { - _i9 = _iterator9.next(); - if (_i9.done) break; - _ref12 = _i9.value; - } - - const loc = _ref12; - - if (files.has(loc.toLowerCase())) { - possibleExtraneous.delete(loc); - } - } - - return actions; - }); - - return function buildActionsForHardlink(_x6, _x7, _x8, _x9) { - return _ref9.apply(this, arguments); - }; -})(); - -let copyBulk = exports.copyBulk = (() => { - var _ref17 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, reporter, _events) { - const events = { - onStart: _events && _events.onStart || noop, - onProgress: _events && _events.onProgress || noop, - possibleExtraneous: _events ? _events.possibleExtraneous : new Set(), - ignoreBasenames: _events && _events.ignoreBasenames || [], - artifactFiles: _events && _events.artifactFiles || [] - }; - - const actions = yield buildActionsForCopy(queue, events, events.possibleExtraneous, reporter); - events.onStart(actions.file.length + actions.symlink.length + actions.link.length); - - const fileActions = actions.file; - - const currentlyWriting = new Map(); - - yield (_promise || _load_promise()).queue(fileActions, (() => { - var _ref18 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { - let writePromise; - while (writePromise = currentlyWriting.get(data.dest)) { - yield writePromise; - } - - reporter.verbose(reporter.lang('verboseFileCopy', data.src, data.dest)); - const copier = (0, (_fsNormalized || _load_fsNormalized()).copyFile)(data, function () { - return currentlyWriting.delete(data.dest); - }); - currentlyWriting.set(data.dest, copier); - events.onProgress(data.dest); - return copier; - }); - - return function (_x14) { - return _ref18.apply(this, arguments); - }; - })(), CONCURRENT_QUEUE_ITEMS); - - // we need to copy symlinks last as they could reference files we were copying - const symlinkActions = actions.symlink; - yield (_promise || _load_promise()).queue(symlinkActions, function (data) { - const linkname = (_path || _load_path()).default.resolve((_path || _load_path()).default.dirname(data.dest), data.linkname); - reporter.verbose(reporter.lang('verboseFileSymlink', data.dest, linkname)); - return symlink(linkname, data.dest); - }); - }); - - return function copyBulk(_x11, _x12, _x13) { - return _ref17.apply(this, arguments); - }; -})(); - -let hardlinkBulk = exports.hardlinkBulk = (() => { - var _ref19 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, reporter, _events) { - const events = { - onStart: _events && _events.onStart || noop, - onProgress: _events && _events.onProgress || noop, - possibleExtraneous: _events ? _events.possibleExtraneous : new Set(), - artifactFiles: _events && _events.artifactFiles || [], - ignoreBasenames: [] - }; - - const actions = yield buildActionsForHardlink(queue, events, events.possibleExtraneous, reporter); - events.onStart(actions.file.length + actions.symlink.length + actions.link.length); - - const fileActions = actions.link; - - yield (_promise || _load_promise()).queue(fileActions, (() => { - var _ref20 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { - reporter.verbose(reporter.lang('verboseFileLink', data.src, data.dest)); - if (data.removeDest) { - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(data.dest); - } - yield link(data.src, data.dest); - }); - - return function (_x18) { - return _ref20.apply(this, arguments); - }; - })(), CONCURRENT_QUEUE_ITEMS); - - // we need to copy symlinks last as they could reference files we were copying - const symlinkActions = actions.symlink; - yield (_promise || _load_promise()).queue(symlinkActions, function (data) { - const linkname = (_path || _load_path()).default.resolve((_path || _load_path()).default.dirname(data.dest), data.linkname); - reporter.verbose(reporter.lang('verboseFileSymlink', data.dest, linkname)); - return symlink(linkname, data.dest); - }); - }); - - return function hardlinkBulk(_x15, _x16, _x17) { - return _ref19.apply(this, arguments); - }; -})(); - -let readFileAny = exports.readFileAny = (() => { - var _ref21 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (files) { - for (var _iterator13 = files, _isArray13 = Array.isArray(_iterator13), _i13 = 0, _iterator13 = _isArray13 ? _iterator13 : _iterator13[Symbol.iterator]();;) { - var _ref22; - - if (_isArray13) { - if (_i13 >= _iterator13.length) break; - _ref22 = _iterator13[_i13++]; - } else { - _i13 = _iterator13.next(); - if (_i13.done) break; - _ref22 = _i13.value; - } - - const file = _ref22; - - if (yield exists(file)) { - return readFile(file); - } - } - return null; - }); - - return function readFileAny(_x19) { - return _ref21.apply(this, arguments); - }; -})(); - -let readJson = exports.readJson = (() => { - var _ref23 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { - return (yield readJsonAndFile(loc)).object; - }); - - return function readJson(_x20) { - return _ref23.apply(this, arguments); - }; -})(); - -let readJsonAndFile = exports.readJsonAndFile = (() => { - var _ref24 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { - const file = yield readFile(loc); - try { - return { - object: (0, (_map || _load_map()).default)(JSON.parse(stripBOM(file))), - content: file - }; - } catch (err) { - err.message = `${loc}: ${err.message}`; - throw err; - } - }); - - return function readJsonAndFile(_x21) { - return _ref24.apply(this, arguments); - }; -})(); - -let find = exports.find = (() => { - var _ref25 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (filename, dir) { - const parts = dir.split((_path || _load_path()).default.sep); - - while (parts.length) { - const loc = parts.concat(filename).join((_path || _load_path()).default.sep); - - if (yield exists(loc)) { - return loc; - } else { - parts.pop(); - } - } - - return false; - }); - - return function find(_x22, _x23) { - return _ref25.apply(this, arguments); - }; -})(); - -let symlink = exports.symlink = (() => { - var _ref26 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (src, dest) { - if (process.platform !== 'win32') { - // use relative paths otherwise which will be retained if the directory is moved - src = (_path || _load_path()).default.relative((_path || _load_path()).default.dirname(dest), src); - // When path.relative returns an empty string for the current directory, we should instead use - // '.', which is a valid fs.symlink target. - src = src || '.'; - } - - try { - const stats = yield lstat(dest); - if (stats.isSymbolicLink()) { - const resolved = dest; - if (resolved === src) { - return; - } - } - } catch (err) { - if (err.code !== 'ENOENT') { - throw err; - } - } - - // We use rimraf for unlink which never throws an ENOENT on missing target - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dest); - - if (process.platform === 'win32') { - // use directory junctions if possible on win32, this requires absolute paths - yield fsSymlink(src, dest, 'junction'); - } else { - yield fsSymlink(src, dest); - } - }); - - return function symlink(_x24, _x25) { - return _ref26.apply(this, arguments); - }; -})(); - -let walk = exports.walk = (() => { - var _ref27 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (dir, relativeDir, ignoreBasenames = new Set()) { - let files = []; - - let filenames = yield readdir(dir); - if (ignoreBasenames.size) { - filenames = filenames.filter(function (name) { - return !ignoreBasenames.has(name); - }); - } - - for (var _iterator14 = filenames, _isArray14 = Array.isArray(_iterator14), _i14 = 0, _iterator14 = _isArray14 ? _iterator14 : _iterator14[Symbol.iterator]();;) { - var _ref28; - - if (_isArray14) { - if (_i14 >= _iterator14.length) break; - _ref28 = _iterator14[_i14++]; - } else { - _i14 = _iterator14.next(); - if (_i14.done) break; - _ref28 = _i14.value; - } - - const name = _ref28; - - const relative = relativeDir ? (_path || _load_path()).default.join(relativeDir, name) : name; - const loc = (_path || _load_path()).default.join(dir, name); - const stat = yield lstat(loc); - - files.push({ - relative, - basename: name, - absolute: loc, - mtime: +stat.mtime - }); - - if (stat.isDirectory()) { - files = files.concat((yield walk(loc, relative, ignoreBasenames))); - } - } - - return files; - }); - - return function walk(_x26, _x27) { - return _ref27.apply(this, arguments); - }; -})(); - -let getFileSizeOnDisk = exports.getFileSizeOnDisk = (() => { - var _ref29 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { - const stat = yield lstat(loc); - const size = stat.size, - blockSize = stat.blksize; - - - return Math.ceil(size / blockSize) * blockSize; - }); - - return function getFileSizeOnDisk(_x28) { - return _ref29.apply(this, arguments); - }; -})(); - -let getEolFromFile = (() => { - var _ref30 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (path) { - if (!(yield exists(path))) { - return undefined; - } - - const buffer = yield readFileBuffer(path); - - for (let i = 0; i < buffer.length; ++i) { - if (buffer[i] === cr) { - return '\r\n'; - } - if (buffer[i] === lf) { - return '\n'; - } - } - return undefined; - }); - - return function getEolFromFile(_x29) { - return _ref30.apply(this, arguments); - }; -})(); - -let writeFilePreservingEol = exports.writeFilePreservingEol = (() => { - var _ref31 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (path, data) { - const eol = (yield getEolFromFile(path)) || (_os || _load_os()).default.EOL; - if (eol !== '\n') { - data = data.replace(/\n/g, eol); - } - yield writeFile(path, data); - }); - - return function writeFilePreservingEol(_x30, _x31) { - return _ref31.apply(this, arguments); - }; -})(); - -let hardlinksWork = exports.hardlinksWork = (() => { - var _ref32 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (dir) { - const filename = 'test-file' + Math.random(); - const file = (_path || _load_path()).default.join(dir, filename); - const fileLink = (_path || _load_path()).default.join(dir, filename + '-link'); - try { - yield writeFile(file, 'test'); - yield link(file, fileLink); - } catch (err) { - return false; - } finally { - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(file); - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(fileLink); - } - return true; - }); - - return function hardlinksWork(_x32) { - return _ref32.apply(this, arguments); - }; -})(); - -// not a strict polyfill for Node's fs.mkdtemp - - -let makeTempDir = exports.makeTempDir = (() => { - var _ref33 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (prefix) { - const dir = (_path || _load_path()).default.join((_os || _load_os()).default.tmpdir(), `yarn-${prefix || ''}-${Date.now()}-${Math.random()}`); - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dir); - yield mkdirp(dir); - return dir; - }); - - return function makeTempDir(_x33) { - return _ref33.apply(this, arguments); - }; -})(); - -let readFirstAvailableStream = exports.readFirstAvailableStream = (() => { - var _ref34 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (paths) { - for (var _iterator15 = paths, _isArray15 = Array.isArray(_iterator15), _i15 = 0, _iterator15 = _isArray15 ? _iterator15 : _iterator15[Symbol.iterator]();;) { - var _ref35; - - if (_isArray15) { - if (_i15 >= _iterator15.length) break; - _ref35 = _iterator15[_i15++]; - } else { - _i15 = _iterator15.next(); - if (_i15.done) break; - _ref35 = _i15.value; - } - - const path = _ref35; - - try { - const fd = yield open(path, 'r'); - return (_fs || _load_fs()).default.createReadStream(path, { fd }); - } catch (err) { - // Try the next one - } - } - return null; - }); - - return function readFirstAvailableStream(_x34) { - return _ref34.apply(this, arguments); - }; -})(); - -let getFirstSuitableFolder = exports.getFirstSuitableFolder = (() => { - var _ref36 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (paths, mode = constants.W_OK | constants.X_OK) { - const result = { - skipped: [], - folder: null - }; - - for (var _iterator16 = paths, _isArray16 = Array.isArray(_iterator16), _i16 = 0, _iterator16 = _isArray16 ? _iterator16 : _iterator16[Symbol.iterator]();;) { - var _ref37; - - if (_isArray16) { - if (_i16 >= _iterator16.length) break; - _ref37 = _iterator16[_i16++]; - } else { - _i16 = _iterator16.next(); - if (_i16.done) break; - _ref37 = _i16.value; - } - - const folder = _ref37; - - try { - yield mkdirp(folder); - yield access(folder, mode); - - result.folder = folder; - - return result; - } catch (error) { - result.skipped.push({ - error, - folder - }); - } - } - return result; - }); - - return function getFirstSuitableFolder(_x35) { - return _ref36.apply(this, arguments); - }; -})(); - -exports.copy = copy; -exports.readFile = readFile; -exports.readFileRaw = readFileRaw; -exports.normalizeOS = normalizeOS; - -var _fs; - -function _load_fs() { - return _fs = _interopRequireDefault(__webpack_require__(5)); -} - -var _glob; - -function _load_glob() { - return _glob = _interopRequireDefault(__webpack_require__(99)); -} - -var _os; - -function _load_os() { - return _os = _interopRequireDefault(__webpack_require__(46)); -} - -var _path; - -function _load_path() { - return _path = _interopRequireDefault(__webpack_require__(0)); -} - -var _blockingQueue; - -function _load_blockingQueue() { - return _blockingQueue = _interopRequireDefault(__webpack_require__(110)); -} - -var _promise; - -function _load_promise() { - return _promise = _interopRequireWildcard(__webpack_require__(50)); -} - -var _promise2; - -function _load_promise2() { - return _promise2 = __webpack_require__(50); -} - -var _map; - -function _load_map() { - return _map = _interopRequireDefault(__webpack_require__(29)); -} - -var _fsNormalized; - -function _load_fsNormalized() { - return _fsNormalized = __webpack_require__(218); -} - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const constants = exports.constants = typeof (_fs || _load_fs()).default.constants !== 'undefined' ? (_fs || _load_fs()).default.constants : { - R_OK: (_fs || _load_fs()).default.R_OK, - W_OK: (_fs || _load_fs()).default.W_OK, - X_OK: (_fs || _load_fs()).default.X_OK -}; - -const lockQueue = exports.lockQueue = new (_blockingQueue || _load_blockingQueue()).default('fs lock'); - -const readFileBuffer = exports.readFileBuffer = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readFile); -const open = exports.open = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.open); -const writeFile = exports.writeFile = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.writeFile); -const readlink = exports.readlink = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readlink); -const realpath = exports.realpath = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.realpath); -const readdir = exports.readdir = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readdir); -const rename = exports.rename = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.rename); -const access = exports.access = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.access); -const stat = exports.stat = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.stat); -const mkdirp = exports.mkdirp = (0, (_promise2 || _load_promise2()).promisify)(__webpack_require__(145)); -const exists = exports.exists = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.exists, true); -const lstat = exports.lstat = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.lstat); -const chmod = exports.chmod = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.chmod); -const link = exports.link = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.link); -const glob = exports.glob = (0, (_promise2 || _load_promise2()).promisify)((_glob || _load_glob()).default); -exports.unlink = (_fsNormalized || _load_fsNormalized()).unlink; - -// fs.copyFile uses the native file copying instructions on the system, performing much better -// than any JS-based solution and consumes fewer resources. Repeated testing to fine tune the -// concurrency level revealed 128 as the sweet spot on a quad-core, 16 CPU Intel system with SSD. - -const CONCURRENT_QUEUE_ITEMS = (_fs || _load_fs()).default.copyFile ? 128 : 4; - -const fsSymlink = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.symlink); -const invariant = __webpack_require__(9); -const stripBOM = __webpack_require__(160); - -const noop = () => {}; - -function copy(src, dest, reporter) { - return copyBulk([{ src, dest }], reporter); -} - -function _readFile(loc, encoding) { - return new Promise((resolve, reject) => { - (_fs || _load_fs()).default.readFile(loc, encoding, function (err, content) { - if (err) { - reject(err); - } else { - resolve(content); - } - }); - }); -} - -function readFile(loc) { - return _readFile(loc, 'utf8').then(normalizeOS); -} - -function readFileRaw(loc) { - return _readFile(loc, 'binary'); -} - -function normalizeOS(body) { - return body.replace(/\r\n/g, '\n'); -} - -const cr = '\r'.charCodeAt(0); -const lf = '\n'.charCodeAt(0); - -/***/ }), -/* 5 */ -/***/ (function(module, exports) { - -module.exports = require("fs"); - -/***/ }), -/* 6 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -class MessageError extends Error { - constructor(msg, code) { - super(msg); - this.code = code; - } - -} - -exports.MessageError = MessageError; -class ProcessSpawnError extends MessageError { - constructor(msg, code, process) { - super(msg, code); - this.process = process; - } - -} - -exports.ProcessSpawnError = ProcessSpawnError; -class SecurityError extends MessageError {} - -exports.SecurityError = SecurityError; -class ProcessTermError extends MessageError {} - -exports.ProcessTermError = ProcessTermError; -class ResponseError extends Error { - constructor(msg, responseCode) { - super(msg); - this.responseCode = responseCode; - } - -} - -exports.ResponseError = ResponseError; -class OneTimePasswordError extends Error {} -exports.OneTimePasswordError = OneTimePasswordError; - -/***/ }), -/* 7 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Subscriber; }); -/* unused harmony export SafeSubscriber */ -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_tslib__ = __webpack_require__(1); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__util_isFunction__ = __webpack_require__(154); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__Observer__ = __webpack_require__(420); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__Subscription__ = __webpack_require__(25); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__internal_symbol_rxSubscriber__ = __webpack_require__(321); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__config__ = __webpack_require__(185); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__util_hostReportError__ = __webpack_require__(323); -/** PURE_IMPORTS_START tslib,_util_isFunction,_Observer,_Subscription,_internal_symbol_rxSubscriber,_config,_util_hostReportError PURE_IMPORTS_END */ - - - - - - - -var Subscriber = /*@__PURE__*/ (function (_super) { - __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](Subscriber, _super); - function Subscriber(destinationOrNext, error, complete) { - var _this = _super.call(this) || this; - _this.syncErrorValue = null; - _this.syncErrorThrown = false; - _this.syncErrorThrowable = false; - _this.isStopped = false; - _this._parentSubscription = null; - switch (arguments.length) { - case 0: - _this.destination = __WEBPACK_IMPORTED_MODULE_2__Observer__["a" /* empty */]; - break; - case 1: - if (!destinationOrNext) { - _this.destination = __WEBPACK_IMPORTED_MODULE_2__Observer__["a" /* empty */]; - break; - } - if (typeof destinationOrNext === 'object') { - if (destinationOrNext instanceof Subscriber) { - _this.syncErrorThrowable = destinationOrNext.syncErrorThrowable; - _this.destination = destinationOrNext; - destinationOrNext.add(_this); - } - else { - _this.syncErrorThrowable = true; - _this.destination = new SafeSubscriber(_this, destinationOrNext); - } - break; - } - default: - _this.syncErrorThrowable = true; - _this.destination = new SafeSubscriber(_this, destinationOrNext, error, complete); - break; - } - return _this; - } - Subscriber.prototype[__WEBPACK_IMPORTED_MODULE_4__internal_symbol_rxSubscriber__["a" /* rxSubscriber */]] = function () { return this; }; - Subscriber.create = function (next, error, complete) { - var subscriber = new Subscriber(next, error, complete); - subscriber.syncErrorThrowable = false; - return subscriber; - }; - Subscriber.prototype.next = function (value) { - if (!this.isStopped) { - this._next(value); - } - }; - Subscriber.prototype.error = function (err) { - if (!this.isStopped) { - this.isStopped = true; - this._error(err); - } - }; - Subscriber.prototype.complete = function () { - if (!this.isStopped) { - this.isStopped = true; - this._complete(); - } - }; - Subscriber.prototype.unsubscribe = function () { - if (this.closed) { - return; - } - this.isStopped = true; - _super.prototype.unsubscribe.call(this); - }; - Subscriber.prototype._next = function (value) { - this.destination.next(value); - }; - Subscriber.prototype._error = function (err) { - this.destination.error(err); - this.unsubscribe(); - }; - Subscriber.prototype._complete = function () { - this.destination.complete(); - this.unsubscribe(); - }; - Subscriber.prototype._unsubscribeAndRecycle = function () { - var _a = this, _parent = _a._parent, _parents = _a._parents; - this._parent = null; - this._parents = null; - this.unsubscribe(); - this.closed = false; - this.isStopped = false; - this._parent = _parent; - this._parents = _parents; - this._parentSubscription = null; - return this; - }; - return Subscriber; -}(__WEBPACK_IMPORTED_MODULE_3__Subscription__["a" /* Subscription */])); - -var SafeSubscriber = /*@__PURE__*/ (function (_super) { - __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](SafeSubscriber, _super); - function SafeSubscriber(_parentSubscriber, observerOrNext, error, complete) { - var _this = _super.call(this) || this; - _this._parentSubscriber = _parentSubscriber; - var next; - var context = _this; - if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__util_isFunction__["a" /* isFunction */])(observerOrNext)) { - next = observerOrNext; - } - else if (observerOrNext) { - next = observerOrNext.next; - error = observerOrNext.error; - complete = observerOrNext.complete; - if (observerOrNext !== __WEBPACK_IMPORTED_MODULE_2__Observer__["a" /* empty */]) { - context = Object.create(observerOrNext); - if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__util_isFunction__["a" /* isFunction */])(context.unsubscribe)) { - _this.add(context.unsubscribe.bind(context)); - } - context.unsubscribe = _this.unsubscribe.bind(_this); - } - } - _this._context = context; - _this._next = next; - _this._error = error; - _this._complete = complete; - return _this; - } - SafeSubscriber.prototype.next = function (value) { - if (!this.isStopped && this._next) { - var _parentSubscriber = this._parentSubscriber; - if (!__WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) { - this.__tryOrUnsub(this._next, value); - } - else if (this.__tryOrSetError(_parentSubscriber, this._next, value)) { - this.unsubscribe(); - } - } - }; - SafeSubscriber.prototype.error = function (err) { - if (!this.isStopped) { - var _parentSubscriber = this._parentSubscriber; - var useDeprecatedSynchronousErrorHandling = __WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling; - if (this._error) { - if (!useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) { - this.__tryOrUnsub(this._error, err); - this.unsubscribe(); - } - else { - this.__tryOrSetError(_parentSubscriber, this._error, err); - this.unsubscribe(); - } - } - else if (!_parentSubscriber.syncErrorThrowable) { - this.unsubscribe(); - if (useDeprecatedSynchronousErrorHandling) { - throw err; - } - __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_6__util_hostReportError__["a" /* hostReportError */])(err); - } - else { - if (useDeprecatedSynchronousErrorHandling) { - _parentSubscriber.syncErrorValue = err; - _parentSubscriber.syncErrorThrown = true; - } - else { - __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_6__util_hostReportError__["a" /* hostReportError */])(err); - } - this.unsubscribe(); - } - } - }; - SafeSubscriber.prototype.complete = function () { - var _this = this; - if (!this.isStopped) { - var _parentSubscriber = this._parentSubscriber; - if (this._complete) { - var wrappedComplete = function () { return _this._complete.call(_this._context); }; - if (!__WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) { - this.__tryOrUnsub(wrappedComplete); - this.unsubscribe(); - } - else { - this.__tryOrSetError(_parentSubscriber, wrappedComplete); - this.unsubscribe(); - } - } - else { - this.unsubscribe(); - } - } - }; - SafeSubscriber.prototype.__tryOrUnsub = function (fn, value) { - try { - fn.call(this._context, value); - } - catch (err) { - this.unsubscribe(); - if (__WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling) { - throw err; - } - else { - __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_6__util_hostReportError__["a" /* hostReportError */])(err); - } - } - }; - SafeSubscriber.prototype.__tryOrSetError = function (parent, fn, value) { - if (!__WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling) { - throw new Error('bad call'); - } - try { - fn.call(this._context, value); - } - catch (err) { - if (__WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling) { - parent.syncErrorValue = err; - parent.syncErrorThrown = true; - return true; - } - else { - __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_6__util_hostReportError__["a" /* hostReportError */])(err); - return true; - } - } - return false; - }; - SafeSubscriber.prototype._unsubscribe = function () { - var _parentSubscriber = this._parentSubscriber; - this._context = null; - this._parentSubscriber = null; - _parentSubscriber.unsubscribe(); - }; - return SafeSubscriber; -}(Subscriber)); - -//# sourceMappingURL=Subscriber.js.map - - -/***/ }), -/* 8 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.getPathKey = getPathKey; -const os = __webpack_require__(46); -const path = __webpack_require__(0); -const userHome = __webpack_require__(67).default; - -var _require = __webpack_require__(225); - -const getCacheDir = _require.getCacheDir, - getConfigDir = _require.getConfigDir, - getDataDir = _require.getDataDir; - -const isWebpackBundle = __webpack_require__(278); - -const DEPENDENCY_TYPES = exports.DEPENDENCY_TYPES = ['devDependencies', 'dependencies', 'optionalDependencies', 'peerDependencies']; -const OWNED_DEPENDENCY_TYPES = exports.OWNED_DEPENDENCY_TYPES = ['devDependencies', 'dependencies', 'optionalDependencies']; - -const RESOLUTIONS = exports.RESOLUTIONS = 'resolutions'; -const MANIFEST_FIELDS = exports.MANIFEST_FIELDS = [RESOLUTIONS, ...DEPENDENCY_TYPES]; - -const SUPPORTED_NODE_VERSIONS = exports.SUPPORTED_NODE_VERSIONS = '^4.8.0 || ^5.7.0 || ^6.2.2 || >=8.0.0'; - -const YARN_REGISTRY = exports.YARN_REGISTRY = 'https://registry.yarnpkg.com'; -const NPM_REGISTRY_RE = exports.NPM_REGISTRY_RE = /https?:\/\/registry\.npmjs\.org/g; - -const YARN_DOCS = exports.YARN_DOCS = 'https://yarnpkg.com/en/docs/cli/'; -const YARN_INSTALLER_SH = exports.YARN_INSTALLER_SH = 'https://yarnpkg.com/install.sh'; -const YARN_INSTALLER_MSI = exports.YARN_INSTALLER_MSI = 'https://yarnpkg.com/latest.msi'; - -const SELF_UPDATE_VERSION_URL = exports.SELF_UPDATE_VERSION_URL = 'https://yarnpkg.com/latest-version'; - -// cache version, bump whenever we make backwards incompatible changes -const CACHE_VERSION = exports.CACHE_VERSION = 6; - -// lockfile version, bump whenever we make backwards incompatible changes -const LOCKFILE_VERSION = exports.LOCKFILE_VERSION = 1; - -// max amount of network requests to perform concurrently -const NETWORK_CONCURRENCY = exports.NETWORK_CONCURRENCY = 8; - -// HTTP timeout used when downloading packages -const NETWORK_TIMEOUT = exports.NETWORK_TIMEOUT = 30 * 1000; // in milliseconds - -// max amount of child processes to execute concurrently -const CHILD_CONCURRENCY = exports.CHILD_CONCURRENCY = 5; - -const REQUIRED_PACKAGE_KEYS = exports.REQUIRED_PACKAGE_KEYS = ['name', 'version', '_uid']; - -function getPreferredCacheDirectories() { - const preferredCacheDirectories = [getCacheDir()]; - - if (process.getuid) { - // $FlowFixMe: process.getuid exists, dammit - preferredCacheDirectories.push(path.join(os.tmpdir(), `.yarn-cache-${process.getuid()}`)); - } - - preferredCacheDirectories.push(path.join(os.tmpdir(), `.yarn-cache`)); - - return preferredCacheDirectories; -} - -const PREFERRED_MODULE_CACHE_DIRECTORIES = exports.PREFERRED_MODULE_CACHE_DIRECTORIES = getPreferredCacheDirectories(); -const CONFIG_DIRECTORY = exports.CONFIG_DIRECTORY = getConfigDir(); -const DATA_DIRECTORY = exports.DATA_DIRECTORY = getDataDir(); -const LINK_REGISTRY_DIRECTORY = exports.LINK_REGISTRY_DIRECTORY = path.join(DATA_DIRECTORY, 'link'); -const GLOBAL_MODULE_DIRECTORY = exports.GLOBAL_MODULE_DIRECTORY = path.join(DATA_DIRECTORY, 'global'); - -const NODE_BIN_PATH = exports.NODE_BIN_PATH = process.execPath; -const YARN_BIN_PATH = exports.YARN_BIN_PATH = getYarnBinPath(); - -// Webpack needs to be configured with node.__dirname/__filename = false -function getYarnBinPath() { - if (isWebpackBundle) { - return __filename; - } else { - return path.join(__dirname, '..', 'bin', 'yarn.js'); - } -} - -const NODE_MODULES_FOLDER = exports.NODE_MODULES_FOLDER = 'node_modules'; -const NODE_PACKAGE_JSON = exports.NODE_PACKAGE_JSON = 'package.json'; - -const PNP_FILENAME = exports.PNP_FILENAME = '.pnp.js'; - -const POSIX_GLOBAL_PREFIX = exports.POSIX_GLOBAL_PREFIX = `${process.env.DESTDIR || ''}/usr/local`; -const FALLBACK_GLOBAL_PREFIX = exports.FALLBACK_GLOBAL_PREFIX = path.join(userHome, '.yarn'); - -const META_FOLDER = exports.META_FOLDER = '.yarn-meta'; -const INTEGRITY_FILENAME = exports.INTEGRITY_FILENAME = '.yarn-integrity'; -const LOCKFILE_FILENAME = exports.LOCKFILE_FILENAME = 'yarn.lock'; -const METADATA_FILENAME = exports.METADATA_FILENAME = '.yarn-metadata.json'; -const TARBALL_FILENAME = exports.TARBALL_FILENAME = '.yarn-tarball.tgz'; -const CLEAN_FILENAME = exports.CLEAN_FILENAME = '.yarnclean'; - -const NPM_LOCK_FILENAME = exports.NPM_LOCK_FILENAME = 'package-lock.json'; -const NPM_SHRINKWRAP_FILENAME = exports.NPM_SHRINKWRAP_FILENAME = 'npm-shrinkwrap.json'; - -const DEFAULT_INDENT = exports.DEFAULT_INDENT = ' '; -const SINGLE_INSTANCE_PORT = exports.SINGLE_INSTANCE_PORT = 31997; -const SINGLE_INSTANCE_FILENAME = exports.SINGLE_INSTANCE_FILENAME = '.yarn-single-instance'; - -const ENV_PATH_KEY = exports.ENV_PATH_KEY = getPathKey(process.platform, process.env); - -function getPathKey(platform, env) { - let pathKey = 'PATH'; - - // windows calls its path "Path" usually, but this is not guaranteed. - if (platform === 'win32') { - pathKey = 'Path'; - - for (const key in env) { - if (key.toLowerCase() === 'path') { - pathKey = key; - } - } - } - - return pathKey; -} - -const VERSION_COLOR_SCHEME = exports.VERSION_COLOR_SCHEME = { - major: 'red', - premajor: 'red', - minor: 'yellow', - preminor: 'yellow', - patch: 'green', - prepatch: 'green', - prerelease: 'red', - unchanged: 'white', - unknown: 'red' -}; - -/***/ }), -/* 9 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/** - * Copyright (c) 2013-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - - -/** - * Use invariant() to assert state which your program assumes to be true. - * - * Provide sprintf-style format (only %s is supported) and arguments - * to provide information about what broke and what you were - * expecting. - * - * The invariant message will be stripped in production, but the invariant - * will remain to ensure logic does not differ in production. - */ - -var NODE_ENV = process.env.NODE_ENV; - -var invariant = function(condition, format, a, b, c, d, e, f) { - if (NODE_ENV !== 'production') { - if (format === undefined) { - throw new Error('invariant requires an error message argument'); - } - } - - if (!condition) { - var error; - if (format === undefined) { - error = new Error( - 'Minified exception occurred; use the non-minified dev environment ' + - 'for the full error message and additional helpful warnings.' - ); - } else { - var args = [a, b, c, d, e, f]; - var argIndex = 0; - error = new Error( - format.replace(/%s/g, function() { return args[argIndex++]; }) - ); - error.name = 'Invariant Violation'; - } - - error.framesToPop = 1; // we don't care about invariant's own frame - throw error; - } -}; - -module.exports = invariant; - - -/***/ }), -/* 10 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var YAMLException = __webpack_require__(54); - -var TYPE_CONSTRUCTOR_OPTIONS = [ - 'kind', - 'resolve', - 'construct', - 'instanceOf', - 'predicate', - 'represent', - 'defaultStyle', - 'styleAliases' -]; - -var YAML_NODE_KINDS = [ - 'scalar', - 'sequence', - 'mapping' -]; - -function compileStyleAliases(map) { - var result = {}; - - if (map !== null) { - Object.keys(map).forEach(function (style) { - map[style].forEach(function (alias) { - result[String(alias)] = style; - }); - }); - } - - return result; -} - -function Type(tag, options) { - options = options || {}; - - Object.keys(options).forEach(function (name) { - if (TYPE_CONSTRUCTOR_OPTIONS.indexOf(name) === -1) { - throw new YAMLException('Unknown option "' + name + '" is met in definition of "' + tag + '" YAML type.'); - } - }); - - // TODO: Add tag format check. - this.tag = tag; - this.kind = options['kind'] || null; - this.resolve = options['resolve'] || function () { return true; }; - this.construct = options['construct'] || function (data) { return data; }; - this.instanceOf = options['instanceOf'] || null; - this.predicate = options['predicate'] || null; - this.represent = options['represent'] || null; - this.defaultStyle = options['defaultStyle'] || null; - this.styleAliases = compileStyleAliases(options['styleAliases'] || null); - - if (YAML_NODE_KINDS.indexOf(this.kind) === -1) { - throw new YAMLException('Unknown kind "' + this.kind + '" is specified for "' + tag + '" YAML type.'); - } -} - -module.exports = Type; - - -/***/ }), -/* 11 */ -/***/ (function(module, exports) { - -module.exports = require("crypto"); - -/***/ }), -/* 12 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Observable; }); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__util_canReportError__ = __webpack_require__(322); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__util_toSubscriber__ = __webpack_require__(932); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__internal_symbol_observable__ = __webpack_require__(117); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__util_pipe__ = __webpack_require__(324); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__config__ = __webpack_require__(185); -/** PURE_IMPORTS_START _util_canReportError,_util_toSubscriber,_internal_symbol_observable,_util_pipe,_config PURE_IMPORTS_END */ - - - - - -var Observable = /*@__PURE__*/ (function () { - function Observable(subscribe) { - this._isScalar = false; - if (subscribe) { - this._subscribe = subscribe; - } - } - Observable.prototype.lift = function (operator) { - var observable = new Observable(); - observable.source = this; - observable.operator = operator; - return observable; - }; - Observable.prototype.subscribe = function (observerOrNext, error, complete) { - var operator = this.operator; - var sink = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__util_toSubscriber__["a" /* toSubscriber */])(observerOrNext, error, complete); - if (operator) { - operator.call(sink, this.source); - } - else { - sink.add(this.source || (__WEBPACK_IMPORTED_MODULE_4__config__["a" /* config */].useDeprecatedSynchronousErrorHandling && !sink.syncErrorThrowable) ? - this._subscribe(sink) : - this._trySubscribe(sink)); - } - if (__WEBPACK_IMPORTED_MODULE_4__config__["a" /* config */].useDeprecatedSynchronousErrorHandling) { - if (sink.syncErrorThrowable) { - sink.syncErrorThrowable = false; - if (sink.syncErrorThrown) { - throw sink.syncErrorValue; - } - } - } - return sink; - }; - Observable.prototype._trySubscribe = function (sink) { - try { - return this._subscribe(sink); - } - catch (err) { - if (__WEBPACK_IMPORTED_MODULE_4__config__["a" /* config */].useDeprecatedSynchronousErrorHandling) { - sink.syncErrorThrown = true; - sink.syncErrorValue = err; - } - if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__util_canReportError__["a" /* canReportError */])(sink)) { - sink.error(err); - } - else { - console.warn(err); - } - } - }; - Observable.prototype.forEach = function (next, promiseCtor) { - var _this = this; - promiseCtor = getPromiseCtor(promiseCtor); - return new promiseCtor(function (resolve, reject) { - var subscription; - subscription = _this.subscribe(function (value) { - try { - next(value); - } - catch (err) { - reject(err); - if (subscription) { - subscription.unsubscribe(); - } - } - }, reject, resolve); - }); - }; - Observable.prototype._subscribe = function (subscriber) { - var source = this.source; - return source && source.subscribe(subscriber); - }; - Observable.prototype[__WEBPACK_IMPORTED_MODULE_2__internal_symbol_observable__["a" /* observable */]] = function () { - return this; - }; - Observable.prototype.pipe = function () { - var operations = []; - for (var _i = 0; _i < arguments.length; _i++) { - operations[_i] = arguments[_i]; - } - if (operations.length === 0) { - return this; - } - return __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_3__util_pipe__["b" /* pipeFromArray */])(operations)(this); - }; - Observable.prototype.toPromise = function (promiseCtor) { - var _this = this; - promiseCtor = getPromiseCtor(promiseCtor); - return new promiseCtor(function (resolve, reject) { - var value; - _this.subscribe(function (x) { return value = x; }, function (err) { return reject(err); }, function () { return resolve(value); }); - }); - }; - Observable.create = function (subscribe) { - return new Observable(subscribe); - }; - return Observable; -}()); - -function getPromiseCtor(promiseCtor) { - if (!promiseCtor) { - promiseCtor = __WEBPACK_IMPORTED_MODULE_4__config__["a" /* config */].Promise || Promise; - } - if (!promiseCtor) { - throw new Error('no Promise impl found'); - } - return promiseCtor; -} -//# sourceMappingURL=Observable.js.map - - -/***/ }), -/* 13 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return OuterSubscriber; }); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_tslib__ = __webpack_require__(1); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__Subscriber__ = __webpack_require__(7); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - - -var OuterSubscriber = /*@__PURE__*/ (function (_super) { - __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](OuterSubscriber, _super); - function OuterSubscriber() { - return _super !== null && _super.apply(this, arguments) || this; - } - OuterSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - this.destination.next(innerValue); - }; - OuterSubscriber.prototype.notifyError = function (error, innerSub) { - this.destination.error(error); - }; - OuterSubscriber.prototype.notifyComplete = function (innerSub) { - this.destination.complete(); - }; - return OuterSubscriber; -}(__WEBPACK_IMPORTED_MODULE_1__Subscriber__["a" /* Subscriber */])); - -//# sourceMappingURL=OuterSubscriber.js.map - - -/***/ }), -/* 14 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -/* harmony export (immutable) */ __webpack_exports__["a"] = subscribeToResult; -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__InnerSubscriber__ = __webpack_require__(84); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__subscribeTo__ = __webpack_require__(446); -/** PURE_IMPORTS_START _InnerSubscriber,_subscribeTo PURE_IMPORTS_END */ - - -function subscribeToResult(outerSubscriber, result, outerValue, outerIndex, destination) { - if (destination === void 0) { - destination = new __WEBPACK_IMPORTED_MODULE_0__InnerSubscriber__["a" /* InnerSubscriber */](outerSubscriber, outerValue, outerIndex); - } - if (destination.closed) { - return; - } - return __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__subscribeTo__["a" /* subscribeTo */])(result)(destination); -} -//# sourceMappingURL=subscribeToResult.js.map - - -/***/ }), -/* 15 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/* eslint-disable node/no-deprecated-api */ - - - -var buffer = __webpack_require__(64) -var Buffer = buffer.Buffer - -var safer = {} - -var key - -for (key in buffer) { - if (!buffer.hasOwnProperty(key)) continue - if (key === 'SlowBuffer' || key === 'Buffer') continue - safer[key] = buffer[key] -} - -var Safer = safer.Buffer = {} -for (key in Buffer) { - if (!Buffer.hasOwnProperty(key)) continue - if (key === 'allocUnsafe' || key === 'allocUnsafeSlow') continue - Safer[key] = Buffer[key] -} - -safer.Buffer.prototype = Buffer.prototype - -if (!Safer.from || Safer.from === Uint8Array.from) { - Safer.from = function (value, encodingOrOffset, length) { - if (typeof value === 'number') { - throw new TypeError('The "value" argument must not be of type number. Received type ' + typeof value) - } - if (value && typeof value.length === 'undefined') { - throw new TypeError('The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type ' + typeof value) - } - return Buffer(value, encodingOrOffset, length) - } -} - -if (!Safer.alloc) { - Safer.alloc = function (size, fill, encoding) { - if (typeof size !== 'number') { - throw new TypeError('The "size" argument must be of type number. Received type ' + typeof size) - } - if (size < 0 || size >= 2 * (1 << 30)) { - throw new RangeError('The value "' + size + '" is invalid for option "size"') - } - var buf = Buffer(size) - if (!fill || fill.length === 0) { - buf.fill(0) - } else if (typeof encoding === 'string') { - buf.fill(fill, encoding) - } else { - buf.fill(fill) - } - return buf - } -} - -if (!safer.kStringMaxLength) { - try { - safer.kStringMaxLength = process.binding('buffer').kStringMaxLength - } catch (e) { - // we can't determine kStringMaxLength in environments where process.binding - // is unsupported, so let's not set it - } -} - -if (!safer.constants) { - safer.constants = { - MAX_LENGTH: safer.kMaxLength - } - if (safer.kStringMaxLength) { - safer.constants.MAX_STRING_LENGTH = safer.kStringMaxLength - } -} - -module.exports = safer - - -/***/ }), -/* 16 */ -/***/ (function(module, exports, __webpack_require__) { - -// Copyright (c) 2012, Mark Cavage. All rights reserved. -// Copyright 2015 Joyent, Inc. - -var assert = __webpack_require__(28); -var Stream = __webpack_require__(23).Stream; -var util = __webpack_require__(3); - - -///--- Globals - -/* JSSTYLED */ -var UUID_REGEXP = /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/; - - -///--- Internal - -function _capitalize(str) { - return (str.charAt(0).toUpperCase() + str.slice(1)); -} - -function _toss(name, expected, oper, arg, actual) { - throw new assert.AssertionError({ - message: util.format('%s (%s) is required', name, expected), - actual: (actual === undefined) ? typeof (arg) : actual(arg), - expected: expected, - operator: oper || '===', - stackStartFunction: _toss.caller - }); -} - -function _getClass(arg) { - return (Object.prototype.toString.call(arg).slice(8, -1)); -} - -function noop() { - // Why even bother with asserts? -} - - -///--- Exports - -var types = { - bool: { - check: function (arg) { return typeof (arg) === 'boolean'; } - }, - func: { - check: function (arg) { return typeof (arg) === 'function'; } - }, - string: { - check: function (arg) { return typeof (arg) === 'string'; } - }, - object: { - check: function (arg) { - return typeof (arg) === 'object' && arg !== null; - } - }, - number: { - check: function (arg) { - return typeof (arg) === 'number' && !isNaN(arg); - } - }, - finite: { - check: function (arg) { - return typeof (arg) === 'number' && !isNaN(arg) && isFinite(arg); - } - }, - buffer: { - check: function (arg) { return Buffer.isBuffer(arg); }, - operator: 'Buffer.isBuffer' - }, - array: { - check: function (arg) { return Array.isArray(arg); }, - operator: 'Array.isArray' - }, - stream: { - check: function (arg) { return arg instanceof Stream; }, - operator: 'instanceof', - actual: _getClass - }, - date: { - check: function (arg) { return arg instanceof Date; }, - operator: 'instanceof', - actual: _getClass - }, - regexp: { - check: function (arg) { return arg instanceof RegExp; }, - operator: 'instanceof', - actual: _getClass - }, - uuid: { - check: function (arg) { - return typeof (arg) === 'string' && UUID_REGEXP.test(arg); - }, - operator: 'isUUID' - } -}; - -function _setExports(ndebug) { - var keys = Object.keys(types); - var out; - - /* re-export standard assert */ - if (process.env.NODE_NDEBUG) { - out = noop; - } else { - out = function (arg, msg) { - if (!arg) { - _toss(msg, 'true', arg); - } - }; - } - - /* standard checks */ - keys.forEach(function (k) { - if (ndebug) { - out[k] = noop; - return; - } - var type = types[k]; - out[k] = function (arg, msg) { - if (!type.check(arg)) { - _toss(msg, k, type.operator, arg, type.actual); - } - }; - }); - - /* optional checks */ - keys.forEach(function (k) { - var name = 'optional' + _capitalize(k); - if (ndebug) { - out[name] = noop; - return; - } - var type = types[k]; - out[name] = function (arg, msg) { - if (arg === undefined || arg === null) { - return; - } - if (!type.check(arg)) { - _toss(msg, k, type.operator, arg, type.actual); - } - }; - }); - - /* arrayOf checks */ - keys.forEach(function (k) { - var name = 'arrayOf' + _capitalize(k); - if (ndebug) { - out[name] = noop; - return; - } - var type = types[k]; - var expected = '[' + k + ']'; - out[name] = function (arg, msg) { - if (!Array.isArray(arg)) { - _toss(msg, expected, type.operator, arg, type.actual); - } - var i; - for (i = 0; i < arg.length; i++) { - if (!type.check(arg[i])) { - _toss(msg, expected, type.operator, arg, type.actual); - } - } - }; - }); - - /* optionalArrayOf checks */ - keys.forEach(function (k) { - var name = 'optionalArrayOf' + _capitalize(k); - if (ndebug) { - out[name] = noop; - return; - } - var type = types[k]; - var expected = '[' + k + ']'; - out[name] = function (arg, msg) { - if (arg === undefined || arg === null) { - return; - } - if (!Array.isArray(arg)) { - _toss(msg, expected, type.operator, arg, type.actual); - } - var i; - for (i = 0; i < arg.length; i++) { - if (!type.check(arg[i])) { - _toss(msg, expected, type.operator, arg, type.actual); - } - } - }; - }); - - /* re-export built-in assertions */ - Object.keys(assert).forEach(function (k) { - if (k === 'AssertionError') { - out[k] = assert[k]; - return; - } - if (ndebug) { - out[k] = noop; - return; - } - out[k] = assert[k]; - }); - - /* export ourselves (for unit tests _only_) */ - out._setExports = _setExports; - - return out; -} - -module.exports = _setExports(process.env.NODE_NDEBUG); - - -/***/ }), -/* 17 */ -/***/ (function(module, exports) { - -// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 -var global = module.exports = typeof window != 'undefined' && window.Math == Math - ? window : typeof self != 'undefined' && self.Math == Math ? self - // eslint-disable-next-line no-new-func - : Function('return this')(); -if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef - - -/***/ }), -/* 18 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.sortAlpha = sortAlpha; -exports.sortOptionsByFlags = sortOptionsByFlags; -exports.entries = entries; -exports.removePrefix = removePrefix; -exports.removeSuffix = removeSuffix; -exports.addSuffix = addSuffix; -exports.hyphenate = hyphenate; -exports.camelCase = camelCase; -exports.compareSortedArrays = compareSortedArrays; -exports.sleep = sleep; -const _camelCase = __webpack_require__(230); - -function sortAlpha(a, b) { - // sort alphabetically in a deterministic way - const shortLen = Math.min(a.length, b.length); - for (let i = 0; i < shortLen; i++) { - const aChar = a.charCodeAt(i); - const bChar = b.charCodeAt(i); - if (aChar !== bChar) { - return aChar - bChar; - } - } - return a.length - b.length; -} - -function sortOptionsByFlags(a, b) { - const aOpt = a.flags.replace(/-/g, ''); - const bOpt = b.flags.replace(/-/g, ''); - return sortAlpha(aOpt, bOpt); -} - -function entries(obj) { - const entries = []; - if (obj) { - for (const key in obj) { - entries.push([key, obj[key]]); - } - } - return entries; -} - -function removePrefix(pattern, prefix) { - if (pattern.startsWith(prefix)) { - pattern = pattern.slice(prefix.length); - } - - return pattern; -} - -function removeSuffix(pattern, suffix) { - if (pattern.endsWith(suffix)) { - return pattern.slice(0, -suffix.length); - } - - return pattern; -} - -function addSuffix(pattern, suffix) { - if (!pattern.endsWith(suffix)) { - return pattern + suffix; - } - - return pattern; -} - -function hyphenate(str) { - return str.replace(/[A-Z]/g, match => { - return '-' + match.charAt(0).toLowerCase(); - }); -} - -function camelCase(str) { - if (/[A-Z]/.test(str)) { - return null; - } else { - return _camelCase(str); - } -} - -function compareSortedArrays(array1, array2) { - if (array1.length !== array2.length) { - return false; - } - for (let i = 0, len = array1.length; i < len; i++) { - if (array1[i] !== array2[i]) { - return false; - } - } - return true; -} - -function sleep(ms) { - return new Promise(resolve => { - setTimeout(resolve, ms); - }); -} - -/***/ }), -/* 19 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.stringify = exports.parse = undefined; - -var _asyncToGenerator2; - -function _load_asyncToGenerator() { - return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(2)); -} - -var _parse; - -function _load_parse() { - return _parse = __webpack_require__(105); -} - -Object.defineProperty(exports, 'parse', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_parse || _load_parse()).default; - } -}); - -var _stringify; - -function _load_stringify() { - return _stringify = __webpack_require__(199); -} - -Object.defineProperty(exports, 'stringify', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_stringify || _load_stringify()).default; - } -}); -exports.implodeEntry = implodeEntry; -exports.explodeEntry = explodeEntry; - -var _misc; - -function _load_misc() { - return _misc = __webpack_require__(18); -} - -var _normalizePattern; - -function _load_normalizePattern() { - return _normalizePattern = __webpack_require__(37); -} - -var _parse2; - -function _load_parse2() { - return _parse2 = _interopRequireDefault(__webpack_require__(105)); -} - -var _constants; - -function _load_constants() { - return _constants = __webpack_require__(8); -} - -var _fs; - -function _load_fs() { - return _fs = _interopRequireWildcard(__webpack_require__(4)); -} - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const invariant = __webpack_require__(9); - -const path = __webpack_require__(0); -const ssri = __webpack_require__(65); - -function getName(pattern) { - return (0, (_normalizePattern || _load_normalizePattern()).normalizePattern)(pattern).name; -} - -function blankObjectUndefined(obj) { - return obj && Object.keys(obj).length ? obj : undefined; -} - -function keyForRemote(remote) { - return remote.resolved || (remote.reference && remote.hash ? `${remote.reference}#${remote.hash}` : null); -} - -function serializeIntegrity(integrity) { - // We need this because `Integrity.toString()` does not use sorting to ensure a stable string output - // See https://git.io/vx2Hy - return integrity.toString().split(' ').sort().join(' '); -} - -function implodeEntry(pattern, obj) { - const inferredName = getName(pattern); - const integrity = obj.integrity ? serializeIntegrity(obj.integrity) : ''; - const imploded = { - name: inferredName === obj.name ? undefined : obj.name, - version: obj.version, - uid: obj.uid === obj.version ? undefined : obj.uid, - resolved: obj.resolved, - registry: obj.registry === 'npm' ? undefined : obj.registry, - dependencies: blankObjectUndefined(obj.dependencies), - optionalDependencies: blankObjectUndefined(obj.optionalDependencies), - permissions: blankObjectUndefined(obj.permissions), - prebuiltVariants: blankObjectUndefined(obj.prebuiltVariants) - }; - if (integrity) { - imploded.integrity = integrity; - } - return imploded; -} - -function explodeEntry(pattern, obj) { - obj.optionalDependencies = obj.optionalDependencies || {}; - obj.dependencies = obj.dependencies || {}; - obj.uid = obj.uid || obj.version; - obj.permissions = obj.permissions || {}; - obj.registry = obj.registry || 'npm'; - obj.name = obj.name || getName(pattern); - const integrity = obj.integrity; - if (integrity && integrity.isIntegrity) { - obj.integrity = ssri.parse(integrity); - } - return obj; -} - -class Lockfile { - constructor({ cache, source, parseResultType } = {}) { - this.source = source || ''; - this.cache = cache; - this.parseResultType = parseResultType; - } - - // source string if the `cache` was parsed - - - // if true, we're parsing an old yarn file and need to update integrity fields - hasEntriesExistWithoutIntegrity() { - if (!this.cache) { - return false; - } - - for (const key in this.cache) { - // $FlowFixMe - `this.cache` is clearly defined at this point - if (!/^.*@(file:|http)/.test(key) && this.cache[key] && !this.cache[key].integrity) { - return true; - } - } - - return false; - } - - static fromDirectory(dir, reporter) { - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - // read the manifest in this directory - const lockfileLoc = path.join(dir, (_constants || _load_constants()).LOCKFILE_FILENAME); - - let lockfile; - let rawLockfile = ''; - let parseResult; - - if (yield (_fs || _load_fs()).exists(lockfileLoc)) { - rawLockfile = yield (_fs || _load_fs()).readFile(lockfileLoc); - parseResult = (0, (_parse2 || _load_parse2()).default)(rawLockfile, lockfileLoc); - - if (reporter) { - if (parseResult.type === 'merge') { - reporter.info(reporter.lang('lockfileMerged')); - } else if (parseResult.type === 'conflict') { - reporter.warn(reporter.lang('lockfileConflict')); - } - } - - lockfile = parseResult.object; - } else if (reporter) { - reporter.info(reporter.lang('noLockfileFound')); - } - - if (lockfile && lockfile.__metadata) { - const lockfilev2 = lockfile; - lockfile = {}; - } - - return new Lockfile({ cache: lockfile, source: rawLockfile, parseResultType: parseResult && parseResult.type }); - })(); - } - - getLocked(pattern) { - const cache = this.cache; - if (!cache) { - return undefined; - } - - const shrunk = pattern in cache && cache[pattern]; - - if (typeof shrunk === 'string') { - return this.getLocked(shrunk); - } else if (shrunk) { - explodeEntry(pattern, shrunk); - return shrunk; - } - - return undefined; - } - - removePattern(pattern) { - const cache = this.cache; - if (!cache) { - return; - } - delete cache[pattern]; - } - - getLockfile(patterns) { - const lockfile = {}; - const seen = new Map(); - - // order by name so that lockfile manifest is assigned to the first dependency with this manifest - // the others that have the same remoteKey will just refer to the first - // ordering allows for consistency in lockfile when it is serialized - const sortedPatternsKeys = Object.keys(patterns).sort((_misc || _load_misc()).sortAlpha); - - for (var _iterator = sortedPatternsKeys, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } - - const pattern = _ref; - - const pkg = patterns[pattern]; - const remote = pkg._remote, - ref = pkg._reference; - - invariant(ref, 'Package is missing a reference'); - invariant(remote, 'Package is missing a remote'); - - const remoteKey = keyForRemote(remote); - const seenPattern = remoteKey && seen.get(remoteKey); - if (seenPattern) { - // no point in duplicating it - lockfile[pattern] = seenPattern; - - // if we're relying on our name being inferred and two of the patterns have - // different inferred names then we need to set it - if (!seenPattern.name && getName(pattern) !== pkg.name) { - seenPattern.name = pkg.name; - } - continue; - } - const obj = implodeEntry(pattern, { - name: pkg.name, - version: pkg.version, - uid: pkg._uid, - resolved: remote.resolved, - integrity: remote.integrity, - registry: remote.registry, - dependencies: pkg.dependencies, - peerDependencies: pkg.peerDependencies, - optionalDependencies: pkg.optionalDependencies, - permissions: ref.permissions, - prebuiltVariants: pkg.prebuiltVariants - }); - - lockfile[pattern] = obj; - - if (remoteKey) { - seen.set(remoteKey, obj); - } - } - - return lockfile; - } -} -exports.default = Lockfile; - -/***/ }), -/* 20 */ -/***/ (function(module, exports, __webpack_require__) { - -var store = __webpack_require__(133)('wks'); -var uid = __webpack_require__(137); -var Symbol = __webpack_require__(17).Symbol; -var USE_SYMBOL = typeof Symbol == 'function'; - -var $exports = module.exports = function (name) { - return store[name] || (store[name] = - USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name)); -}; - -$exports.store = store; - - -/***/ }), -/* 21 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -exports.__esModule = true; - -var _assign = __webpack_require__(591); - -var _assign2 = _interopRequireDefault(_assign); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -exports.default = _assign2.default || function (target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i]; - - for (var key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } - } - - return target; -}; - -/***/ }), -/* 22 */ -/***/ (function(module, exports) { - -exports = module.exports = SemVer; - -// The debug function is excluded entirely from the minified version. -/* nomin */ var debug; -/* nomin */ if (typeof process === 'object' && - /* nomin */ process.env && - /* nomin */ process.env.NODE_DEBUG && - /* nomin */ /\bsemver\b/i.test(process.env.NODE_DEBUG)) - /* nomin */ debug = function() { - /* nomin */ var args = Array.prototype.slice.call(arguments, 0); - /* nomin */ args.unshift('SEMVER'); - /* nomin */ console.log.apply(console, args); - /* nomin */ }; -/* nomin */ else - /* nomin */ debug = function() {}; - -// Note: this is the semver.org version of the spec that it implements -// Not necessarily the package version of this code. -exports.SEMVER_SPEC_VERSION = '2.0.0'; - -var MAX_LENGTH = 256; -var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; - -// Max safe segment length for coercion. -var MAX_SAFE_COMPONENT_LENGTH = 16; - -// The actual regexps go on exports.re -var re = exports.re = []; -var src = exports.src = []; -var R = 0; - -// The following Regular Expressions can be used for tokenizing, -// validating, and parsing SemVer version strings. - -// ## Numeric Identifier -// A single `0`, or a non-zero digit followed by zero or more digits. - -var NUMERICIDENTIFIER = R++; -src[NUMERICIDENTIFIER] = '0|[1-9]\\d*'; -var NUMERICIDENTIFIERLOOSE = R++; -src[NUMERICIDENTIFIERLOOSE] = '[0-9]+'; - - -// ## Non-numeric Identifier -// Zero or more digits, followed by a letter or hyphen, and then zero or -// more letters, digits, or hyphens. - -var NONNUMERICIDENTIFIER = R++; -src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*'; - - -// ## Main Version -// Three dot-separated numeric identifiers. - -var MAINVERSION = R++; -src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + - '(' + src[NUMERICIDENTIFIER] + ')\\.' + - '(' + src[NUMERICIDENTIFIER] + ')'; - -var MAINVERSIONLOOSE = R++; -src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[NUMERICIDENTIFIERLOOSE] + ')'; - -// ## Pre-release Version Identifier -// A numeric identifier, or a non-numeric identifier. - -var PRERELEASEIDENTIFIER = R++; -src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + - '|' + src[NONNUMERICIDENTIFIER] + ')'; - -var PRERELEASEIDENTIFIERLOOSE = R++; -src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + - '|' + src[NONNUMERICIDENTIFIER] + ')'; - - -// ## Pre-release Version -// Hyphen, followed by one or more dot-separated pre-release version -// identifiers. - -var PRERELEASE = R++; -src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + - '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))'; - -var PRERELEASELOOSE = R++; -src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + - '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))'; - -// ## Build Metadata Identifier -// Any combination of digits, letters, or hyphens. - -var BUILDIDENTIFIER = R++; -src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+'; - -// ## Build Metadata -// Plus sign, followed by one or more period-separated build metadata -// identifiers. - -var BUILD = R++; -src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + - '(?:\\.' + src[BUILDIDENTIFIER] + ')*))'; - - -// ## Full Version String -// A main version, followed optionally by a pre-release version and -// build metadata. - -// Note that the only major, minor, patch, and pre-release sections of -// the version string are capturing groups. The build metadata is not a -// capturing group, because it should not ever be used in version -// comparison. - -var FULL = R++; -var FULLPLAIN = 'v?' + src[MAINVERSION] + - src[PRERELEASE] + '?' + - src[BUILD] + '?'; - -src[FULL] = '^' + FULLPLAIN + '$'; - -// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. -// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty -// common in the npm registry. -var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + - src[PRERELEASELOOSE] + '?' + - src[BUILD] + '?'; - -var LOOSE = R++; -src[LOOSE] = '^' + LOOSEPLAIN + '$'; - -var GTLT = R++; -src[GTLT] = '((?:<|>)?=?)'; - -// Something like "2.*" or "1.2.x". -// Note that "x.x" is a valid xRange identifer, meaning "any version" -// Only the first item is strictly required. -var XRANGEIDENTIFIERLOOSE = R++; -src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*'; -var XRANGEIDENTIFIER = R++; -src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*'; - -var XRANGEPLAIN = R++; -src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + - '(?:' + src[PRERELEASE] + ')?' + - src[BUILD] + '?' + - ')?)?'; - -var XRANGEPLAINLOOSE = R++; -src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:' + src[PRERELEASELOOSE] + ')?' + - src[BUILD] + '?' + - ')?)?'; - -var XRANGE = R++; -src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$'; -var XRANGELOOSE = R++; -src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$'; - -// Coercion. -// Extract anything that could conceivably be a part of a valid semver -var COERCE = R++; -src[COERCE] = '(?:^|[^\\d])' + - '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:$|[^\\d])'; - -// Tilde ranges. -// Meaning is "reasonably at or greater than" -var LONETILDE = R++; -src[LONETILDE] = '(?:~>?)'; - -var TILDETRIM = R++; -src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+'; -re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g'); -var tildeTrimReplace = '$1~'; - -var TILDE = R++; -src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$'; -var TILDELOOSE = R++; -src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$'; - -// Caret ranges. -// Meaning is "at least and backwards compatible with" -var LONECARET = R++; -src[LONECARET] = '(?:\\^)'; - -var CARETTRIM = R++; -src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+'; -re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g'); -var caretTrimReplace = '$1^'; - -var CARET = R++; -src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$'; -var CARETLOOSE = R++; -src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$'; - -// A simple gt/lt/eq thing, or just "" to indicate "any version" -var COMPARATORLOOSE = R++; -src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$'; -var COMPARATOR = R++; -src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$'; - - -// An expression to strip any whitespace between the gtlt and the thing -// it modifies, so that `> 1.2.3` ==> `>1.2.3` -var COMPARATORTRIM = R++; -src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + - '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')'; - -// this one has to use the /g flag -re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g'); -var comparatorTrimReplace = '$1$2$3'; - - -// Something like `1.2.3 - 1.2.4` -// Note that these all use the loose form, because they'll be -// checked against either the strict or loose comparator form -// later. -var HYPHENRANGE = R++; -src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + - '\\s+-\\s+' + - '(' + src[XRANGEPLAIN] + ')' + - '\\s*$'; - -var HYPHENRANGELOOSE = R++; -src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + - '\\s+-\\s+' + - '(' + src[XRANGEPLAINLOOSE] + ')' + - '\\s*$'; - -// Star ranges basically just allow anything at all. -var STAR = R++; -src[STAR] = '(<|>)?=?\\s*\\*'; - -// Compile to actual regexp objects. -// All are flag-free, unless they were created above with a flag. -for (var i = 0; i < R; i++) { - debug(i, src[i]); - if (!re[i]) - re[i] = new RegExp(src[i]); -} - -exports.parse = parse; -function parse(version, loose) { - if (version instanceof SemVer) - return version; - - if (typeof version !== 'string') - return null; - - if (version.length > MAX_LENGTH) - return null; - - var r = loose ? re[LOOSE] : re[FULL]; - if (!r.test(version)) - return null; - - try { - return new SemVer(version, loose); - } catch (er) { - return null; - } -} - -exports.valid = valid; -function valid(version, loose) { - var v = parse(version, loose); - return v ? v.version : null; -} - - -exports.clean = clean; -function clean(version, loose) { - var s = parse(version.trim().replace(/^[=v]+/, ''), loose); - return s ? s.version : null; -} - -exports.SemVer = SemVer; - -function SemVer(version, loose) { - if (version instanceof SemVer) { - if (version.loose === loose) - return version; - else - version = version.version; - } else if (typeof version !== 'string') { - throw new TypeError('Invalid Version: ' + version); - } - - if (version.length > MAX_LENGTH) - throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') - - if (!(this instanceof SemVer)) - return new SemVer(version, loose); - - debug('SemVer', version, loose); - this.loose = loose; - var m = version.trim().match(loose ? re[LOOSE] : re[FULL]); - - if (!m) - throw new TypeError('Invalid Version: ' + version); - - this.raw = version; - - // these are actually numbers - this.major = +m[1]; - this.minor = +m[2]; - this.patch = +m[3]; - - if (this.major > MAX_SAFE_INTEGER || this.major < 0) - throw new TypeError('Invalid major version') - - if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) - throw new TypeError('Invalid minor version') - - if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) - throw new TypeError('Invalid patch version') - - // numberify any prerelease numeric ids - if (!m[4]) - this.prerelease = []; - else - this.prerelease = m[4].split('.').map(function(id) { - if (/^[0-9]+$/.test(id)) { - var num = +id; - if (num >= 0 && num < MAX_SAFE_INTEGER) - return num; - } - return id; - }); - - this.build = m[5] ? m[5].split('.') : []; - this.format(); -} - -SemVer.prototype.format = function() { - this.version = this.major + '.' + this.minor + '.' + this.patch; - if (this.prerelease.length) - this.version += '-' + this.prerelease.join('.'); - return this.version; -}; - -SemVer.prototype.toString = function() { - return this.version; -}; - -SemVer.prototype.compare = function(other) { - debug('SemVer.compare', this.version, this.loose, other); - if (!(other instanceof SemVer)) - other = new SemVer(other, this.loose); - - return this.compareMain(other) || this.comparePre(other); -}; - -SemVer.prototype.compareMain = function(other) { - if (!(other instanceof SemVer)) - other = new SemVer(other, this.loose); - - return compareIdentifiers(this.major, other.major) || - compareIdentifiers(this.minor, other.minor) || - compareIdentifiers(this.patch, other.patch); -}; - -SemVer.prototype.comparePre = function(other) { - if (!(other instanceof SemVer)) - other = new SemVer(other, this.loose); - - // NOT having a prerelease is > having one - if (this.prerelease.length && !other.prerelease.length) - return -1; - else if (!this.prerelease.length && other.prerelease.length) - return 1; - else if (!this.prerelease.length && !other.prerelease.length) - return 0; - - var i = 0; - do { - var a = this.prerelease[i]; - var b = other.prerelease[i]; - debug('prerelease compare', i, a, b); - if (a === undefined && b === undefined) - return 0; - else if (b === undefined) - return 1; - else if (a === undefined) - return -1; - else if (a === b) - continue; - else - return compareIdentifiers(a, b); - } while (++i); -}; - -// preminor will bump the version up to the next minor release, and immediately -// down to pre-release. premajor and prepatch work the same way. -SemVer.prototype.inc = function(release, identifier) { - switch (release) { - case 'premajor': - this.prerelease.length = 0; - this.patch = 0; - this.minor = 0; - this.major++; - this.inc('pre', identifier); - break; - case 'preminor': - this.prerelease.length = 0; - this.patch = 0; - this.minor++; - this.inc('pre', identifier); - break; - case 'prepatch': - // If this is already a prerelease, it will bump to the next version - // drop any prereleases that might already exist, since they are not - // relevant at this point. - this.prerelease.length = 0; - this.inc('patch', identifier); - this.inc('pre', identifier); - break; - // If the input is a non-prerelease version, this acts the same as - // prepatch. - case 'prerelease': - if (this.prerelease.length === 0) - this.inc('patch', identifier); - this.inc('pre', identifier); - break; - - case 'major': - // If this is a pre-major version, bump up to the same major version. - // Otherwise increment major. - // 1.0.0-5 bumps to 1.0.0 - // 1.1.0 bumps to 2.0.0 - if (this.minor !== 0 || this.patch !== 0 || this.prerelease.length === 0) - this.major++; - this.minor = 0; - this.patch = 0; - this.prerelease = []; - break; - case 'minor': - // If this is a pre-minor version, bump up to the same minor version. - // Otherwise increment minor. - // 1.2.0-5 bumps to 1.2.0 - // 1.2.1 bumps to 1.3.0 - if (this.patch !== 0 || this.prerelease.length === 0) - this.minor++; - this.patch = 0; - this.prerelease = []; - break; - case 'patch': - // If this is not a pre-release version, it will increment the patch. - // If it is a pre-release it will bump up to the same patch version. - // 1.2.0-5 patches to 1.2.0 - // 1.2.0 patches to 1.2.1 - if (this.prerelease.length === 0) - this.patch++; - this.prerelease = []; - break; - // This probably shouldn't be used publicly. - // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. - case 'pre': - if (this.prerelease.length === 0) - this.prerelease = [0]; - else { - var i = this.prerelease.length; - while (--i >= 0) { - if (typeof this.prerelease[i] === 'number') { - this.prerelease[i]++; - i = -2; - } - } - if (i === -1) // didn't increment anything - this.prerelease.push(0); - } - if (identifier) { - // 1.2.0-beta.1 bumps to 1.2.0-beta.2, - // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 - if (this.prerelease[0] === identifier) { - if (isNaN(this.prerelease[1])) - this.prerelease = [identifier, 0]; - } else - this.prerelease = [identifier, 0]; - } - break; - - default: - throw new Error('invalid increment argument: ' + release); - } - this.format(); - this.raw = this.version; - return this; -}; - -exports.inc = inc; -function inc(version, release, loose, identifier) { - if (typeof(loose) === 'string') { - identifier = loose; - loose = undefined; - } - - try { - return new SemVer(version, loose).inc(release, identifier).version; - } catch (er) { - return null; - } -} - -exports.diff = diff; -function diff(version1, version2) { - if (eq(version1, version2)) { - return null; - } else { - var v1 = parse(version1); - var v2 = parse(version2); - if (v1.prerelease.length || v2.prerelease.length) { - for (var key in v1) { - if (key === 'major' || key === 'minor' || key === 'patch') { - if (v1[key] !== v2[key]) { - return 'pre'+key; - } - } - } - return 'prerelease'; - } - for (var key in v1) { - if (key === 'major' || key === 'minor' || key === 'patch') { - if (v1[key] !== v2[key]) { - return key; - } - } - } - } -} - -exports.compareIdentifiers = compareIdentifiers; - -var numeric = /^[0-9]+$/; -function compareIdentifiers(a, b) { - var anum = numeric.test(a); - var bnum = numeric.test(b); - - if (anum && bnum) { - a = +a; - b = +b; - } - - return (anum && !bnum) ? -1 : - (bnum && !anum) ? 1 : - a < b ? -1 : - a > b ? 1 : - 0; -} - -exports.rcompareIdentifiers = rcompareIdentifiers; -function rcompareIdentifiers(a, b) { - return compareIdentifiers(b, a); -} - -exports.major = major; -function major(a, loose) { - return new SemVer(a, loose).major; -} - -exports.minor = minor; -function minor(a, loose) { - return new SemVer(a, loose).minor; -} - -exports.patch = patch; -function patch(a, loose) { - return new SemVer(a, loose).patch; -} - -exports.compare = compare; -function compare(a, b, loose) { - return new SemVer(a, loose).compare(new SemVer(b, loose)); -} - -exports.compareLoose = compareLoose; -function compareLoose(a, b) { - return compare(a, b, true); -} - -exports.rcompare = rcompare; -function rcompare(a, b, loose) { - return compare(b, a, loose); -} - -exports.sort = sort; -function sort(list, loose) { - return list.sort(function(a, b) { - return exports.compare(a, b, loose); - }); -} - -exports.rsort = rsort; -function rsort(list, loose) { - return list.sort(function(a, b) { - return exports.rcompare(a, b, loose); - }); -} - -exports.gt = gt; -function gt(a, b, loose) { - return compare(a, b, loose) > 0; -} - -exports.lt = lt; -function lt(a, b, loose) { - return compare(a, b, loose) < 0; -} - -exports.eq = eq; -function eq(a, b, loose) { - return compare(a, b, loose) === 0; -} - -exports.neq = neq; -function neq(a, b, loose) { - return compare(a, b, loose) !== 0; -} - -exports.gte = gte; -function gte(a, b, loose) { - return compare(a, b, loose) >= 0; -} - -exports.lte = lte; -function lte(a, b, loose) { - return compare(a, b, loose) <= 0; -} - -exports.cmp = cmp; -function cmp(a, op, b, loose) { - var ret; - switch (op) { - case '===': - if (typeof a === 'object') a = a.version; - if (typeof b === 'object') b = b.version; - ret = a === b; - break; - case '!==': - if (typeof a === 'object') a = a.version; - if (typeof b === 'object') b = b.version; - ret = a !== b; - break; - case '': case '=': case '==': ret = eq(a, b, loose); break; - case '!=': ret = neq(a, b, loose); break; - case '>': ret = gt(a, b, loose); break; - case '>=': ret = gte(a, b, loose); break; - case '<': ret = lt(a, b, loose); break; - case '<=': ret = lte(a, b, loose); break; - default: throw new TypeError('Invalid operator: ' + op); - } - return ret; -} - -exports.Comparator = Comparator; -function Comparator(comp, loose) { - if (comp instanceof Comparator) { - if (comp.loose === loose) - return comp; - else - comp = comp.value; - } - - if (!(this instanceof Comparator)) - return new Comparator(comp, loose); - - debug('comparator', comp, loose); - this.loose = loose; - this.parse(comp); - - if (this.semver === ANY) - this.value = ''; - else - this.value = this.operator + this.semver.version; - - debug('comp', this); -} - -var ANY = {}; -Comparator.prototype.parse = function(comp) { - var r = this.loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; - var m = comp.match(r); - - if (!m) - throw new TypeError('Invalid comparator: ' + comp); - - this.operator = m[1]; - if (this.operator === '=') - this.operator = ''; - - // if it literally is just '>' or '' then allow anything. - if (!m[2]) - this.semver = ANY; - else - this.semver = new SemVer(m[2], this.loose); -}; - -Comparator.prototype.toString = function() { - return this.value; -}; - -Comparator.prototype.test = function(version) { - debug('Comparator.test', version, this.loose); - - if (this.semver === ANY) - return true; - - if (typeof version === 'string') - version = new SemVer(version, this.loose); - - return cmp(version, this.operator, this.semver, this.loose); -}; - -Comparator.prototype.intersects = function(comp, loose) { - if (!(comp instanceof Comparator)) { - throw new TypeError('a Comparator is required'); - } - - var rangeTmp; - - if (this.operator === '') { - rangeTmp = new Range(comp.value, loose); - return satisfies(this.value, rangeTmp, loose); - } else if (comp.operator === '') { - rangeTmp = new Range(this.value, loose); - return satisfies(comp.semver, rangeTmp, loose); - } - - var sameDirectionIncreasing = - (this.operator === '>=' || this.operator === '>') && - (comp.operator === '>=' || comp.operator === '>'); - var sameDirectionDecreasing = - (this.operator === '<=' || this.operator === '<') && - (comp.operator === '<=' || comp.operator === '<'); - var sameSemVer = this.semver.version === comp.semver.version; - var differentDirectionsInclusive = - (this.operator === '>=' || this.operator === '<=') && - (comp.operator === '>=' || comp.operator === '<='); - var oppositeDirectionsLessThan = - cmp(this.semver, '<', comp.semver, loose) && - ((this.operator === '>=' || this.operator === '>') && - (comp.operator === '<=' || comp.operator === '<')); - var oppositeDirectionsGreaterThan = - cmp(this.semver, '>', comp.semver, loose) && - ((this.operator === '<=' || this.operator === '<') && - (comp.operator === '>=' || comp.operator === '>')); - - return sameDirectionIncreasing || sameDirectionDecreasing || - (sameSemVer && differentDirectionsInclusive) || - oppositeDirectionsLessThan || oppositeDirectionsGreaterThan; -}; - - -exports.Range = Range; -function Range(range, loose) { - if (range instanceof Range) { - if (range.loose === loose) { - return range; - } else { - return new Range(range.raw, loose); - } - } - - if (range instanceof Comparator) { - return new Range(range.value, loose); - } - - if (!(this instanceof Range)) - return new Range(range, loose); - - this.loose = loose; - - // First, split based on boolean or || - this.raw = range; - this.set = range.split(/\s*\|\|\s*/).map(function(range) { - return this.parseRange(range.trim()); - }, this).filter(function(c) { - // throw out any that are not relevant for whatever reason - return c.length; - }); - - if (!this.set.length) { - throw new TypeError('Invalid SemVer Range: ' + range); - } - - this.format(); -} - -Range.prototype.format = function() { - this.range = this.set.map(function(comps) { - return comps.join(' ').trim(); - }).join('||').trim(); - return this.range; -}; - -Range.prototype.toString = function() { - return this.range; -}; - -Range.prototype.parseRange = function(range) { - var loose = this.loose; - range = range.trim(); - debug('range', range, loose); - // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` - var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE]; - range = range.replace(hr, hyphenReplace); - debug('hyphen replace', range); - // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` - range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace); - debug('comparator trim', range, re[COMPARATORTRIM]); - - // `~ 1.2.3` => `~1.2.3` - range = range.replace(re[TILDETRIM], tildeTrimReplace); - - // `^ 1.2.3` => `^1.2.3` - range = range.replace(re[CARETTRIM], caretTrimReplace); - - // normalize spaces - range = range.split(/\s+/).join(' '); - - // At this point, the range is completely trimmed and - // ready to be split into comparators. - - var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; - var set = range.split(' ').map(function(comp) { - return parseComparator(comp, loose); - }).join(' ').split(/\s+/); - if (this.loose) { - // in loose mode, throw out any that are not valid comparators - set = set.filter(function(comp) { - return !!comp.match(compRe); - }); - } - set = set.map(function(comp) { - return new Comparator(comp, loose); - }); - - return set; -}; - -Range.prototype.intersects = function(range, loose) { - if (!(range instanceof Range)) { - throw new TypeError('a Range is required'); - } - - return this.set.some(function(thisComparators) { - return thisComparators.every(function(thisComparator) { - return range.set.some(function(rangeComparators) { - return rangeComparators.every(function(rangeComparator) { - return thisComparator.intersects(rangeComparator, loose); - }); - }); - }); - }); -}; - -// Mostly just for testing and legacy API reasons -exports.toComparators = toComparators; -function toComparators(range, loose) { - return new Range(range, loose).set.map(function(comp) { - return comp.map(function(c) { - return c.value; - }).join(' ').trim().split(' '); - }); -} - -// comprised of xranges, tildes, stars, and gtlt's at this point. -// already replaced the hyphen ranges -// turn into a set of JUST comparators. -function parseComparator(comp, loose) { - debug('comp', comp); - comp = replaceCarets(comp, loose); - debug('caret', comp); - comp = replaceTildes(comp, loose); - debug('tildes', comp); - comp = replaceXRanges(comp, loose); - debug('xrange', comp); - comp = replaceStars(comp, loose); - debug('stars', comp); - return comp; -} - -function isX(id) { - return !id || id.toLowerCase() === 'x' || id === '*'; -} - -// ~, ~> --> * (any, kinda silly) -// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 -// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 -// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 -// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 -// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 -function replaceTildes(comp, loose) { - return comp.trim().split(/\s+/).map(function(comp) { - return replaceTilde(comp, loose); - }).join(' '); -} - -function replaceTilde(comp, loose) { - var r = loose ? re[TILDELOOSE] : re[TILDE]; - return comp.replace(r, function(_, M, m, p, pr) { - debug('tilde', comp, _, M, m, p, pr); - var ret; - - if (isX(M)) - ret = ''; - else if (isX(m)) - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; - else if (isX(p)) - // ~1.2 == >=1.2.0 <1.3.0 - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; - else if (pr) { - debug('replaceTilde pr', pr); - if (pr.charAt(0) !== '-') - pr = '-' + pr; - ret = '>=' + M + '.' + m + '.' + p + pr + - ' <' + M + '.' + (+m + 1) + '.0'; - } else - // ~1.2.3 == >=1.2.3 <1.3.0 - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0'; - - debug('tilde return', ret); - return ret; - }); -} - -// ^ --> * (any, kinda silly) -// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 -// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 -// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 -// ^1.2.3 --> >=1.2.3 <2.0.0 -// ^1.2.0 --> >=1.2.0 <2.0.0 -function replaceCarets(comp, loose) { - return comp.trim().split(/\s+/).map(function(comp) { - return replaceCaret(comp, loose); - }).join(' '); -} - -function replaceCaret(comp, loose) { - debug('caret', comp, loose); - var r = loose ? re[CARETLOOSE] : re[CARET]; - return comp.replace(r, function(_, M, m, p, pr) { - debug('caret', comp, _, M, m, p, pr); - var ret; - - if (isX(M)) - ret = ''; - else if (isX(m)) - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; - else if (isX(p)) { - if (M === '0') - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; - else - ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0'; - } else if (pr) { - debug('replaceCaret pr', pr); - if (pr.charAt(0) !== '-') - pr = '-' + pr; - if (M === '0') { - if (m === '0') - ret = '>=' + M + '.' + m + '.' + p + pr + - ' <' + M + '.' + m + '.' + (+p + 1); - else - ret = '>=' + M + '.' + m + '.' + p + pr + - ' <' + M + '.' + (+m + 1) + '.0'; - } else - ret = '>=' + M + '.' + m + '.' + p + pr + - ' <' + (+M + 1) + '.0.0'; - } else { - debug('no pr'); - if (M === '0') { - if (m === '0') - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + m + '.' + (+p + 1); - else - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0'; - } else - ret = '>=' + M + '.' + m + '.' + p + - ' <' + (+M + 1) + '.0.0'; - } - - debug('caret return', ret); - return ret; - }); -} - -function replaceXRanges(comp, loose) { - debug('replaceXRanges', comp, loose); - return comp.split(/\s+/).map(function(comp) { - return replaceXRange(comp, loose); - }).join(' '); -} - -function replaceXRange(comp, loose) { - comp = comp.trim(); - var r = loose ? re[XRANGELOOSE] : re[XRANGE]; - return comp.replace(r, function(ret, gtlt, M, m, p, pr) { - debug('xRange', comp, ret, gtlt, M, m, p, pr); - var xM = isX(M); - var xm = xM || isX(m); - var xp = xm || isX(p); - var anyX = xp; - - if (gtlt === '=' && anyX) - gtlt = ''; - - if (xM) { - if (gtlt === '>' || gtlt === '<') { - // nothing is allowed - ret = '<0.0.0'; - } else { - // nothing is forbidden - ret = '*'; - } - } else if (gtlt && anyX) { - // replace X with 0 - if (xm) - m = 0; - if (xp) - p = 0; - - if (gtlt === '>') { - // >1 => >=2.0.0 - // >1.2 => >=1.3.0 - // >1.2.3 => >= 1.2.4 - gtlt = '>='; - if (xm) { - M = +M + 1; - m = 0; - p = 0; - } else if (xp) { - m = +m + 1; - p = 0; - } - } else if (gtlt === '<=') { - // <=0.7.x is actually <0.8.0, since any 0.7.x should - // pass. Similarly, <=7.x is actually <8.0.0, etc. - gtlt = '<'; - if (xm) - M = +M + 1; - else - m = +m + 1; - } - - ret = gtlt + M + '.' + m + '.' + p; - } else if (xm) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; - } else if (xp) { - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; - } - - debug('xRange return', ret); - - return ret; - }); -} - -// Because * is AND-ed with everything else in the comparator, -// and '' means "any version", just remove the *s entirely. -function replaceStars(comp, loose) { - debug('replaceStars', comp, loose); - // Looseness is ignored here. star is always as loose as it gets! - return comp.trim().replace(re[STAR], ''); -} - -// This function is passed to string.replace(re[HYPHENRANGE]) -// M, m, patch, prerelease, build -// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 -// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do -// 1.2 - 3.4 => >=1.2.0 <3.5.0 -function hyphenReplace($0, - from, fM, fm, fp, fpr, fb, - to, tM, tm, tp, tpr, tb) { - - if (isX(fM)) - from = ''; - else if (isX(fm)) - from = '>=' + fM + '.0.0'; - else if (isX(fp)) - from = '>=' + fM + '.' + fm + '.0'; - else - from = '>=' + from; - - if (isX(tM)) - to = ''; - else if (isX(tm)) - to = '<' + (+tM + 1) + '.0.0'; - else if (isX(tp)) - to = '<' + tM + '.' + (+tm + 1) + '.0'; - else if (tpr) - to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr; - else - to = '<=' + to; - - return (from + ' ' + to).trim(); -} - - -// if ANY of the sets match ALL of its comparators, then pass -Range.prototype.test = function(version) { - if (!version) - return false; - - if (typeof version === 'string') - version = new SemVer(version, this.loose); - - for (var i = 0; i < this.set.length; i++) { - if (testSet(this.set[i], version)) - return true; - } - return false; -}; - -function testSet(set, version) { - for (var i = 0; i < set.length; i++) { - if (!set[i].test(version)) - return false; - } - - if (version.prerelease.length) { - // Find the set of versions that are allowed to have prereleases - // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 - // That should allow `1.2.3-pr.2` to pass. - // However, `1.2.4-alpha.notready` should NOT be allowed, - // even though it's within the range set by the comparators. - for (var i = 0; i < set.length; i++) { - debug(set[i].semver); - if (set[i].semver === ANY) - continue; - - if (set[i].semver.prerelease.length > 0) { - var allowed = set[i].semver; - if (allowed.major === version.major && - allowed.minor === version.minor && - allowed.patch === version.patch) - return true; - } - } - - // Version has a -pre, but it's not one of the ones we like. - return false; - } - - return true; -} - -exports.satisfies = satisfies; -function satisfies(version, range, loose) { - try { - range = new Range(range, loose); - } catch (er) { - return false; - } - return range.test(version); -} - -exports.maxSatisfying = maxSatisfying; -function maxSatisfying(versions, range, loose) { - var max = null; - var maxSV = null; - try { - var rangeObj = new Range(range, loose); - } catch (er) { - return null; - } - versions.forEach(function (v) { - if (rangeObj.test(v)) { // satisfies(v, range, loose) - if (!max || maxSV.compare(v) === -1) { // compare(max, v, true) - max = v; - maxSV = new SemVer(max, loose); - } - } - }) - return max; -} - -exports.minSatisfying = minSatisfying; -function minSatisfying(versions, range, loose) { - var min = null; - var minSV = null; - try { - var rangeObj = new Range(range, loose); - } catch (er) { - return null; - } - versions.forEach(function (v) { - if (rangeObj.test(v)) { // satisfies(v, range, loose) - if (!min || minSV.compare(v) === 1) { // compare(min, v, true) - min = v; - minSV = new SemVer(min, loose); - } - } - }) - return min; -} - -exports.validRange = validRange; -function validRange(range, loose) { - try { - // Return '*' instead of '' so that truthiness works. - // This will throw if it's invalid anyway - return new Range(range, loose).range || '*'; - } catch (er) { - return null; - } -} - -// Determine if version is less than all the versions possible in the range -exports.ltr = ltr; -function ltr(version, range, loose) { - return outside(version, range, '<', loose); -} - -// Determine if version is greater than all the versions possible in the range. -exports.gtr = gtr; -function gtr(version, range, loose) { - return outside(version, range, '>', loose); -} - -exports.outside = outside; -function outside(version, range, hilo, loose) { - version = new SemVer(version, loose); - range = new Range(range, loose); - - var gtfn, ltefn, ltfn, comp, ecomp; - switch (hilo) { - case '>': - gtfn = gt; - ltefn = lte; - ltfn = lt; - comp = '>'; - ecomp = '>='; - break; - case '<': - gtfn = lt; - ltefn = gte; - ltfn = gt; - comp = '<'; - ecomp = '<='; - break; - default: - throw new TypeError('Must provide a hilo val of "<" or ">"'); - } - - // If it satisifes the range it is not outside - if (satisfies(version, range, loose)) { - return false; - } - - // From now on, variable terms are as if we're in "gtr" mode. - // but note that everything is flipped for the "ltr" function. - - for (var i = 0; i < range.set.length; ++i) { - var comparators = range.set[i]; - - var high = null; - var low = null; - - comparators.forEach(function(comparator) { - if (comparator.semver === ANY) { - comparator = new Comparator('>=0.0.0') - } - high = high || comparator; - low = low || comparator; - if (gtfn(comparator.semver, high.semver, loose)) { - high = comparator; - } else if (ltfn(comparator.semver, low.semver, loose)) { - low = comparator; - } - }); - - // If the edge version comparator has a operator then our version - // isn't outside it - if (high.operator === comp || high.operator === ecomp) { - return false; - } - - // If the lowest version comparator has an operator and our version - // is less than it then it isn't higher than the range - if ((!low.operator || low.operator === comp) && - ltefn(version, low.semver)) { - return false; - } else if (low.operator === ecomp && ltfn(version, low.semver)) { - return false; - } - } - return true; -} - -exports.prerelease = prerelease; -function prerelease(version, loose) { - var parsed = parse(version, loose); - return (parsed && parsed.prerelease.length) ? parsed.prerelease : null; -} - -exports.intersects = intersects; -function intersects(r1, r2, loose) { - r1 = new Range(r1, loose) - r2 = new Range(r2, loose) - return r1.intersects(r2) -} - -exports.coerce = coerce; -function coerce(version) { - if (version instanceof SemVer) - return version; - - if (typeof version !== 'string') - return null; - - var match = version.match(re[COERCE]); - - if (match == null) - return null; - - return parse((match[1] || '0') + '.' + (match[2] || '0') + '.' + (match[3] || '0')); -} - - -/***/ }), -/* 23 */ -/***/ (function(module, exports) { - -module.exports = require("stream"); - -/***/ }), -/* 24 */ -/***/ (function(module, exports) { - -module.exports = require("url"); - -/***/ }), -/* 25 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Subscription; }); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__util_isArray__ = __webpack_require__(41); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__util_isObject__ = __webpack_require__(444); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__util_isFunction__ = __webpack_require__(154); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__util_tryCatch__ = __webpack_require__(56); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__util_errorObject__ = __webpack_require__(48); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__util_UnsubscriptionError__ = __webpack_require__(441); -/** PURE_IMPORTS_START _util_isArray,_util_isObject,_util_isFunction,_util_tryCatch,_util_errorObject,_util_UnsubscriptionError PURE_IMPORTS_END */ - - - - - - -var Subscription = /*@__PURE__*/ (function () { - function Subscription(unsubscribe) { - this.closed = false; - this._parent = null; - this._parents = null; - this._subscriptions = null; - if (unsubscribe) { - this._unsubscribe = unsubscribe; - } - } - Subscription.prototype.unsubscribe = function () { - var hasErrors = false; - var errors; - if (this.closed) { - return; - } - var _a = this, _parent = _a._parent, _parents = _a._parents, _unsubscribe = _a._unsubscribe, _subscriptions = _a._subscriptions; - this.closed = true; - this._parent = null; - this._parents = null; - this._subscriptions = null; - var index = -1; - var len = _parents ? _parents.length : 0; - while (_parent) { - _parent.remove(this); - _parent = ++index < len && _parents[index] || null; - } - if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__util_isFunction__["a" /* isFunction */])(_unsubscribe)) { - var trial = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_3__util_tryCatch__["a" /* tryCatch */])(_unsubscribe).call(this); - if (trial === __WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */]) { - hasErrors = true; - errors = errors || (__WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */].e instanceof __WEBPACK_IMPORTED_MODULE_5__util_UnsubscriptionError__["a" /* UnsubscriptionError */] ? - flattenUnsubscriptionErrors(__WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */].e.errors) : [__WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */].e]); - } - } - if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__util_isArray__["a" /* isArray */])(_subscriptions)) { - index = -1; - len = _subscriptions.length; - while (++index < len) { - var sub = _subscriptions[index]; - if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__util_isObject__["a" /* isObject */])(sub)) { - var trial = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_3__util_tryCatch__["a" /* tryCatch */])(sub.unsubscribe).call(sub); - if (trial === __WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */]) { - hasErrors = true; - errors = errors || []; - var err = __WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */].e; - if (err instanceof __WEBPACK_IMPORTED_MODULE_5__util_UnsubscriptionError__["a" /* UnsubscriptionError */]) { - errors = errors.concat(flattenUnsubscriptionErrors(err.errors)); - } - else { - errors.push(err); - } - } - } - } - } - if (hasErrors) { - throw new __WEBPACK_IMPORTED_MODULE_5__util_UnsubscriptionError__["a" /* UnsubscriptionError */](errors); - } - }; - Subscription.prototype.add = function (teardown) { - if (!teardown || (teardown === Subscription.EMPTY)) { - return Subscription.EMPTY; - } - if (teardown === this) { - return this; - } - var subscription = teardown; - switch (typeof teardown) { - case 'function': - subscription = new Subscription(teardown); - case 'object': - if (subscription.closed || typeof subscription.unsubscribe !== 'function') { - return subscription; - } - else if (this.closed) { - subscription.unsubscribe(); - return subscription; - } - else if (typeof subscription._addParent !== 'function') { - var tmp = subscription; - subscription = new Subscription(); - subscription._subscriptions = [tmp]; - } - break; - default: - throw new Error('unrecognized teardown ' + teardown + ' added to Subscription.'); - } - var subscriptions = this._subscriptions || (this._subscriptions = []); - subscriptions.push(subscription); - subscription._addParent(this); - return subscription; - }; - Subscription.prototype.remove = function (subscription) { - var subscriptions = this._subscriptions; - if (subscriptions) { - var subscriptionIndex = subscriptions.indexOf(subscription); - if (subscriptionIndex !== -1) { - subscriptions.splice(subscriptionIndex, 1); - } - } - }; - Subscription.prototype._addParent = function (parent) { - var _a = this, _parent = _a._parent, _parents = _a._parents; - if (!_parent || _parent === parent) { - this._parent = parent; - } - else if (!_parents) { - this._parents = [parent]; - } - else if (_parents.indexOf(parent) === -1) { - _parents.push(parent); - } - }; - Subscription.EMPTY = (function (empty) { - empty.closed = true; - return empty; - }(new Subscription())); - return Subscription; -}()); - -function flattenUnsubscriptionErrors(errors) { - return errors.reduce(function (errs, err) { return errs.concat((err instanceof __WEBPACK_IMPORTED_MODULE_5__util_UnsubscriptionError__["a" /* UnsubscriptionError */]) ? err.errors : err); }, []); -} -//# sourceMappingURL=Subscription.js.map - - -/***/ }), -/* 26 */ -/***/ (function(module, exports, __webpack_require__) { - -// Copyright 2015 Joyent, Inc. - -module.exports = { - bufferSplit: bufferSplit, - addRSAMissing: addRSAMissing, - calculateDSAPublic: calculateDSAPublic, - calculateED25519Public: calculateED25519Public, - calculateX25519Public: calculateX25519Public, - mpNormalize: mpNormalize, - mpDenormalize: mpDenormalize, - ecNormalize: ecNormalize, - countZeros: countZeros, - assertCompatible: assertCompatible, - isCompatible: isCompatible, - opensslKeyDeriv: opensslKeyDeriv, - opensshCipherInfo: opensshCipherInfo, - publicFromPrivateECDSA: publicFromPrivateECDSA, - zeroPadToLength: zeroPadToLength, - writeBitString: writeBitString, - readBitString: readBitString -}; - -var assert = __webpack_require__(16); -var Buffer = __webpack_require__(15).Buffer; -var PrivateKey = __webpack_require__(33); -var Key = __webpack_require__(27); -var crypto = __webpack_require__(11); -var algs = __webpack_require__(32); -var asn1 = __webpack_require__(66); - -var ec, jsbn; -var nacl; - -var MAX_CLASS_DEPTH = 3; - -function isCompatible(obj, klass, needVer) { - if (obj === null || typeof (obj) !== 'object') - return (false); - if (needVer === undefined) - needVer = klass.prototype._sshpkApiVersion; - if (obj instanceof klass && - klass.prototype._sshpkApiVersion[0] == needVer[0]) - return (true); - var proto = Object.getPrototypeOf(obj); - var depth = 0; - while (proto.constructor.name !== klass.name) { - proto = Object.getPrototypeOf(proto); - if (!proto || ++depth > MAX_CLASS_DEPTH) - return (false); - } - if (proto.constructor.name !== klass.name) - return (false); - var ver = proto._sshpkApiVersion; - if (ver === undefined) - ver = klass._oldVersionDetect(obj); - if (ver[0] != needVer[0] || ver[1] < needVer[1]) - return (false); - return (true); -} - -function assertCompatible(obj, klass, needVer, name) { - if (name === undefined) - name = 'object'; - assert.ok(obj, name + ' must not be null'); - assert.object(obj, name + ' must be an object'); - if (needVer === undefined) - needVer = klass.prototype._sshpkApiVersion; - if (obj instanceof klass && - klass.prototype._sshpkApiVersion[0] == needVer[0]) - return; - var proto = Object.getPrototypeOf(obj); - var depth = 0; - while (proto.constructor.name !== klass.name) { - proto = Object.getPrototypeOf(proto); - assert.ok(proto && ++depth <= MAX_CLASS_DEPTH, - name + ' must be a ' + klass.name + ' instance'); - } - assert.strictEqual(proto.constructor.name, klass.name, - name + ' must be a ' + klass.name + ' instance'); - var ver = proto._sshpkApiVersion; - if (ver === undefined) - ver = klass._oldVersionDetect(obj); - assert.ok(ver[0] == needVer[0] && ver[1] >= needVer[1], - name + ' must be compatible with ' + klass.name + ' klass ' + - 'version ' + needVer[0] + '.' + needVer[1]); -} - -var CIPHER_LEN = { - 'des-ede3-cbc': { key: 7, iv: 8 }, - 'aes-128-cbc': { key: 16, iv: 16 } -}; -var PKCS5_SALT_LEN = 8; - -function opensslKeyDeriv(cipher, salt, passphrase, count) { - assert.buffer(salt, 'salt'); - assert.buffer(passphrase, 'passphrase'); - assert.number(count, 'iteration count'); - - var clen = CIPHER_LEN[cipher]; - assert.object(clen, 'supported cipher'); - - salt = salt.slice(0, PKCS5_SALT_LEN); - - var D, D_prev, bufs; - var material = Buffer.alloc(0); - while (material.length < clen.key + clen.iv) { - bufs = []; - if (D_prev) - bufs.push(D_prev); - bufs.push(passphrase); - bufs.push(salt); - D = Buffer.concat(bufs); - for (var j = 0; j < count; ++j) - D = crypto.createHash('md5').update(D).digest(); - material = Buffer.concat([material, D]); - D_prev = D; - } - - return ({ - key: material.slice(0, clen.key), - iv: material.slice(clen.key, clen.key + clen.iv) - }); -} - -/* Count leading zero bits on a buffer */ -function countZeros(buf) { - var o = 0, obit = 8; - while (o < buf.length) { - var mask = (1 << obit); - if ((buf[o] & mask) === mask) - break; - obit--; - if (obit < 0) { - o++; - obit = 8; - } - } - return (o*8 + (8 - obit) - 1); -} - -function bufferSplit(buf, chr) { - assert.buffer(buf); - assert.string(chr); - - var parts = []; - var lastPart = 0; - var matches = 0; - for (var i = 0; i < buf.length; ++i) { - if (buf[i] === chr.charCodeAt(matches)) - ++matches; - else if (buf[i] === chr.charCodeAt(0)) - matches = 1; - else - matches = 0; - - if (matches >= chr.length) { - var newPart = i + 1; - parts.push(buf.slice(lastPart, newPart - matches)); - lastPart = newPart; - matches = 0; - } - } - if (lastPart <= buf.length) - parts.push(buf.slice(lastPart, buf.length)); - - return (parts); -} - -function ecNormalize(buf, addZero) { - assert.buffer(buf); - if (buf[0] === 0x00 && buf[1] === 0x04) { - if (addZero) - return (buf); - return (buf.slice(1)); - } else if (buf[0] === 0x04) { - if (!addZero) - return (buf); - } else { - while (buf[0] === 0x00) - buf = buf.slice(1); - if (buf[0] === 0x02 || buf[0] === 0x03) - throw (new Error('Compressed elliptic curve points ' + - 'are not supported')); - if (buf[0] !== 0x04) - throw (new Error('Not a valid elliptic curve point')); - if (!addZero) - return (buf); - } - var b = Buffer.alloc(buf.length + 1); - b[0] = 0x0; - buf.copy(b, 1); - return (b); -} - -function readBitString(der, tag) { - if (tag === undefined) - tag = asn1.Ber.BitString; - var buf = der.readString(tag, true); - assert.strictEqual(buf[0], 0x00, 'bit strings with unused bits are ' + - 'not supported (0x' + buf[0].toString(16) + ')'); - return (buf.slice(1)); -} - -function writeBitString(der, buf, tag) { - if (tag === undefined) - tag = asn1.Ber.BitString; - var b = Buffer.alloc(buf.length + 1); - b[0] = 0x00; - buf.copy(b, 1); - der.writeBuffer(b, tag); -} - -function mpNormalize(buf) { - assert.buffer(buf); - while (buf.length > 1 && buf[0] === 0x00 && (buf[1] & 0x80) === 0x00) - buf = buf.slice(1); - if ((buf[0] & 0x80) === 0x80) { - var b = Buffer.alloc(buf.length + 1); - b[0] = 0x00; - buf.copy(b, 1); - buf = b; - } - return (buf); -} - -function mpDenormalize(buf) { - assert.buffer(buf); - while (buf.length > 1 && buf[0] === 0x00) - buf = buf.slice(1); - return (buf); -} - -function zeroPadToLength(buf, len) { - assert.buffer(buf); - assert.number(len); - while (buf.length > len) { - assert.equal(buf[0], 0x00); - buf = buf.slice(1); - } - while (buf.length < len) { - var b = Buffer.alloc(buf.length + 1); - b[0] = 0x00; - buf.copy(b, 1); - buf = b; - } - return (buf); -} - -function bigintToMpBuf(bigint) { - var buf = Buffer.from(bigint.toByteArray()); - buf = mpNormalize(buf); - return (buf); -} - -function calculateDSAPublic(g, p, x) { - assert.buffer(g); - assert.buffer(p); - assert.buffer(x); - try { - var bigInt = __webpack_require__(81).BigInteger; - } catch (e) { - throw (new Error('To load a PKCS#8 format DSA private key, ' + - 'the node jsbn library is required.')); - } - g = new bigInt(g); - p = new bigInt(p); - x = new bigInt(x); - var y = g.modPow(x, p); - var ybuf = bigintToMpBuf(y); - return (ybuf); -} - -function calculateED25519Public(k) { - assert.buffer(k); - - if (nacl === undefined) - nacl = __webpack_require__(76); - - var kp = nacl.sign.keyPair.fromSeed(new Uint8Array(k)); - return (Buffer.from(kp.publicKey)); -} - -function calculateX25519Public(k) { - assert.buffer(k); - - if (nacl === undefined) - nacl = __webpack_require__(76); - - var kp = nacl.box.keyPair.fromSeed(new Uint8Array(k)); - return (Buffer.from(kp.publicKey)); -} - -function addRSAMissing(key) { - assert.object(key); - assertCompatible(key, PrivateKey, [1, 1]); - try { - var bigInt = __webpack_require__(81).BigInteger; - } catch (e) { - throw (new Error('To write a PEM private key from ' + - 'this source, the node jsbn lib is required.')); - } - - var d = new bigInt(key.part.d.data); - var buf; - - if (!key.part.dmodp) { - var p = new bigInt(key.part.p.data); - var dmodp = d.mod(p.subtract(1)); - - buf = bigintToMpBuf(dmodp); - key.part.dmodp = {name: 'dmodp', data: buf}; - key.parts.push(key.part.dmodp); - } - if (!key.part.dmodq) { - var q = new bigInt(key.part.q.data); - var dmodq = d.mod(q.subtract(1)); - - buf = bigintToMpBuf(dmodq); - key.part.dmodq = {name: 'dmodq', data: buf}; - key.parts.push(key.part.dmodq); - } -} - -function publicFromPrivateECDSA(curveName, priv) { - assert.string(curveName, 'curveName'); - assert.buffer(priv); - if (ec === undefined) - ec = __webpack_require__(139); - if (jsbn === undefined) - jsbn = __webpack_require__(81).BigInteger; - var params = algs.curves[curveName]; - var p = new jsbn(params.p); - var a = new jsbn(params.a); - var b = new jsbn(params.b); - var curve = new ec.ECCurveFp(p, a, b); - var G = curve.decodePointHex(params.G.toString('hex')); - - var d = new jsbn(mpNormalize(priv)); - var pub = G.multiply(d); - pub = Buffer.from(curve.encodePointHex(pub), 'hex'); - - var parts = []; - parts.push({name: 'curve', data: Buffer.from(curveName)}); - parts.push({name: 'Q', data: pub}); - - var key = new Key({type: 'ecdsa', curve: curve, parts: parts}); - return (key); -} - -function opensshCipherInfo(cipher) { - var inf = {}; - switch (cipher) { - case '3des-cbc': - inf.keySize = 24; - inf.blockSize = 8; - inf.opensslName = 'des-ede3-cbc'; - break; - case 'blowfish-cbc': - inf.keySize = 16; - inf.blockSize = 8; - inf.opensslName = 'bf-cbc'; - break; - case 'aes128-cbc': - case 'aes128-ctr': - case 'aes128-gcm@openssh.com': - inf.keySize = 16; - inf.blockSize = 16; - inf.opensslName = 'aes-128-' + cipher.slice(7, 10); - break; - case 'aes192-cbc': - case 'aes192-ctr': - case 'aes192-gcm@openssh.com': - inf.keySize = 24; - inf.blockSize = 16; - inf.opensslName = 'aes-192-' + cipher.slice(7, 10); - break; - case 'aes256-cbc': - case 'aes256-ctr': - case 'aes256-gcm@openssh.com': - inf.keySize = 32; - inf.blockSize = 16; - inf.opensslName = 'aes-256-' + cipher.slice(7, 10); - break; - default: - throw (new Error( - 'Unsupported openssl cipher "' + cipher + '"')); - } - return (inf); -} - - -/***/ }), -/* 27 */ -/***/ (function(module, exports, __webpack_require__) { - -// Copyright 2017 Joyent, Inc. - -module.exports = Key; - -var assert = __webpack_require__(16); -var algs = __webpack_require__(32); -var crypto = __webpack_require__(11); -var Fingerprint = __webpack_require__(156); -var Signature = __webpack_require__(75); -var DiffieHellman = __webpack_require__(325).DiffieHellman; -var errs = __webpack_require__(74); -var utils = __webpack_require__(26); -var PrivateKey = __webpack_require__(33); -var edCompat; - -try { - edCompat = __webpack_require__(454); -} catch (e) { - /* Just continue through, and bail out if we try to use it. */ -} - -var InvalidAlgorithmError = errs.InvalidAlgorithmError; -var KeyParseError = errs.KeyParseError; - -var formats = {}; -formats['auto'] = __webpack_require__(455); -formats['pem'] = __webpack_require__(86); -formats['pkcs1'] = __webpack_require__(327); -formats['pkcs8'] = __webpack_require__(157); -formats['rfc4253'] = __webpack_require__(103); -formats['ssh'] = __webpack_require__(456); -formats['ssh-private'] = __webpack_require__(192); -formats['openssh'] = formats['ssh-private']; -formats['dnssec'] = __webpack_require__(326); - -function Key(opts) { - assert.object(opts, 'options'); - assert.arrayOfObject(opts.parts, 'options.parts'); - assert.string(opts.type, 'options.type'); - assert.optionalString(opts.comment, 'options.comment'); - - var algInfo = algs.info[opts.type]; - if (typeof (algInfo) !== 'object') - throw (new InvalidAlgorithmError(opts.type)); - - var partLookup = {}; - for (var i = 0; i < opts.parts.length; ++i) { - var part = opts.parts[i]; - partLookup[part.name] = part; - } - - this.type = opts.type; - this.parts = opts.parts; - this.part = partLookup; - this.comment = undefined; - this.source = opts.source; - - /* for speeding up hashing/fingerprint operations */ - this._rfc4253Cache = opts._rfc4253Cache; - this._hashCache = {}; - - var sz; - this.curve = undefined; - if (this.type === 'ecdsa') { - var curve = this.part.curve.data.toString(); - this.curve = curve; - sz = algs.curves[curve].size; - } else if (this.type === 'ed25519' || this.type === 'curve25519') { - sz = 256; - this.curve = 'curve25519'; - } else { - var szPart = this.part[algInfo.sizePart]; - sz = szPart.data.length; - sz = sz * 8 - utils.countZeros(szPart.data); - } - this.size = sz; -} - -Key.formats = formats; - -Key.prototype.toBuffer = function (format, options) { - if (format === undefined) - format = 'ssh'; - assert.string(format, 'format'); - assert.object(formats[format], 'formats[format]'); - assert.optionalObject(options, 'options'); - - if (format === 'rfc4253') { - if (this._rfc4253Cache === undefined) - this._rfc4253Cache = formats['rfc4253'].write(this); - return (this._rfc4253Cache); - } - - return (formats[format].write(this, options)); -}; - -Key.prototype.toString = function (format, options) { - return (this.toBuffer(format, options).toString()); -}; - -Key.prototype.hash = function (algo) { - assert.string(algo, 'algorithm'); - algo = algo.toLowerCase(); - if (algs.hashAlgs[algo] === undefined) - throw (new InvalidAlgorithmError(algo)); - - if (this._hashCache[algo]) - return (this._hashCache[algo]); - var hash = crypto.createHash(algo). - update(this.toBuffer('rfc4253')).digest(); - this._hashCache[algo] = hash; - return (hash); -}; - -Key.prototype.fingerprint = function (algo) { - if (algo === undefined) - algo = 'sha256'; - assert.string(algo, 'algorithm'); - var opts = { - type: 'key', - hash: this.hash(algo), - algorithm: algo - }; - return (new Fingerprint(opts)); -}; - -Key.prototype.defaultHashAlgorithm = function () { - var hashAlgo = 'sha1'; - if (this.type === 'rsa') - hashAlgo = 'sha256'; - if (this.type === 'dsa' && this.size > 1024) - hashAlgo = 'sha256'; - if (this.type === 'ed25519') - hashAlgo = 'sha512'; - if (this.type === 'ecdsa') { - if (this.size <= 256) - hashAlgo = 'sha256'; - else if (this.size <= 384) - hashAlgo = 'sha384'; - else - hashAlgo = 'sha512'; - } - return (hashAlgo); -}; - -Key.prototype.createVerify = function (hashAlgo) { - if (hashAlgo === undefined) - hashAlgo = this.defaultHashAlgorithm(); - assert.string(hashAlgo, 'hash algorithm'); - - /* ED25519 is not supported by OpenSSL, use a javascript impl. */ - if (this.type === 'ed25519' && edCompat !== undefined) - return (new edCompat.Verifier(this, hashAlgo)); - if (this.type === 'curve25519') - throw (new Error('Curve25519 keys are not suitable for ' + - 'signing or verification')); - - var v, nm, err; - try { - nm = hashAlgo.toUpperCase(); - v = crypto.createVerify(nm); - } catch (e) { - err = e; - } - if (v === undefined || (err instanceof Error && - err.message.match(/Unknown message digest/))) { - nm = 'RSA-'; - nm += hashAlgo.toUpperCase(); - v = crypto.createVerify(nm); - } - assert.ok(v, 'failed to create verifier'); - var oldVerify = v.verify.bind(v); - var key = this.toBuffer('pkcs8'); - var curve = this.curve; - var self = this; - v.verify = function (signature, fmt) { - if (Signature.isSignature(signature, [2, 0])) { - if (signature.type !== self.type) - return (false); - if (signature.hashAlgorithm && - signature.hashAlgorithm !== hashAlgo) - return (false); - if (signature.curve && self.type === 'ecdsa' && - signature.curve !== curve) - return (false); - return (oldVerify(key, signature.toBuffer('asn1'))); - - } else if (typeof (signature) === 'string' || - Buffer.isBuffer(signature)) { - return (oldVerify(key, signature, fmt)); - - /* - * Avoid doing this on valid arguments, walking the prototype - * chain can be quite slow. - */ - } else if (Signature.isSignature(signature, [1, 0])) { - throw (new Error('signature was created by too old ' + - 'a version of sshpk and cannot be verified')); - - } else { - throw (new TypeError('signature must be a string, ' + - 'Buffer, or Signature object')); - } - }; - return (v); -}; - -Key.prototype.createDiffieHellman = function () { - if (this.type === 'rsa') - throw (new Error('RSA keys do not support Diffie-Hellman')); - - return (new DiffieHellman(this)); -}; -Key.prototype.createDH = Key.prototype.createDiffieHellman; - -Key.parse = function (data, format, options) { - if (typeof (data) !== 'string') - assert.buffer(data, 'data'); - if (format === undefined) - format = 'auto'; - assert.string(format, 'format'); - if (typeof (options) === 'string') - options = { filename: options }; - assert.optionalObject(options, 'options'); - if (options === undefined) - options = {}; - assert.optionalString(options.filename, 'options.filename'); - if (options.filename === undefined) - options.filename = '(unnamed)'; - - assert.object(formats[format], 'formats[format]'); - - try { - var k = formats[format].read(data, options); - if (k instanceof PrivateKey) - k = k.toPublic(); - if (!k.comment) - k.comment = options.filename; - return (k); - } catch (e) { - if (e.name === 'KeyEncryptedError') - throw (e); - throw (new KeyParseError(options.filename, format, e)); - } -}; - -Key.isKey = function (obj, ver) { - return (utils.isCompatible(obj, Key, ver)); -}; - -/* - * API versions for Key: - * [1,0] -- initial ver, may take Signature for createVerify or may not - * [1,1] -- added pkcs1, pkcs8 formats - * [1,2] -- added auto, ssh-private, openssh formats - * [1,3] -- added defaultHashAlgorithm - * [1,4] -- added ed support, createDH - * [1,5] -- first explicitly tagged version - * [1,6] -- changed ed25519 part names - */ -Key.prototype._sshpkApiVersion = [1, 6]; - -Key._oldVersionDetect = function (obj) { - assert.func(obj.toBuffer); - assert.func(obj.fingerprint); - if (obj.createDH) - return ([1, 4]); - if (obj.defaultHashAlgorithm) - return ([1, 3]); - if (obj.formats['auto']) - return ([1, 2]); - if (obj.formats['pkcs1']) - return ([1, 1]); - return ([1, 0]); -}; - - -/***/ }), -/* 28 */ -/***/ (function(module, exports) { - -module.exports = require("assert"); - -/***/ }), -/* 29 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = nullify; -function nullify(obj = {}) { - if (Array.isArray(obj)) { - for (var _iterator = obj, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } - - const item = _ref; - - nullify(item); - } - } else if (obj !== null && typeof obj === 'object' || typeof obj === 'function') { - Object.setPrototypeOf(obj, null); - - // for..in can only be applied to 'object', not 'function' - if (typeof obj === 'object') { - for (const key in obj) { - nullify(obj[key]); - } - } - } - - return obj; -} - -/***/ }), -/* 30 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -const escapeStringRegexp = __webpack_require__(388); -const ansiStyles = __webpack_require__(506); -const stdoutColor = __webpack_require__(598).stdout; - -const template = __webpack_require__(599); - -const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); - -// `supportsColor.level` → `ansiStyles.color[name]` mapping -const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m']; - -// `color-convert` models to exclude from the Chalk API due to conflicts and such -const skipModels = new Set(['gray']); - -const styles = Object.create(null); - -function applyOptions(obj, options) { - options = options || {}; - - // Detect level if not set manually - const scLevel = stdoutColor ? stdoutColor.level : 0; - obj.level = options.level === undefined ? scLevel : options.level; - obj.enabled = 'enabled' in options ? options.enabled : obj.level > 0; -} - -function Chalk(options) { - // We check for this.template here since calling `chalk.constructor()` - // by itself will have a `this` of a previously constructed chalk object - if (!this || !(this instanceof Chalk) || this.template) { - const chalk = {}; - applyOptions(chalk, options); - - chalk.template = function () { - const args = [].slice.call(arguments); - return chalkTag.apply(null, [chalk.template].concat(args)); - }; - - Object.setPrototypeOf(chalk, Chalk.prototype); - Object.setPrototypeOf(chalk.template, chalk); - - chalk.template.constructor = Chalk; - - return chalk.template; - } - - applyOptions(this, options); -} - -// Use bright blue on Windows as the normal blue color is illegible -if (isSimpleWindowsTerm) { - ansiStyles.blue.open = '\u001B[94m'; -} - -for (const key of Object.keys(ansiStyles)) { - ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); - - styles[key] = { - get() { - const codes = ansiStyles[key]; - return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key); - } - }; -} - -styles.visible = { - get() { - return build.call(this, this._styles || [], true, 'visible'); - } -}; - -ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g'); -for (const model of Object.keys(ansiStyles.color.ansi)) { - if (skipModels.has(model)) { - continue; - } - - styles[model] = { - get() { - const level = this.level; - return function () { - const open = ansiStyles.color[levelMapping[level]][model].apply(null, arguments); - const codes = { - open, - close: ansiStyles.color.close, - closeRe: ansiStyles.color.closeRe - }; - return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); - }; - } - }; -} - -ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g'); -for (const model of Object.keys(ansiStyles.bgColor.ansi)) { - if (skipModels.has(model)) { - continue; - } - - const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); - styles[bgModel] = { - get() { - const level = this.level; - return function () { - const open = ansiStyles.bgColor[levelMapping[level]][model].apply(null, arguments); - const codes = { - open, - close: ansiStyles.bgColor.close, - closeRe: ansiStyles.bgColor.closeRe - }; - return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); - }; - } - }; -} - -const proto = Object.defineProperties(() => {}, styles); - -function build(_styles, _empty, key) { - const builder = function () { - return applyStyle.apply(builder, arguments); - }; - - builder._styles = _styles; - builder._empty = _empty; - - const self = this; - - Object.defineProperty(builder, 'level', { - enumerable: true, - get() { - return self.level; - }, - set(level) { - self.level = level; - } - }); - - Object.defineProperty(builder, 'enabled', { - enumerable: true, - get() { - return self.enabled; - }, - set(enabled) { - self.enabled = enabled; - } - }); - - // See below for fix regarding invisible grey/dim combination on Windows - builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey'; - - // `__proto__` is used because we must return a function, but there is - // no way to create a function with a different prototype - builder.__proto__ = proto; // eslint-disable-line no-proto - - return builder; -} - -function applyStyle() { - // Support varags, but simply cast to string in case there's only one arg - const args = arguments; - const argsLen = args.length; - let str = String(arguments[0]); - - if (argsLen === 0) { - return ''; - } - - if (argsLen > 1) { - // Don't slice `arguments`, it prevents V8 optimizations - for (let a = 1; a < argsLen; a++) { - str += ' ' + args[a]; - } - } - - if (!this.enabled || this.level <= 0 || !str) { - return this._empty ? '' : str; - } - - // Turns out that on Windows dimmed gray text becomes invisible in cmd.exe, - // see https://github.com/chalk/chalk/issues/58 - // If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop. - const originalDim = ansiStyles.dim.open; - if (isSimpleWindowsTerm && this.hasGrey) { - ansiStyles.dim.open = ''; - } - - for (const code of this._styles.slice().reverse()) { - // Replace any instances already present with a re-opening code - // otherwise only the part of the string until said closing code - // will be colored, and the rest will simply be 'plain'. - str = code.open + str.replace(code.closeRe, code.open) + code.close; - - // Close the styling before a linebreak and reopen - // after next line to fix a bleed issue on macOS - // https://github.com/chalk/chalk/pull/92 - str = str.replace(/\r?\n/g, `${code.close}$&${code.open}`); - } - - // Reset the original `dim` if we changed it to work around the Windows dimmed gray issue - ansiStyles.dim.open = originalDim; - - return str; -} - -function chalkTag(chalk, strings) { - if (!Array.isArray(strings)) { - // If chalk() was called by itself or with a string, - // return the string itself as a string. - return [].slice.call(arguments, 1).join(' '); - } - - const args = [].slice.call(arguments, 2); - const parts = [strings.raw[0]]; - - for (let i = 1; i < strings.length; i++) { - parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&')); - parts.push(String(strings.raw[i])); - } - - return template(chalk, parts.join('')); -} - -Object.defineProperties(Chalk.prototype, styles); - -module.exports = Chalk(); // eslint-disable-line new-cap -module.exports.supportsColor = stdoutColor; -module.exports.default = module.exports; // For TypeScript - - -/***/ }), -/* 31 */ -/***/ (function(module, exports) { - -var core = module.exports = { version: '2.5.7' }; -if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef - - -/***/ }), -/* 32 */ -/***/ (function(module, exports, __webpack_require__) { - -// Copyright 2015 Joyent, Inc. - -var Buffer = __webpack_require__(15).Buffer; - -var algInfo = { - 'dsa': { - parts: ['p', 'q', 'g', 'y'], - sizePart: 'p' - }, - 'rsa': { - parts: ['e', 'n'], - sizePart: 'n' - }, - 'ecdsa': { - parts: ['curve', 'Q'], - sizePart: 'Q' - }, - 'ed25519': { - parts: ['A'], - sizePart: 'A' - } -}; -algInfo['curve25519'] = algInfo['ed25519']; - -var algPrivInfo = { - 'dsa': { - parts: ['p', 'q', 'g', 'y', 'x'] - }, - 'rsa': { - parts: ['n', 'e', 'd', 'iqmp', 'p', 'q'] - }, - 'ecdsa': { - parts: ['curve', 'Q', 'd'] - }, - 'ed25519': { - parts: ['A', 'k'] - } -}; -algPrivInfo['curve25519'] = algPrivInfo['ed25519']; - -var hashAlgs = { - 'md5': true, - 'sha1': true, - 'sha256': true, - 'sha384': true, - 'sha512': true -}; - -/* - * Taken from - * http://csrc.nist.gov/groups/ST/toolkit/documents/dss/NISTReCur.pdf - */ -var curves = { - 'nistp256': { - size: 256, - pkcs8oid: '1.2.840.10045.3.1.7', - p: Buffer.from(('00' + - 'ffffffff 00000001 00000000 00000000' + - '00000000 ffffffff ffffffff ffffffff'). - replace(/ /g, ''), 'hex'), - a: Buffer.from(('00' + - 'FFFFFFFF 00000001 00000000 00000000' + - '00000000 FFFFFFFF FFFFFFFF FFFFFFFC'). - replace(/ /g, ''), 'hex'), - b: Buffer.from(( - '5ac635d8 aa3a93e7 b3ebbd55 769886bc' + - '651d06b0 cc53b0f6 3bce3c3e 27d2604b'). - replace(/ /g, ''), 'hex'), - s: Buffer.from(('00' + - 'c49d3608 86e70493 6a6678e1 139d26b7' + - '819f7e90'). - replace(/ /g, ''), 'hex'), - n: Buffer.from(('00' + - 'ffffffff 00000000 ffffffff ffffffff' + - 'bce6faad a7179e84 f3b9cac2 fc632551'). - replace(/ /g, ''), 'hex'), - G: Buffer.from(('04' + - '6b17d1f2 e12c4247 f8bce6e5 63a440f2' + - '77037d81 2deb33a0 f4a13945 d898c296' + - '4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16' + - '2bce3357 6b315ece cbb64068 37bf51f5'). - replace(/ /g, ''), 'hex') - }, - 'nistp384': { - size: 384, - pkcs8oid: '1.3.132.0.34', - p: Buffer.from(('00' + - 'ffffffff ffffffff ffffffff ffffffff' + - 'ffffffff ffffffff ffffffff fffffffe' + - 'ffffffff 00000000 00000000 ffffffff'). - replace(/ /g, ''), 'hex'), - a: Buffer.from(('00' + - 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + - 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE' + - 'FFFFFFFF 00000000 00000000 FFFFFFFC'). - replace(/ /g, ''), 'hex'), - b: Buffer.from(( - 'b3312fa7 e23ee7e4 988e056b e3f82d19' + - '181d9c6e fe814112 0314088f 5013875a' + - 'c656398d 8a2ed19d 2a85c8ed d3ec2aef'). - replace(/ /g, ''), 'hex'), - s: Buffer.from(('00' + - 'a335926a a319a27a 1d00896a 6773a482' + - '7acdac73'). - replace(/ /g, ''), 'hex'), - n: Buffer.from(('00' + - 'ffffffff ffffffff ffffffff ffffffff' + - 'ffffffff ffffffff c7634d81 f4372ddf' + - '581a0db2 48b0a77a ecec196a ccc52973'). - replace(/ /g, ''), 'hex'), - G: Buffer.from(('04' + - 'aa87ca22 be8b0537 8eb1c71e f320ad74' + - '6e1d3b62 8ba79b98 59f741e0 82542a38' + - '5502f25d bf55296c 3a545e38 72760ab7' + - '3617de4a 96262c6f 5d9e98bf 9292dc29' + - 'f8f41dbd 289a147c e9da3113 b5f0b8c0' + - '0a60b1ce 1d7e819d 7a431d7c 90ea0e5f'). - replace(/ /g, ''), 'hex') - }, - 'nistp521': { - size: 521, - pkcs8oid: '1.3.132.0.35', - p: Buffer.from(( - '01ffffff ffffffff ffffffff ffffffff' + - 'ffffffff ffffffff ffffffff ffffffff' + - 'ffffffff ffffffff ffffffff ffffffff' + - 'ffffffff ffffffff ffffffff ffffffff' + - 'ffff').replace(/ /g, ''), 'hex'), - a: Buffer.from(('01FF' + - 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + - 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + - 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + - 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFC'). - replace(/ /g, ''), 'hex'), - b: Buffer.from(('51' + - '953eb961 8e1c9a1f 929a21a0 b68540ee' + - 'a2da725b 99b315f3 b8b48991 8ef109e1' + - '56193951 ec7e937b 1652c0bd 3bb1bf07' + - '3573df88 3d2c34f1 ef451fd4 6b503f00'). - replace(/ /g, ''), 'hex'), - s: Buffer.from(('00' + - 'd09e8800 291cb853 96cc6717 393284aa' + - 'a0da64ba').replace(/ /g, ''), 'hex'), - n: Buffer.from(('01ff' + - 'ffffffff ffffffff ffffffff ffffffff' + - 'ffffffff ffffffff ffffffff fffffffa' + - '51868783 bf2f966b 7fcc0148 f709a5d0' + - '3bb5c9b8 899c47ae bb6fb71e 91386409'). - replace(/ /g, ''), 'hex'), - G: Buffer.from(('04' + - '00c6 858e06b7 0404e9cd 9e3ecb66 2395b442' + - '9c648139 053fb521 f828af60 6b4d3dba' + - 'a14b5e77 efe75928 fe1dc127 a2ffa8de' + - '3348b3c1 856a429b f97e7e31 c2e5bd66' + - '0118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9' + - '98f54449 579b4468 17afbd17 273e662c' + - '97ee7299 5ef42640 c550b901 3fad0761' + - '353c7086 a272c240 88be9476 9fd16650'). - replace(/ /g, ''), 'hex') - } -}; - -module.exports = { - info: algInfo, - privInfo: algPrivInfo, - hashAlgs: hashAlgs, - curves: curves -}; - - -/***/ }), -/* 33 */ -/***/ (function(module, exports, __webpack_require__) { - -// Copyright 2017 Joyent, Inc. - -module.exports = PrivateKey; - -var assert = __webpack_require__(16); -var Buffer = __webpack_require__(15).Buffer; -var algs = __webpack_require__(32); -var crypto = __webpack_require__(11); -var Fingerprint = __webpack_require__(156); -var Signature = __webpack_require__(75); -var errs = __webpack_require__(74); -var util = __webpack_require__(3); -var utils = __webpack_require__(26); -var dhe = __webpack_require__(325); -var generateECDSA = dhe.generateECDSA; -var generateED25519 = dhe.generateED25519; -var edCompat; -var nacl; - -try { - edCompat = __webpack_require__(454); -} catch (e) { - /* Just continue through, and bail out if we try to use it. */ -} - -var Key = __webpack_require__(27); - -var InvalidAlgorithmError = errs.InvalidAlgorithmError; -var KeyParseError = errs.KeyParseError; -var KeyEncryptedError = errs.KeyEncryptedError; - -var formats = {}; -formats['auto'] = __webpack_require__(455); -formats['pem'] = __webpack_require__(86); -formats['pkcs1'] = __webpack_require__(327); -formats['pkcs8'] = __webpack_require__(157); -formats['rfc4253'] = __webpack_require__(103); -formats['ssh-private'] = __webpack_require__(192); -formats['openssh'] = formats['ssh-private']; -formats['ssh'] = formats['ssh-private']; -formats['dnssec'] = __webpack_require__(326); - -function PrivateKey(opts) { - assert.object(opts, 'options'); - Key.call(this, opts); - - this._pubCache = undefined; -} -util.inherits(PrivateKey, Key); - -PrivateKey.formats = formats; - -PrivateKey.prototype.toBuffer = function (format, options) { - if (format === undefined) - format = 'pkcs1'; - assert.string(format, 'format'); - assert.object(formats[format], 'formats[format]'); - assert.optionalObject(options, 'options'); - - return (formats[format].write(this, options)); -}; - -PrivateKey.prototype.hash = function (algo) { - return (this.toPublic().hash(algo)); -}; - -PrivateKey.prototype.toPublic = function () { - if (this._pubCache) - return (this._pubCache); - - var algInfo = algs.info[this.type]; - var pubParts = []; - for (var i = 0; i < algInfo.parts.length; ++i) { - var p = algInfo.parts[i]; - pubParts.push(this.part[p]); - } - - this._pubCache = new Key({ - type: this.type, - source: this, - parts: pubParts - }); - if (this.comment) - this._pubCache.comment = this.comment; - return (this._pubCache); -}; - -PrivateKey.prototype.derive = function (newType) { - assert.string(newType, 'type'); - var priv, pub, pair; - - if (this.type === 'ed25519' && newType === 'curve25519') { - if (nacl === undefined) - nacl = __webpack_require__(76); - - priv = this.part.k.data; - if (priv[0] === 0x00) - priv = priv.slice(1); - - pair = nacl.box.keyPair.fromSecretKey(new Uint8Array(priv)); - pub = Buffer.from(pair.publicKey); - - return (new PrivateKey({ - type: 'curve25519', - parts: [ - { name: 'A', data: utils.mpNormalize(pub) }, - { name: 'k', data: utils.mpNormalize(priv) } - ] - })); - } else if (this.type === 'curve25519' && newType === 'ed25519') { - if (nacl === undefined) - nacl = __webpack_require__(76); - - priv = this.part.k.data; - if (priv[0] === 0x00) - priv = priv.slice(1); - - pair = nacl.sign.keyPair.fromSeed(new Uint8Array(priv)); - pub = Buffer.from(pair.publicKey); - - return (new PrivateKey({ - type: 'ed25519', - parts: [ - { name: 'A', data: utils.mpNormalize(pub) }, - { name: 'k', data: utils.mpNormalize(priv) } - ] - })); - } - throw (new Error('Key derivation not supported from ' + this.type + - ' to ' + newType)); -}; - -PrivateKey.prototype.createVerify = function (hashAlgo) { - return (this.toPublic().createVerify(hashAlgo)); -}; - -PrivateKey.prototype.createSign = function (hashAlgo) { - if (hashAlgo === undefined) - hashAlgo = this.defaultHashAlgorithm(); - assert.string(hashAlgo, 'hash algorithm'); - - /* ED25519 is not supported by OpenSSL, use a javascript impl. */ - if (this.type === 'ed25519' && edCompat !== undefined) - return (new edCompat.Signer(this, hashAlgo)); - if (this.type === 'curve25519') - throw (new Error('Curve25519 keys are not suitable for ' + - 'signing or verification')); - - var v, nm, err; - try { - nm = hashAlgo.toUpperCase(); - v = crypto.createSign(nm); - } catch (e) { - err = e; - } - if (v === undefined || (err instanceof Error && - err.message.match(/Unknown message digest/))) { - nm = 'RSA-'; - nm += hashAlgo.toUpperCase(); - v = crypto.createSign(nm); - } - assert.ok(v, 'failed to create verifier'); - var oldSign = v.sign.bind(v); - var key = this.toBuffer('pkcs1'); - var type = this.type; - var curve = this.curve; - v.sign = function () { - var sig = oldSign(key); - if (typeof (sig) === 'string') - sig = Buffer.from(sig, 'binary'); - sig = Signature.parse(sig, type, 'asn1'); - sig.hashAlgorithm = hashAlgo; - sig.curve = curve; - return (sig); - }; - return (v); -}; - -PrivateKey.parse = function (data, format, options) { - if (typeof (data) !== 'string') - assert.buffer(data, 'data'); - if (format === undefined) - format = 'auto'; - assert.string(format, 'format'); - if (typeof (options) === 'string') - options = { filename: options }; - assert.optionalObject(options, 'options'); - if (options === undefined) - options = {}; - assert.optionalString(options.filename, 'options.filename'); - if (options.filename === undefined) - options.filename = '(unnamed)'; - - assert.object(formats[format], 'formats[format]'); - - try { - var k = formats[format].read(data, options); - assert.ok(k instanceof PrivateKey, 'key is not a private key'); - if (!k.comment) - k.comment = options.filename; - return (k); - } catch (e) { - if (e.name === 'KeyEncryptedError') - throw (e); - throw (new KeyParseError(options.filename, format, e)); - } -}; - -PrivateKey.isPrivateKey = function (obj, ver) { - return (utils.isCompatible(obj, PrivateKey, ver)); -}; - -PrivateKey.generate = function (type, options) { - if (options === undefined) - options = {}; - assert.object(options, 'options'); - - switch (type) { - case 'ecdsa': - if (options.curve === undefined) - options.curve = 'nistp256'; - assert.string(options.curve, 'options.curve'); - return (generateECDSA(options.curve)); - case 'ed25519': - return (generateED25519()); - default: - throw (new Error('Key generation not supported with key ' + - 'type "' + type + '"')); - } -}; - -/* - * API versions for PrivateKey: - * [1,0] -- initial ver - * [1,1] -- added auto, pkcs[18], openssh/ssh-private formats - * [1,2] -- added defaultHashAlgorithm - * [1,3] -- added derive, ed, createDH - * [1,4] -- first tagged version - * [1,5] -- changed ed25519 part names and format - */ -PrivateKey.prototype._sshpkApiVersion = [1, 5]; - -PrivateKey._oldVersionDetect = function (obj) { - assert.func(obj.toPublic); - assert.func(obj.createSign); - if (obj.derive) - return ([1, 3]); - if (obj.defaultHashAlgorithm) - return ([1, 2]); - if (obj.formats['auto']) - return ([1, 1]); - return ([1, 0]); -}; - - -/***/ }), -/* 34 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.wrapLifecycle = exports.run = exports.install = exports.Install = undefined; - -var _extends2; - -function _load_extends() { - return _extends2 = _interopRequireDefault(__webpack_require__(21)); -} - -var _asyncToGenerator2; - -function _load_asyncToGenerator() { - return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(2)); -} - -let install = exports.install = (() => { - var _ref29 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (config, reporter, flags, lockfile) { - yield wrapLifecycle(config, flags, (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - const install = new Install(flags, config, reporter, lockfile); - yield install.init(); - })); - }); - - return function install(_x7, _x8, _x9, _x10) { - return _ref29.apply(this, arguments); - }; -})(); - -let run = exports.run = (() => { - var _ref31 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (config, reporter, flags, args) { - let lockfile; - let error = 'installCommandRenamed'; - if (flags.lockfile === false) { - lockfile = new (_lockfile || _load_lockfile()).default(); - } else { - lockfile = yield (_lockfile || _load_lockfile()).default.fromDirectory(config.lockfileFolder, reporter); - } - - if (args.length) { - const exampleArgs = args.slice(); - - if (flags.saveDev) { - exampleArgs.push('--dev'); - } - if (flags.savePeer) { - exampleArgs.push('--peer'); - } - if (flags.saveOptional) { - exampleArgs.push('--optional'); - } - if (flags.saveExact) { - exampleArgs.push('--exact'); - } - if (flags.saveTilde) { - exampleArgs.push('--tilde'); - } - let command = 'add'; - if (flags.global) { - error = 'globalFlagRemoved'; - command = 'global add'; - } - throw new (_errors || _load_errors()).MessageError(reporter.lang(error, `yarn ${command} ${exampleArgs.join(' ')}`)); - } - - yield install(config, reporter, flags, lockfile); - }); - - return function run(_x11, _x12, _x13, _x14) { - return _ref31.apply(this, arguments); - }; -})(); - -let wrapLifecycle = exports.wrapLifecycle = (() => { - var _ref32 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (config, flags, factory) { - yield config.executeLifecycleScript('preinstall'); - - yield factory(); - - // npm behaviour, seems kinda funky but yay compatibility - yield config.executeLifecycleScript('install'); - yield config.executeLifecycleScript('postinstall'); - - if (!config.production) { - if (!config.disablePrepublish) { - yield config.executeLifecycleScript('prepublish'); - } - yield config.executeLifecycleScript('prepare'); - } - }); - - return function wrapLifecycle(_x15, _x16, _x17) { - return _ref32.apply(this, arguments); - }; -})(); - -exports.hasWrapper = hasWrapper; -exports.setFlags = setFlags; - -var _objectPath; - -function _load_objectPath() { - return _objectPath = _interopRequireDefault(__webpack_require__(304)); -} - -var _hooks; - -function _load_hooks() { - return _hooks = __webpack_require__(374); -} - -var _index; - -function _load_index() { - return _index = _interopRequireDefault(__webpack_require__(220)); -} - -var _errors; - -function _load_errors() { - return _errors = __webpack_require__(6); -} - -var _integrityChecker; - -function _load_integrityChecker() { - return _integrityChecker = _interopRequireDefault(__webpack_require__(208)); -} - -var _lockfile; - -function _load_lockfile() { - return _lockfile = _interopRequireDefault(__webpack_require__(19)); -} - -var _lockfile2; - -function _load_lockfile2() { - return _lockfile2 = __webpack_require__(19); -} - -var _packageFetcher; - -function _load_packageFetcher() { - return _packageFetcher = _interopRequireWildcard(__webpack_require__(210)); -} - -var _packageInstallScripts; - -function _load_packageInstallScripts() { - return _packageInstallScripts = _interopRequireDefault(__webpack_require__(557)); -} - -var _packageCompatibility; - -function _load_packageCompatibility() { - return _packageCompatibility = _interopRequireWildcard(__webpack_require__(209)); -} - -var _packageResolver; - -function _load_packageResolver() { - return _packageResolver = _interopRequireDefault(__webpack_require__(366)); -} - -var _packageLinker; - -function _load_packageLinker() { - return _packageLinker = _interopRequireDefault(__webpack_require__(211)); -} - -var _index2; - -function _load_index2() { - return _index2 = __webpack_require__(57); -} - -var _index3; - -function _load_index3() { - return _index3 = __webpack_require__(78); -} - -var _autoclean; - -function _load_autoclean() { - return _autoclean = __webpack_require__(354); -} - -var _constants; - -function _load_constants() { - return _constants = _interopRequireWildcard(__webpack_require__(8)); -} - -var _normalizePattern; - -function _load_normalizePattern() { - return _normalizePattern = __webpack_require__(37); -} - -var _fs; - -function _load_fs() { - return _fs = _interopRequireWildcard(__webpack_require__(4)); -} - -var _map; - -function _load_map() { - return _map = _interopRequireDefault(__webpack_require__(29)); -} - -var _yarnVersion; - -function _load_yarnVersion() { - return _yarnVersion = __webpack_require__(120); -} - -var _generatePnpMap; - -function _load_generatePnpMap() { - return _generatePnpMap = __webpack_require__(579); -} - -var _workspaceLayout; - -function _load_workspaceLayout() { - return _workspaceLayout = _interopRequireDefault(__webpack_require__(90)); -} - -var _resolutionMap; - -function _load_resolutionMap() { - return _resolutionMap = _interopRequireDefault(__webpack_require__(214)); -} - -var _guessName; - -function _load_guessName() { - return _guessName = _interopRequireDefault(__webpack_require__(169)); -} - -var _audit; - -function _load_audit() { - return _audit = _interopRequireDefault(__webpack_require__(353)); -} - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const deepEqual = __webpack_require__(631); - -const emoji = __webpack_require__(302); -const invariant = __webpack_require__(9); -const path = __webpack_require__(0); -const semver = __webpack_require__(22); -const uuid = __webpack_require__(119); -const ssri = __webpack_require__(65); - -const ONE_DAY = 1000 * 60 * 60 * 24; - -/** - * Try and detect the installation method for Yarn and provide a command to update it with. - */ - -function getUpdateCommand(installationMethod) { - if (installationMethod === 'tar') { - return `curl --compressed -o- -L ${(_constants || _load_constants()).YARN_INSTALLER_SH} | bash`; - } - - if (installationMethod === 'homebrew') { - return 'brew upgrade yarn'; - } - - if (installationMethod === 'deb') { - return 'sudo apt-get update && sudo apt-get install yarn'; - } - - if (installationMethod === 'rpm') { - return 'sudo yum install yarn'; - } - - if (installationMethod === 'npm') { - return 'npm install --global yarn'; - } - - if (installationMethod === 'chocolatey') { - return 'choco upgrade yarn'; - } - - if (installationMethod === 'apk') { - return 'apk update && apk add -u yarn'; - } - - if (installationMethod === 'portage') { - return 'sudo emerge --sync && sudo emerge -au sys-apps/yarn'; - } - - return null; -} - -function getUpdateInstaller(installationMethod) { - // Windows - if (installationMethod === 'msi') { - return (_constants || _load_constants()).YARN_INSTALLER_MSI; - } - - return null; -} - -function normalizeFlags(config, rawFlags) { - const flags = { - // install - har: !!rawFlags.har, - ignorePlatform: !!rawFlags.ignorePlatform, - ignoreEngines: !!rawFlags.ignoreEngines, - ignoreScripts: !!rawFlags.ignoreScripts, - ignoreOptional: !!rawFlags.ignoreOptional, - force: !!rawFlags.force, - flat: !!rawFlags.flat, - lockfile: rawFlags.lockfile !== false, - pureLockfile: !!rawFlags.pureLockfile, - updateChecksums: !!rawFlags.updateChecksums, - skipIntegrityCheck: !!rawFlags.skipIntegrityCheck, - frozenLockfile: !!rawFlags.frozenLockfile, - linkDuplicates: !!rawFlags.linkDuplicates, - checkFiles: !!rawFlags.checkFiles, - audit: !!rawFlags.audit, - - // add - peer: !!rawFlags.peer, - dev: !!rawFlags.dev, - optional: !!rawFlags.optional, - exact: !!rawFlags.exact, - tilde: !!rawFlags.tilde, - ignoreWorkspaceRootCheck: !!rawFlags.ignoreWorkspaceRootCheck, - - // outdated, update-interactive - includeWorkspaceDeps: !!rawFlags.includeWorkspaceDeps, - - // add, remove, update - workspaceRootIsCwd: rawFlags.workspaceRootIsCwd !== false - }; - - if (config.getOption('ignore-scripts')) { - flags.ignoreScripts = true; - } - - if (config.getOption('ignore-platform')) { - flags.ignorePlatform = true; - } - - if (config.getOption('ignore-engines')) { - flags.ignoreEngines = true; - } - - if (config.getOption('ignore-optional')) { - flags.ignoreOptional = true; - } - - if (config.getOption('force')) { - flags.force = true; - } - - return flags; -} - -class Install { - constructor(flags, config, reporter, lockfile) { - this.rootManifestRegistries = []; - this.rootPatternsToOrigin = (0, (_map || _load_map()).default)(); - this.lockfile = lockfile; - this.reporter = reporter; - this.config = config; - this.flags = normalizeFlags(config, flags); - this.resolutions = (0, (_map || _load_map()).default)(); // Legacy resolutions field used for flat install mode - this.resolutionMap = new (_resolutionMap || _load_resolutionMap()).default(config); // Selective resolutions for nested dependencies - this.resolver = new (_packageResolver || _load_packageResolver()).default(config, lockfile, this.resolutionMap); - this.integrityChecker = new (_integrityChecker || _load_integrityChecker()).default(config); - this.linker = new (_packageLinker || _load_packageLinker()).default(config, this.resolver); - this.scripts = new (_packageInstallScripts || _load_packageInstallScripts()).default(config, this.resolver, this.flags.force); - } - - /** - * Create a list of dependency requests from the current directories manifests. - */ - - fetchRequestFromCwd(excludePatterns = [], ignoreUnusedPatterns = false) { - var _this = this; - - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - const patterns = []; - const deps = []; - let resolutionDeps = []; - const manifest = {}; - - const ignorePatterns = []; - const usedPatterns = []; - let workspaceLayout; - - // some commands should always run in the context of the entire workspace - const cwd = _this.flags.includeWorkspaceDeps || _this.flags.workspaceRootIsCwd ? _this.config.lockfileFolder : _this.config.cwd; - - // non-workspaces are always root, otherwise check for workspace root - const cwdIsRoot = !_this.config.workspaceRootFolder || _this.config.lockfileFolder === cwd; - - // exclude package names that are in install args - const excludeNames = []; - for (var _iterator = excludePatterns, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } - - const pattern = _ref; - - if ((0, (_index3 || _load_index3()).getExoticResolver)(pattern)) { - excludeNames.push((0, (_guessName || _load_guessName()).default)(pattern)); - } else { - // extract the name - const parts = (0, (_normalizePattern || _load_normalizePattern()).normalizePattern)(pattern); - excludeNames.push(parts.name); - } - } - - const stripExcluded = function stripExcluded(manifest) { - for (var _iterator2 = excludeNames, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref2; - - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref2 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref2 = _i2.value; - } - - const exclude = _ref2; - - if (manifest.dependencies && manifest.dependencies[exclude]) { - delete manifest.dependencies[exclude]; - } - if (manifest.devDependencies && manifest.devDependencies[exclude]) { - delete manifest.devDependencies[exclude]; - } - if (manifest.optionalDependencies && manifest.optionalDependencies[exclude]) { - delete manifest.optionalDependencies[exclude]; - } - } - }; - - for (var _iterator3 = Object.keys((_index2 || _load_index2()).registries), _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { - var _ref3; - - if (_isArray3) { - if (_i3 >= _iterator3.length) break; - _ref3 = _iterator3[_i3++]; - } else { - _i3 = _iterator3.next(); - if (_i3.done) break; - _ref3 = _i3.value; - } - - const registry = _ref3; - - const filename = (_index2 || _load_index2()).registries[registry].filename; - - const loc = path.join(cwd, filename); - if (!(yield (_fs || _load_fs()).exists(loc))) { - continue; - } - - _this.rootManifestRegistries.push(registry); - - const projectManifestJson = yield _this.config.readJson(loc); - yield (0, (_index || _load_index()).default)(projectManifestJson, cwd, _this.config, cwdIsRoot); - - Object.assign(_this.resolutions, projectManifestJson.resolutions); - Object.assign(manifest, projectManifestJson); - - _this.resolutionMap.init(_this.resolutions); - for (var _iterator4 = Object.keys(_this.resolutionMap.resolutionsByPackage), _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { - var _ref4; - - if (_isArray4) { - if (_i4 >= _iterator4.length) break; - _ref4 = _iterator4[_i4++]; - } else { - _i4 = _iterator4.next(); - if (_i4.done) break; - _ref4 = _i4.value; - } - - const packageName = _ref4; - - const optional = (_objectPath || _load_objectPath()).default.has(manifest.optionalDependencies, packageName) && _this.flags.ignoreOptional; - for (var _iterator8 = _this.resolutionMap.resolutionsByPackage[packageName], _isArray8 = Array.isArray(_iterator8), _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { - var _ref9; - - if (_isArray8) { - if (_i8 >= _iterator8.length) break; - _ref9 = _iterator8[_i8++]; - } else { - _i8 = _iterator8.next(); - if (_i8.done) break; - _ref9 = _i8.value; - } - - const _ref8 = _ref9; - const pattern = _ref8.pattern; - - resolutionDeps = [...resolutionDeps, { registry, pattern, optional, hint: 'resolution' }]; - } - } - - const pushDeps = function pushDeps(depType, manifest, { hint, optional }, isUsed) { - if (ignoreUnusedPatterns && !isUsed) { - return; - } - // We only take unused dependencies into consideration to get deterministic hoisting. - // Since flat mode doesn't care about hoisting and everything is top level and specified then we can safely - // leave these out. - if (_this.flags.flat && !isUsed) { - return; - } - const depMap = manifest[depType]; - for (const name in depMap) { - if (excludeNames.indexOf(name) >= 0) { - continue; - } - - let pattern = name; - if (!_this.lockfile.getLocked(pattern)) { - // when we use --save we save the dependency to the lockfile with just the name rather than the - // version combo - pattern += '@' + depMap[name]; - } - - // normalization made sure packages are mentioned only once - if (isUsed) { - usedPatterns.push(pattern); - } else { - ignorePatterns.push(pattern); - } - - _this.rootPatternsToOrigin[pattern] = depType; - patterns.push(pattern); - deps.push({ pattern, registry, hint, optional, workspaceName: manifest.name, workspaceLoc: manifest._loc }); - } - }; - - if (cwdIsRoot) { - pushDeps('dependencies', projectManifestJson, { hint: null, optional: false }, true); - pushDeps('devDependencies', projectManifestJson, { hint: 'dev', optional: false }, !_this.config.production); - pushDeps('optionalDependencies', projectManifestJson, { hint: 'optional', optional: true }, true); - } - - if (_this.config.workspaceRootFolder) { - const workspaceLoc = cwdIsRoot ? loc : path.join(_this.config.lockfileFolder, filename); - const workspacesRoot = path.dirname(workspaceLoc); - - let workspaceManifestJson = projectManifestJson; - if (!cwdIsRoot) { - // the manifest we read before was a child workspace, so get the root - workspaceManifestJson = yield _this.config.readJson(workspaceLoc); - yield (0, (_index || _load_index()).default)(workspaceManifestJson, workspacesRoot, _this.config, true); - } - - const workspaces = yield _this.config.resolveWorkspaces(workspacesRoot, workspaceManifestJson); - workspaceLayout = new (_workspaceLayout || _load_workspaceLayout()).default(workspaces, _this.config); - - // add virtual manifest that depends on all workspaces, this way package hoisters and resolvers will work fine - const workspaceDependencies = (0, (_extends2 || _load_extends()).default)({}, workspaceManifestJson.dependencies); - for (var _iterator5 = Object.keys(workspaces), _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { - var _ref5; - - if (_isArray5) { - if (_i5 >= _iterator5.length) break; - _ref5 = _iterator5[_i5++]; - } else { - _i5 = _iterator5.next(); - if (_i5.done) break; - _ref5 = _i5.value; - } - - const workspaceName = _ref5; - - const workspaceManifest = workspaces[workspaceName].manifest; - workspaceDependencies[workspaceName] = workspaceManifest.version; - - // include dependencies from all workspaces - if (_this.flags.includeWorkspaceDeps) { - pushDeps('dependencies', workspaceManifest, { hint: null, optional: false }, true); - pushDeps('devDependencies', workspaceManifest, { hint: 'dev', optional: false }, !_this.config.production); - pushDeps('optionalDependencies', workspaceManifest, { hint: 'optional', optional: true }, true); - } - } - const virtualDependencyManifest = { - _uid: '', - name: `workspace-aggregator-${uuid.v4()}`, - version: '1.0.0', - _registry: 'npm', - _loc: workspacesRoot, - dependencies: workspaceDependencies, - devDependencies: (0, (_extends2 || _load_extends()).default)({}, workspaceManifestJson.devDependencies), - optionalDependencies: (0, (_extends2 || _load_extends()).default)({}, workspaceManifestJson.optionalDependencies), - private: workspaceManifestJson.private, - workspaces: workspaceManifestJson.workspaces - }; - workspaceLayout.virtualManifestName = virtualDependencyManifest.name; - const virtualDep = {}; - virtualDep[virtualDependencyManifest.name] = virtualDependencyManifest.version; - workspaces[virtualDependencyManifest.name] = { loc: workspacesRoot, manifest: virtualDependencyManifest }; - - // ensure dependencies that should be excluded are stripped from the correct manifest - stripExcluded(cwdIsRoot ? virtualDependencyManifest : workspaces[projectManifestJson.name].manifest); - - pushDeps('workspaces', { workspaces: virtualDep }, { hint: 'workspaces', optional: false }, true); - - const implicitWorkspaceDependencies = (0, (_extends2 || _load_extends()).default)({}, workspaceDependencies); - - for (var _iterator6 = (_constants || _load_constants()).OWNED_DEPENDENCY_TYPES, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { - var _ref6; - - if (_isArray6) { - if (_i6 >= _iterator6.length) break; - _ref6 = _iterator6[_i6++]; - } else { - _i6 = _iterator6.next(); - if (_i6.done) break; - _ref6 = _i6.value; - } - - const type = _ref6; - - for (var _iterator7 = Object.keys(projectManifestJson[type] || {}), _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { - var _ref7; - - if (_isArray7) { - if (_i7 >= _iterator7.length) break; - _ref7 = _iterator7[_i7++]; - } else { - _i7 = _iterator7.next(); - if (_i7.done) break; - _ref7 = _i7.value; - } - - const dependencyName = _ref7; - - delete implicitWorkspaceDependencies[dependencyName]; - } - } - - pushDeps('dependencies', { dependencies: implicitWorkspaceDependencies }, { hint: 'workspaces', optional: false }, true); - } - - break; - } - - // inherit root flat flag - if (manifest.flat) { - _this.flags.flat = true; - } - - return { - requests: [...resolutionDeps, ...deps], - patterns, - manifest, - usedPatterns, - ignorePatterns, - workspaceLayout - }; - })(); - } - - /** - * TODO description - */ - - prepareRequests(requests) { - return requests; - } - - preparePatterns(patterns) { - return patterns; - } - preparePatternsForLinking(patterns, cwdManifest, cwdIsRoot) { - return patterns; - } - - prepareManifests() { - var _this2 = this; - - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - const manifests = yield _this2.config.getRootManifests(); - return manifests; - })(); - } - - bailout(patterns, workspaceLayout) { - var _this3 = this; - - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - // We don't want to skip the audit - it could yield important errors - if (_this3.flags.audit) { - return false; - } - // PNP is so fast that the integrity check isn't pertinent - if (_this3.config.plugnplayEnabled) { - return false; - } - if (_this3.flags.skipIntegrityCheck || _this3.flags.force) { - return false; - } - const lockfileCache = _this3.lockfile.cache; - if (!lockfileCache) { - return false; - } - const lockfileClean = _this3.lockfile.parseResultType === 'success'; - const match = yield _this3.integrityChecker.check(patterns, lockfileCache, _this3.flags, workspaceLayout); - if (_this3.flags.frozenLockfile && (!lockfileClean || match.missingPatterns.length > 0)) { - throw new (_errors || _load_errors()).MessageError(_this3.reporter.lang('frozenLockfileError')); - } - - const haveLockfile = yield (_fs || _load_fs()).exists(path.join(_this3.config.lockfileFolder, (_constants || _load_constants()).LOCKFILE_FILENAME)); - - const lockfileIntegrityPresent = !_this3.lockfile.hasEntriesExistWithoutIntegrity(); - const integrityBailout = lockfileIntegrityPresent || !_this3.config.autoAddIntegrity; - - if (match.integrityMatches && haveLockfile && lockfileClean && integrityBailout) { - _this3.reporter.success(_this3.reporter.lang('upToDate')); - return true; - } - - if (match.integrityFileMissing && haveLockfile) { - // Integrity file missing, force script installations - _this3.scripts.setForce(true); - return false; - } - - if (match.hardRefreshRequired) { - // e.g. node version doesn't match, force script installations - _this3.scripts.setForce(true); - return false; - } - - if (!patterns.length && !match.integrityFileMissing) { - _this3.reporter.success(_this3.reporter.lang('nothingToInstall')); - yield _this3.createEmptyManifestFolders(); - yield _this3.saveLockfileAndIntegrity(patterns, workspaceLayout); - return true; - } - - return false; - })(); - } - - /** - * Produce empty folders for all used root manifests. - */ - - createEmptyManifestFolders() { - var _this4 = this; - - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - if (_this4.config.modulesFolder) { - // already created - return; - } - - for (var _iterator9 = _this4.rootManifestRegistries, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { - var _ref10; - - if (_isArray9) { - if (_i9 >= _iterator9.length) break; - _ref10 = _iterator9[_i9++]; - } else { - _i9 = _iterator9.next(); - if (_i9.done) break; - _ref10 = _i9.value; - } - - const registryName = _ref10; - const folder = _this4.config.registries[registryName].folder; - - yield (_fs || _load_fs()).mkdirp(path.join(_this4.config.lockfileFolder, folder)); - } - })(); - } - - /** - * TODO description - */ - - markIgnored(patterns) { - for (var _iterator10 = patterns, _isArray10 = Array.isArray(_iterator10), _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) { - var _ref11; - - if (_isArray10) { - if (_i10 >= _iterator10.length) break; - _ref11 = _iterator10[_i10++]; - } else { - _i10 = _iterator10.next(); - if (_i10.done) break; - _ref11 = _i10.value; - } - - const pattern = _ref11; - - const manifest = this.resolver.getStrictResolvedPattern(pattern); - const ref = manifest._reference; - invariant(ref, 'expected package reference'); - - // just mark the package as ignored. if the package is used by a required package, the hoister - // will take care of that. - ref.ignore = true; - } - } - - /** - * helper method that gets only recent manifests - * used by global.ls command - */ - getFlattenedDeps() { - var _this5 = this; - - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - var _ref12 = yield _this5.fetchRequestFromCwd(); - - const depRequests = _ref12.requests, - rawPatterns = _ref12.patterns; - - - yield _this5.resolver.init(depRequests, {}); - - const manifests = yield (_packageFetcher || _load_packageFetcher()).fetch(_this5.resolver.getManifests(), _this5.config); - _this5.resolver.updateManifests(manifests); - - return _this5.flatten(rawPatterns); - })(); - } - - /** - * TODO description - */ - - init() { - var _this6 = this; - - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - _this6.checkUpdate(); - - // warn if we have a shrinkwrap - if (yield (_fs || _load_fs()).exists(path.join(_this6.config.lockfileFolder, (_constants || _load_constants()).NPM_SHRINKWRAP_FILENAME))) { - _this6.reporter.warn(_this6.reporter.lang('shrinkwrapWarning')); - } - - // warn if we have an npm lockfile - if (yield (_fs || _load_fs()).exists(path.join(_this6.config.lockfileFolder, (_constants || _load_constants()).NPM_LOCK_FILENAME))) { - _this6.reporter.warn(_this6.reporter.lang('npmLockfileWarning')); - } - - if (_this6.config.plugnplayEnabled) { - _this6.reporter.info(_this6.reporter.lang('plugnplaySuggestV2L1')); - _this6.reporter.info(_this6.reporter.lang('plugnplaySuggestV2L2')); - } - - let flattenedTopLevelPatterns = []; - const steps = []; - - var _ref13 = yield _this6.fetchRequestFromCwd(); - - const depRequests = _ref13.requests, - rawPatterns = _ref13.patterns, - ignorePatterns = _ref13.ignorePatterns, - workspaceLayout = _ref13.workspaceLayout, - manifest = _ref13.manifest; - - let topLevelPatterns = []; - - const artifacts = yield _this6.integrityChecker.getArtifacts(); - if (artifacts) { - _this6.linker.setArtifacts(artifacts); - _this6.scripts.setArtifacts(artifacts); - } - - if ((_packageCompatibility || _load_packageCompatibility()).shouldCheck(manifest, _this6.flags)) { - steps.push((() => { - var _ref14 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (curr, total) { - _this6.reporter.step(curr, total, _this6.reporter.lang('checkingManifest'), emoji.get('mag')); - yield _this6.checkCompatibility(); - }); - - return function (_x, _x2) { - return _ref14.apply(this, arguments); - }; - })()); - } - - const audit = new (_audit || _load_audit()).default(_this6.config, _this6.reporter, { groups: (_constants || _load_constants()).OWNED_DEPENDENCY_TYPES }); - let auditFoundProblems = false; - - steps.push(function (curr, total) { - return (0, (_hooks || _load_hooks()).callThroughHook)('resolveStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - _this6.reporter.step(curr, total, _this6.reporter.lang('resolvingPackages'), emoji.get('mag')); - yield _this6.resolver.init(_this6.prepareRequests(depRequests), { - isFlat: _this6.flags.flat, - isFrozen: _this6.flags.frozenLockfile, - workspaceLayout - }); - topLevelPatterns = _this6.preparePatterns(rawPatterns); - flattenedTopLevelPatterns = yield _this6.flatten(topLevelPatterns); - return { bailout: !_this6.flags.audit && (yield _this6.bailout(topLevelPatterns, workspaceLayout)) }; - })); - }); - - if (_this6.flags.audit) { - steps.push(function (curr, total) { - return (0, (_hooks || _load_hooks()).callThroughHook)('auditStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - _this6.reporter.step(curr, total, _this6.reporter.lang('auditRunning'), emoji.get('mag')); - if (_this6.flags.offline) { - _this6.reporter.warn(_this6.reporter.lang('auditOffline')); - return { bailout: false }; - } - const preparedManifests = yield _this6.prepareManifests(); - // $FlowFixMe - Flow considers `m` in the map operation to be "mixed", so does not recognize `m.object` - const mergedManifest = Object.assign({}, ...Object.values(preparedManifests).map(function (m) { - return m.object; - })); - const auditVulnerabilityCounts = yield audit.performAudit(mergedManifest, _this6.lockfile, _this6.resolver, _this6.linker, topLevelPatterns); - auditFoundProblems = auditVulnerabilityCounts.info || auditVulnerabilityCounts.low || auditVulnerabilityCounts.moderate || auditVulnerabilityCounts.high || auditVulnerabilityCounts.critical; - return { bailout: yield _this6.bailout(topLevelPatterns, workspaceLayout) }; - })); - }); - } - - steps.push(function (curr, total) { - return (0, (_hooks || _load_hooks()).callThroughHook)('fetchStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - _this6.markIgnored(ignorePatterns); - _this6.reporter.step(curr, total, _this6.reporter.lang('fetchingPackages'), emoji.get('truck')); - const manifests = yield (_packageFetcher || _load_packageFetcher()).fetch(_this6.resolver.getManifests(), _this6.config); - _this6.resolver.updateManifests(manifests); - yield (_packageCompatibility || _load_packageCompatibility()).check(_this6.resolver.getManifests(), _this6.config, _this6.flags.ignoreEngines); - })); - }); - - steps.push(function (curr, total) { - return (0, (_hooks || _load_hooks()).callThroughHook)('linkStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - // remove integrity hash to make this operation atomic - yield _this6.integrityChecker.removeIntegrityFile(); - _this6.reporter.step(curr, total, _this6.reporter.lang('linkingDependencies'), emoji.get('link')); - flattenedTopLevelPatterns = _this6.preparePatternsForLinking(flattenedTopLevelPatterns, manifest, _this6.config.lockfileFolder === _this6.config.cwd); - yield _this6.linker.init(flattenedTopLevelPatterns, workspaceLayout, { - linkDuplicates: _this6.flags.linkDuplicates, - ignoreOptional: _this6.flags.ignoreOptional - }); - })); - }); - - if (_this6.config.plugnplayEnabled) { - steps.push(function (curr, total) { - return (0, (_hooks || _load_hooks()).callThroughHook)('pnpStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - const pnpPath = `${_this6.config.lockfileFolder}/${(_constants || _load_constants()).PNP_FILENAME}`; - - const code = yield (0, (_generatePnpMap || _load_generatePnpMap()).generatePnpMap)(_this6.config, flattenedTopLevelPatterns, { - resolver: _this6.resolver, - reporter: _this6.reporter, - targetPath: pnpPath, - workspaceLayout - }); - - try { - const file = yield (_fs || _load_fs()).readFile(pnpPath); - if (file === code) { - return; - } - } catch (error) {} - - yield (_fs || _load_fs()).writeFile(pnpPath, code); - yield (_fs || _load_fs()).chmod(pnpPath, 0o755); - })); - }); - } - - steps.push(function (curr, total) { - return (0, (_hooks || _load_hooks()).callThroughHook)('buildStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - _this6.reporter.step(curr, total, _this6.flags.force ? _this6.reporter.lang('rebuildingPackages') : _this6.reporter.lang('buildingFreshPackages'), emoji.get('hammer')); - - if (_this6.config.ignoreScripts) { - _this6.reporter.warn(_this6.reporter.lang('ignoredScripts')); - } else { - yield _this6.scripts.init(flattenedTopLevelPatterns); - } - })); - }); - - if (_this6.flags.har) { - steps.push((() => { - var _ref21 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (curr, total) { - const formattedDate = new Date().toISOString().replace(/:/g, '-'); - const filename = `yarn-install_${formattedDate}.har`; - _this6.reporter.step(curr, total, _this6.reporter.lang('savingHar', filename), emoji.get('black_circle_for_record')); - yield _this6.config.requestManager.saveHar(filename); - }); - - return function (_x3, _x4) { - return _ref21.apply(this, arguments); - }; - })()); - } - - if (yield _this6.shouldClean()) { - steps.push((() => { - var _ref22 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (curr, total) { - _this6.reporter.step(curr, total, _this6.reporter.lang('cleaningModules'), emoji.get('recycle')); - yield (0, (_autoclean || _load_autoclean()).clean)(_this6.config, _this6.reporter); - }); - - return function (_x5, _x6) { - return _ref22.apply(this, arguments); - }; - })()); - } - - let currentStep = 0; - for (var _iterator11 = steps, _isArray11 = Array.isArray(_iterator11), _i11 = 0, _iterator11 = _isArray11 ? _iterator11 : _iterator11[Symbol.iterator]();;) { - var _ref23; - - if (_isArray11) { - if (_i11 >= _iterator11.length) break; - _ref23 = _iterator11[_i11++]; - } else { - _i11 = _iterator11.next(); - if (_i11.done) break; - _ref23 = _i11.value; - } - - const step = _ref23; - - const stepResult = yield step(++currentStep, steps.length); - if (stepResult && stepResult.bailout) { - if (_this6.flags.audit) { - audit.summary(); - } - if (auditFoundProblems) { - _this6.reporter.warn(_this6.reporter.lang('auditRunAuditForDetails')); - } - _this6.maybeOutputUpdate(); - return flattenedTopLevelPatterns; - } - } - - // fin! - if (_this6.flags.audit) { - audit.summary(); - } - if (auditFoundProblems) { - _this6.reporter.warn(_this6.reporter.lang('auditRunAuditForDetails')); - } - yield _this6.saveLockfileAndIntegrity(topLevelPatterns, workspaceLayout); - yield _this6.persistChanges(); - _this6.maybeOutputUpdate(); - _this6.config.requestManager.clearCache(); - return flattenedTopLevelPatterns; - })(); - } - - checkCompatibility() { - var _this7 = this; - - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - var _ref24 = yield _this7.fetchRequestFromCwd(); - - const manifest = _ref24.manifest; - - yield (_packageCompatibility || _load_packageCompatibility()).checkOne(manifest, _this7.config, _this7.flags.ignoreEngines); - })(); - } - - persistChanges() { - var _this8 = this; - - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - // get all the different registry manifests in this folder - const manifests = yield _this8.config.getRootManifests(); - - if (yield _this8.applyChanges(manifests)) { - yield _this8.config.saveRootManifests(manifests); - } - })(); - } - - applyChanges(manifests) { - let hasChanged = false; - - if (this.config.plugnplayPersist) { - const object = manifests.npm.object; - - - if (typeof object.installConfig !== 'object') { - object.installConfig = {}; - } - - if (this.config.plugnplayEnabled && object.installConfig.pnp !== true) { - object.installConfig.pnp = true; - hasChanged = true; - } else if (!this.config.plugnplayEnabled && typeof object.installConfig.pnp !== 'undefined') { - delete object.installConfig.pnp; - hasChanged = true; - } - - if (Object.keys(object.installConfig).length === 0) { - delete object.installConfig; - } - } - - return Promise.resolve(hasChanged); - } - - /** - * Check if we should run the cleaning step. - */ - - shouldClean() { - return (_fs || _load_fs()).exists(path.join(this.config.lockfileFolder, (_constants || _load_constants()).CLEAN_FILENAME)); - } - - /** - * TODO - */ - - flatten(patterns) { - var _this9 = this; - - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - if (!_this9.flags.flat) { - return patterns; - } - - const flattenedPatterns = []; - - for (var _iterator12 = _this9.resolver.getAllDependencyNamesByLevelOrder(patterns), _isArray12 = Array.isArray(_iterator12), _i12 = 0, _iterator12 = _isArray12 ? _iterator12 : _iterator12[Symbol.iterator]();;) { - var _ref25; - - if (_isArray12) { - if (_i12 >= _iterator12.length) break; - _ref25 = _iterator12[_i12++]; - } else { - _i12 = _iterator12.next(); - if (_i12.done) break; - _ref25 = _i12.value; - } - - const name = _ref25; - - const infos = _this9.resolver.getAllInfoForPackageName(name).filter(function (manifest) { - const ref = manifest._reference; - invariant(ref, 'expected package reference'); - return !ref.ignore; - }); - - if (infos.length === 0) { - continue; - } - - if (infos.length === 1) { - // single version of this package - // take out a single pattern as multiple patterns may have resolved to this package - flattenedPatterns.push(_this9.resolver.patternsByPackage[name][0]); - continue; - } - - const options = infos.map(function (info) { - const ref = info._reference; - invariant(ref, 'expected reference'); - return { - // TODO `and is required by {PARENT}`, - name: _this9.reporter.lang('manualVersionResolutionOption', ref.patterns.join(', '), info.version), - - value: info.version - }; - }); - const versions = infos.map(function (info) { - return info.version; - }); - let version; - - const resolutionVersion = _this9.resolutions[name]; - if (resolutionVersion && versions.indexOf(resolutionVersion) >= 0) { - // use json `resolution` version - version = resolutionVersion; - } else { - version = yield _this9.reporter.select(_this9.reporter.lang('manualVersionResolution', name), _this9.reporter.lang('answer'), options); - _this9.resolutions[name] = version; - } - - flattenedPatterns.push(_this9.resolver.collapseAllVersionsOfPackage(name, version)); - } - - // save resolutions to their appropriate root manifest - if (Object.keys(_this9.resolutions).length) { - const manifests = yield _this9.config.getRootManifests(); - - for (const name in _this9.resolutions) { - const version = _this9.resolutions[name]; - - const patterns = _this9.resolver.patternsByPackage[name]; - if (!patterns) { - continue; - } - - let manifest; - for (var _iterator13 = patterns, _isArray13 = Array.isArray(_iterator13), _i13 = 0, _iterator13 = _isArray13 ? _iterator13 : _iterator13[Symbol.iterator]();;) { - var _ref26; - - if (_isArray13) { - if (_i13 >= _iterator13.length) break; - _ref26 = _iterator13[_i13++]; - } else { - _i13 = _iterator13.next(); - if (_i13.done) break; - _ref26 = _i13.value; - } - - const pattern = _ref26; - - manifest = _this9.resolver.getResolvedPattern(pattern); - if (manifest) { - break; - } - } - invariant(manifest, 'expected manifest'); - - const ref = manifest._reference; - invariant(ref, 'expected reference'); - - const object = manifests[ref.registry].object; - object.resolutions = object.resolutions || {}; - object.resolutions[name] = version; - } - - yield _this9.config.saveRootManifests(manifests); - } - - return flattenedPatterns; - })(); - } - - /** - * Remove offline tarballs that are no longer required - */ - - pruneOfflineMirror(lockfile) { - var _this10 = this; - - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - const mirror = _this10.config.getOfflineMirrorPath(); - if (!mirror) { - return; - } - - const requiredTarballs = new Set(); - for (const dependency in lockfile) { - const resolved = lockfile[dependency].resolved; - if (resolved) { - const basename = path.basename(resolved.split('#')[0]); - if (dependency[0] === '@' && basename[0] !== '@') { - requiredTarballs.add(`${dependency.split('/')[0]}-${basename}`); - } - requiredTarballs.add(basename); - } - } - - const mirrorFiles = yield (_fs || _load_fs()).walk(mirror); - for (var _iterator14 = mirrorFiles, _isArray14 = Array.isArray(_iterator14), _i14 = 0, _iterator14 = _isArray14 ? _iterator14 : _iterator14[Symbol.iterator]();;) { - var _ref27; - - if (_isArray14) { - if (_i14 >= _iterator14.length) break; - _ref27 = _iterator14[_i14++]; - } else { - _i14 = _iterator14.next(); - if (_i14.done) break; - _ref27 = _i14.value; - } - - const file = _ref27; - - const isTarball = path.extname(file.basename) === '.tgz'; - // if using experimental-pack-script-packages-in-mirror flag, don't unlink prebuilt packages - const hasPrebuiltPackage = file.relative.startsWith('prebuilt/'); - if (isTarball && !hasPrebuiltPackage && !requiredTarballs.has(file.basename)) { - yield (_fs || _load_fs()).unlink(file.absolute); - } - } - })(); - } - - /** - * Save updated integrity and lockfiles. - */ - - saveLockfileAndIntegrity(patterns, workspaceLayout) { - var _this11 = this; - - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - const resolvedPatterns = {}; - Object.keys(_this11.resolver.patterns).forEach(function (pattern) { - if (!workspaceLayout || !workspaceLayout.getManifestByPattern(pattern)) { - resolvedPatterns[pattern] = _this11.resolver.patterns[pattern]; - } - }); - - // TODO this code is duplicated in a few places, need a common way to filter out workspace patterns from lockfile - patterns = patterns.filter(function (p) { - return !workspaceLayout || !workspaceLayout.getManifestByPattern(p); - }); - - const lockfileBasedOnResolver = _this11.lockfile.getLockfile(resolvedPatterns); - - if (_this11.config.pruneOfflineMirror) { - yield _this11.pruneOfflineMirror(lockfileBasedOnResolver); - } - - // write integrity hash - if (!_this11.config.plugnplayEnabled) { - yield _this11.integrityChecker.save(patterns, lockfileBasedOnResolver, _this11.flags, workspaceLayout, _this11.scripts.getArtifacts()); - } - - // --no-lockfile or --pure-lockfile or --frozen-lockfile - if (_this11.flags.lockfile === false || _this11.flags.pureLockfile || _this11.flags.frozenLockfile) { - return; - } - - const lockFileHasAllPatterns = patterns.every(function (p) { - return _this11.lockfile.getLocked(p); - }); - const lockfilePatternsMatch = Object.keys(_this11.lockfile.cache || {}).every(function (p) { - return lockfileBasedOnResolver[p]; - }); - const resolverPatternsAreSameAsInLockfile = Object.keys(lockfileBasedOnResolver).every(function (pattern) { - const manifest = _this11.lockfile.getLocked(pattern); - return manifest && manifest.resolved === lockfileBasedOnResolver[pattern].resolved && deepEqual(manifest.prebuiltVariants, lockfileBasedOnResolver[pattern].prebuiltVariants); - }); - const integrityPatternsAreSameAsInLockfile = Object.keys(lockfileBasedOnResolver).every(function (pattern) { - const existingIntegrityInfo = lockfileBasedOnResolver[pattern].integrity; - if (!existingIntegrityInfo) { - // if this entry does not have an integrity, no need to re-write the lockfile because of it - return true; - } - const manifest = _this11.lockfile.getLocked(pattern); - if (manifest && manifest.integrity) { - const manifestIntegrity = ssri.stringify(manifest.integrity); - return manifestIntegrity === existingIntegrityInfo; - } - return false; - }); - - // remove command is followed by install with force, lockfile will be rewritten in any case then - if (!_this11.flags.force && _this11.lockfile.parseResultType === 'success' && lockFileHasAllPatterns && lockfilePatternsMatch && resolverPatternsAreSameAsInLockfile && integrityPatternsAreSameAsInLockfile && patterns.length) { - return; - } - - // build lockfile location - const loc = path.join(_this11.config.lockfileFolder, (_constants || _load_constants()).LOCKFILE_FILENAME); - - // write lockfile - const lockSource = (0, (_lockfile2 || _load_lockfile2()).stringify)(lockfileBasedOnResolver, false, _this11.config.enableLockfileVersions); - yield (_fs || _load_fs()).writeFilePreservingEol(loc, lockSource); - - _this11._logSuccessSaveLockfile(); - })(); - } - - _logSuccessSaveLockfile() { - this.reporter.success(this.reporter.lang('savedLockfile')); - } - - /** - * Load the dependency graph of the current install. Only does package resolving and wont write to the cwd. - */ - hydrate(ignoreUnusedPatterns) { - var _this12 = this; - - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - const request = yield _this12.fetchRequestFromCwd([], ignoreUnusedPatterns); - const depRequests = request.requests, - rawPatterns = request.patterns, - ignorePatterns = request.ignorePatterns, - workspaceLayout = request.workspaceLayout; - - - yield _this12.resolver.init(depRequests, { - isFlat: _this12.flags.flat, - isFrozen: _this12.flags.frozenLockfile, - workspaceLayout - }); - yield _this12.flatten(rawPatterns); - _this12.markIgnored(ignorePatterns); - - // fetch packages, should hit cache most of the time - const manifests = yield (_packageFetcher || _load_packageFetcher()).fetch(_this12.resolver.getManifests(), _this12.config); - _this12.resolver.updateManifests(manifests); - yield (_packageCompatibility || _load_packageCompatibility()).check(_this12.resolver.getManifests(), _this12.config, _this12.flags.ignoreEngines); - - // expand minimal manifests - for (var _iterator15 = _this12.resolver.getManifests(), _isArray15 = Array.isArray(_iterator15), _i15 = 0, _iterator15 = _isArray15 ? _iterator15 : _iterator15[Symbol.iterator]();;) { - var _ref28; - - if (_isArray15) { - if (_i15 >= _iterator15.length) break; - _ref28 = _iterator15[_i15++]; - } else { - _i15 = _iterator15.next(); - if (_i15.done) break; - _ref28 = _i15.value; - } - - const manifest = _ref28; - - const ref = manifest._reference; - invariant(ref, 'expected reference'); - const type = ref.remote.type; - // link specifier won't ever hit cache - - let loc = ''; - if (type === 'link') { - continue; - } else if (type === 'workspace') { - if (!ref.remote.reference) { - continue; - } - loc = ref.remote.reference; - } else { - loc = _this12.config.generateModuleCachePath(ref); - } - const newPkg = yield _this12.config.readManifest(loc); - yield _this12.resolver.updateManifest(ref, newPkg); - } - - return request; - })(); - } - - /** - * Check for updates every day and output a nag message if there's a newer version. - */ - - checkUpdate() { - if (this.config.nonInteractive) { - // don't show upgrade dialog on CI or non-TTY terminals - return; - } - - // don't check if disabled - if (this.config.getOption('disable-self-update-check')) { - return; - } - - // only check for updates once a day - const lastUpdateCheck = Number(this.config.getOption('lastUpdateCheck')) || 0; - if (lastUpdateCheck && Date.now() - lastUpdateCheck < ONE_DAY) { - return; - } - - // don't bug for updates on tagged releases - if ((_yarnVersion || _load_yarnVersion()).version.indexOf('-') >= 0) { - return; - } - - this._checkUpdate().catch(() => { - // swallow errors - }); - } - - _checkUpdate() { - var _this13 = this; - - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - let latestVersion = yield _this13.config.requestManager.request({ - url: (_constants || _load_constants()).SELF_UPDATE_VERSION_URL - }); - invariant(typeof latestVersion === 'string', 'expected string'); - latestVersion = latestVersion.trim(); - if (!semver.valid(latestVersion)) { - return; - } - - // ensure we only check for updates periodically - _this13.config.registries.yarn.saveHomeConfig({ - lastUpdateCheck: Date.now() - }); - - if (semver.gt(latestVersion, (_yarnVersion || _load_yarnVersion()).version)) { - const installationMethod = yield (0, (_yarnVersion || _load_yarnVersion()).getInstallationMethod)(); - _this13.maybeOutputUpdate = function () { - _this13.reporter.warn(_this13.reporter.lang('yarnOutdated', latestVersion, (_yarnVersion || _load_yarnVersion()).version)); - - const command = getUpdateCommand(installationMethod); - if (command) { - _this13.reporter.info(_this13.reporter.lang('yarnOutdatedCommand')); - _this13.reporter.command(command); - } else { - const installer = getUpdateInstaller(installationMethod); - if (installer) { - _this13.reporter.info(_this13.reporter.lang('yarnOutdatedInstaller', installer)); - } - } - }; - } - })(); - } - - /** - * Method to override with a possible upgrade message. - */ - - maybeOutputUpdate() {} -} - -exports.Install = Install; -function hasWrapper(commander, args) { - return true; -} - -function setFlags(commander) { - commander.description('Yarn install is used to install all dependencies for a project.'); - commander.usage('install [flags]'); - commander.option('-A, --audit', 'Run vulnerability audit on installed packages'); - commander.option('-g, --global', 'DEPRECATED'); - commander.option('-S, --save', 'DEPRECATED - save package to your `dependencies`'); - commander.option('-D, --save-dev', 'DEPRECATED - save package to your `devDependencies`'); - commander.option('-P, --save-peer', 'DEPRECATED - save package to your `peerDependencies`'); - commander.option('-O, --save-optional', 'DEPRECATED - save package to your `optionalDependencies`'); - commander.option('-E, --save-exact', 'DEPRECATED'); - commander.option('-T, --save-tilde', 'DEPRECATED'); -} - -/***/ }), -/* 35 */ -/***/ (function(module, exports, __webpack_require__) { - -var isObject = __webpack_require__(52); -module.exports = function (it) { - if (!isObject(it)) throw TypeError(it + ' is not an object!'); - return it; -}; - - -/***/ }), -/* 36 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return SubjectSubscriber; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Subject; }); -/* unused harmony export AnonymousSubject */ -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_tslib__ = __webpack_require__(1); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__Observable__ = __webpack_require__(12); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__Subscriber__ = __webpack_require__(7); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__Subscription__ = __webpack_require__(25); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__ = __webpack_require__(189); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__SubjectSubscription__ = __webpack_require__(422); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__internal_symbol_rxSubscriber__ = __webpack_require__(321); -/** PURE_IMPORTS_START tslib,_Observable,_Subscriber,_Subscription,_util_ObjectUnsubscribedError,_SubjectSubscription,_internal_symbol_rxSubscriber PURE_IMPORTS_END */ - - - - - - - -var SubjectSubscriber = /*@__PURE__*/ (function (_super) { - __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](SubjectSubscriber, _super); - function SubjectSubscriber(destination) { - var _this = _super.call(this, destination) || this; - _this.destination = destination; - return _this; - } - return SubjectSubscriber; -}(__WEBPACK_IMPORTED_MODULE_2__Subscriber__["a" /* Subscriber */])); - -var Subject = /*@__PURE__*/ (function (_super) { - __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](Subject, _super); - function Subject() { - var _this = _super.call(this) || this; - _this.observers = []; - _this.closed = false; - _this.isStopped = false; - _this.hasError = false; - _this.thrownError = null; - return _this; - } - Subject.prototype[__WEBPACK_IMPORTED_MODULE_6__internal_symbol_rxSubscriber__["a" /* rxSubscriber */]] = function () { - return new SubjectSubscriber(this); - }; - Subject.prototype.lift = function (operator) { - var subject = new AnonymousSubject(this, this); - subject.operator = operator; - return subject; - }; - Subject.prototype.next = function (value) { - if (this.closed) { - throw new __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__["a" /* ObjectUnsubscribedError */](); - } - if (!this.isStopped) { - var observers = this.observers; - var len = observers.length; - var copy = observers.slice(); - for (var i = 0; i < len; i++) { - copy[i].next(value); - } - } - }; - Subject.prototype.error = function (err) { - if (this.closed) { - throw new __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__["a" /* ObjectUnsubscribedError */](); - } - this.hasError = true; - this.thrownError = err; - this.isStopped = true; - var observers = this.observers; - var len = observers.length; - var copy = observers.slice(); - for (var i = 0; i < len; i++) { - copy[i].error(err); - } - this.observers.length = 0; - }; - Subject.prototype.complete = function () { - if (this.closed) { - throw new __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__["a" /* ObjectUnsubscribedError */](); - } - this.isStopped = true; - var observers = this.observers; - var len = observers.length; - var copy = observers.slice(); - for (var i = 0; i < len; i++) { - copy[i].complete(); - } - this.observers.length = 0; - }; - Subject.prototype.unsubscribe = function () { - this.isStopped = true; - this.closed = true; - this.observers = null; - }; - Subject.prototype._trySubscribe = function (subscriber) { - if (this.closed) { - throw new __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__["a" /* ObjectUnsubscribedError */](); - } - else { - return _super.prototype._trySubscribe.call(this, subscriber); - } - }; - Subject.prototype._subscribe = function (subscriber) { - if (this.closed) { - throw new __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__["a" /* ObjectUnsubscribedError */](); - } - else if (this.hasError) { - subscriber.error(this.thrownError); - return __WEBPACK_IMPORTED_MODULE_3__Subscription__["a" /* Subscription */].EMPTY; - } - else if (this.isStopped) { - subscriber.complete(); - return __WEBPACK_IMPORTED_MODULE_3__Subscription__["a" /* Subscription */].EMPTY; - } - else { - this.observers.push(subscriber); - return new __WEBPACK_IMPORTED_MODULE_5__SubjectSubscription__["a" /* SubjectSubscription */](this, subscriber); - } - }; - Subject.prototype.asObservable = function () { - var observable = new __WEBPACK_IMPORTED_MODULE_1__Observable__["a" /* Observable */](); - observable.source = this; - return observable; - }; - Subject.create = function (destination, source) { - return new AnonymousSubject(destination, source); - }; - return Subject; -}(__WEBPACK_IMPORTED_MODULE_1__Observable__["a" /* Observable */])); - -var AnonymousSubject = /*@__PURE__*/ (function (_super) { - __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](AnonymousSubject, _super); - function AnonymousSubject(destination, source) { - var _this = _super.call(this) || this; - _this.destination = destination; - _this.source = source; - return _this; - } - AnonymousSubject.prototype.next = function (value) { - var destination = this.destination; - if (destination && destination.next) { - destination.next(value); - } - }; - AnonymousSubject.prototype.error = function (err) { - var destination = this.destination; - if (destination && destination.error) { - this.destination.error(err); - } - }; - AnonymousSubject.prototype.complete = function () { - var destination = this.destination; - if (destination && destination.complete) { - this.destination.complete(); - } - }; - AnonymousSubject.prototype._subscribe = function (subscriber) { - var source = this.source; - if (source) { - return this.source.subscribe(subscriber); - } - else { - return __WEBPACK_IMPORTED_MODULE_3__Subscription__["a" /* Subscription */].EMPTY; - } - }; - return AnonymousSubject; -}(Subject)); - -//# sourceMappingURL=Subject.js.map - - -/***/ }), -/* 37 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.normalizePattern = normalizePattern; - -/** - * Explode and normalize a pattern into its name and range. - */ - -function normalizePattern(pattern) { - let hasVersion = false; - let range = 'latest'; - let name = pattern; - - // if we're a scope then remove the @ and add it back later - let isScoped = false; - if (name[0] === '@') { - isScoped = true; - name = name.slice(1); - } - - // take first part as the name - const parts = name.split('@'); - if (parts.length > 1) { - name = parts.shift(); - range = parts.join('@'); - - if (range) { - hasVersion = true; - } else { - range = '*'; - } - } - - // add back @ scope suffix - if (isScoped) { - name = `@${name}`; - } - - return { name, range, hasVersion }; -} - -/***/ }), -/* 38 */ -/***/ (function(module, exports, __webpack_require__) { - -/* WEBPACK VAR INJECTION */(function(module) {var __WEBPACK_AMD_DEFINE_RESULT__;/** - * @license - * Lodash - * Copyright JS Foundation and other contributors - * Released under MIT license - * Based on Underscore.js 1.8.3 - * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - */ -;(function() { - - /** Used as a safe reference for `undefined` in pre-ES5 environments. */ - var undefined; - - /** Used as the semantic version number. */ - var VERSION = '4.17.10'; - - /** Used as the size to enable large array optimizations. */ - var LARGE_ARRAY_SIZE = 200; - - /** Error message constants. */ - var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.', - FUNC_ERROR_TEXT = 'Expected a function'; - - /** Used to stand-in for `undefined` hash values. */ - var HASH_UNDEFINED = '__lodash_hash_undefined__'; - - /** Used as the maximum memoize cache size. */ - var MAX_MEMOIZE_SIZE = 500; - - /** Used as the internal argument placeholder. */ - var PLACEHOLDER = '__lodash_placeholder__'; - - /** Used to compose bitmasks for cloning. */ - var CLONE_DEEP_FLAG = 1, - CLONE_FLAT_FLAG = 2, - CLONE_SYMBOLS_FLAG = 4; - - /** Used to compose bitmasks for value comparisons. */ - var COMPARE_PARTIAL_FLAG = 1, - COMPARE_UNORDERED_FLAG = 2; - - /** Used to compose bitmasks for function metadata. */ - var WRAP_BIND_FLAG = 1, - WRAP_BIND_KEY_FLAG = 2, - WRAP_CURRY_BOUND_FLAG = 4, - WRAP_CURRY_FLAG = 8, - WRAP_CURRY_RIGHT_FLAG = 16, - WRAP_PARTIAL_FLAG = 32, - WRAP_PARTIAL_RIGHT_FLAG = 64, - WRAP_ARY_FLAG = 128, - WRAP_REARG_FLAG = 256, - WRAP_FLIP_FLAG = 512; - - /** Used as default options for `_.truncate`. */ - var DEFAULT_TRUNC_LENGTH = 30, - DEFAULT_TRUNC_OMISSION = '...'; - - /** Used to detect hot functions by number of calls within a span of milliseconds. */ - var HOT_COUNT = 800, - HOT_SPAN = 16; - - /** Used to indicate the type of lazy iteratees. */ - var LAZY_FILTER_FLAG = 1, - LAZY_MAP_FLAG = 2, - LAZY_WHILE_FLAG = 3; - - /** Used as references for various `Number` constants. */ - var INFINITY = 1 / 0, - MAX_SAFE_INTEGER = 9007199254740991, - MAX_INTEGER = 1.7976931348623157e+308, - NAN = 0 / 0; - - /** Used as references for the maximum length and index of an array. */ - var MAX_ARRAY_LENGTH = 4294967295, - MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1, - HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1; - - /** Used to associate wrap methods with their bit flags. */ - var wrapFlags = [ - ['ary', WRAP_ARY_FLAG], - ['bind', WRAP_BIND_FLAG], - ['bindKey', WRAP_BIND_KEY_FLAG], - ['curry', WRAP_CURRY_FLAG], - ['curryRight', WRAP_CURRY_RIGHT_FLAG], - ['flip', WRAP_FLIP_FLAG], - ['partial', WRAP_PARTIAL_FLAG], - ['partialRight', WRAP_PARTIAL_RIGHT_FLAG], - ['rearg', WRAP_REARG_FLAG] - ]; - - /** `Object#toString` result references. */ - var argsTag = '[object Arguments]', - arrayTag = '[object Array]', - asyncTag = '[object AsyncFunction]', - boolTag = '[object Boolean]', - dateTag = '[object Date]', - domExcTag = '[object DOMException]', - errorTag = '[object Error]', - funcTag = '[object Function]', - genTag = '[object GeneratorFunction]', - mapTag = '[object Map]', - numberTag = '[object Number]', - nullTag = '[object Null]', - objectTag = '[object Object]', - promiseTag = '[object Promise]', - proxyTag = '[object Proxy]', - regexpTag = '[object RegExp]', - setTag = '[object Set]', - stringTag = '[object String]', - symbolTag = '[object Symbol]', - undefinedTag = '[object Undefined]', - weakMapTag = '[object WeakMap]', - weakSetTag = '[object WeakSet]'; - - var arrayBufferTag = '[object ArrayBuffer]', - dataViewTag = '[object DataView]', - float32Tag = '[object Float32Array]', - float64Tag = '[object Float64Array]', - int8Tag = '[object Int8Array]', - int16Tag = '[object Int16Array]', - int32Tag = '[object Int32Array]', - uint8Tag = '[object Uint8Array]', - uint8ClampedTag = '[object Uint8ClampedArray]', - uint16Tag = '[object Uint16Array]', - uint32Tag = '[object Uint32Array]'; - - /** Used to match empty string literals in compiled template source. */ - var reEmptyStringLeading = /\b__p \+= '';/g, - reEmptyStringMiddle = /\b(__p \+=) '' \+/g, - reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; - - /** Used to match HTML entities and HTML characters. */ - var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g, - reUnescapedHtml = /[&<>"']/g, - reHasEscapedHtml = RegExp(reEscapedHtml.source), - reHasUnescapedHtml = RegExp(reUnescapedHtml.source); - - /** Used to match template delimiters. */ - var reEscape = /<%-([\s\S]+?)%>/g, - reEvaluate = /<%([\s\S]+?)%>/g, - reInterpolate = /<%=([\s\S]+?)%>/g; - - /** Used to match property names within property paths. */ - var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, - reIsPlainProp = /^\w*$/, - rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; - - /** - * Used to match `RegExp` - * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). - */ - var reRegExpChar = /[\\^$.*+?()[\]{}|]/g, - reHasRegExpChar = RegExp(reRegExpChar.source); - - /** Used to match leading and trailing whitespace. */ - var reTrim = /^\s+|\s+$/g, - reTrimStart = /^\s+/, - reTrimEnd = /\s+$/; - - /** Used to match wrap detail comments. */ - var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/, - reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/, - reSplitDetails = /,? & /; - - /** Used to match words composed of alphanumeric characters. */ - var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g; - - /** Used to match backslashes in property paths. */ - var reEscapeChar = /\\(\\)?/g; - - /** - * Used to match - * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components). - */ - var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; - - /** Used to match `RegExp` flags from their coerced string values. */ - var reFlags = /\w*$/; - - /** Used to detect bad signed hexadecimal string values. */ - var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; - - /** Used to detect binary string values. */ - var reIsBinary = /^0b[01]+$/i; - - /** Used to detect host constructors (Safari). */ - var reIsHostCtor = /^\[object .+?Constructor\]$/; - - /** Used to detect octal string values. */ - var reIsOctal = /^0o[0-7]+$/i; - - /** Used to detect unsigned integer values. */ - var reIsUint = /^(?:0|[1-9]\d*)$/; - - /** Used to match Latin Unicode letters (excluding mathematical operators). */ - var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; - - /** Used to ensure capturing order of template delimiters. */ - var reNoMatch = /($^)/; - - /** Used to match unescaped characters in compiled string literals. */ - var reUnescapedString = /['\n\r\u2028\u2029\\]/g; - - /** Used to compose unicode character classes. */ - var rsAstralRange = '\\ud800-\\udfff', - rsComboMarksRange = '\\u0300-\\u036f', - reComboHalfMarksRange = '\\ufe20-\\ufe2f', - rsComboSymbolsRange = '\\u20d0-\\u20ff', - rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange, - rsDingbatRange = '\\u2700-\\u27bf', - rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff', - rsMathOpRange = '\\xac\\xb1\\xd7\\xf7', - rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf', - rsPunctuationRange = '\\u2000-\\u206f', - rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000', - rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde', - rsVarRange = '\\ufe0e\\ufe0f', - rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange; - - /** Used to compose unicode capture groups. */ - var rsApos = "['\u2019]", - rsAstral = '[' + rsAstralRange + ']', - rsBreak = '[' + rsBreakRange + ']', - rsCombo = '[' + rsComboRange + ']', - rsDigits = '\\d+', - rsDingbat = '[' + rsDingbatRange + ']', - rsLower = '[' + rsLowerRange + ']', - rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']', - rsFitz = '\\ud83c[\\udffb-\\udfff]', - rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')', - rsNonAstral = '[^' + rsAstralRange + ']', - rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}', - rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]', - rsUpper = '[' + rsUpperRange + ']', - rsZWJ = '\\u200d'; - - /** Used to compose unicode regexes. */ - var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')', - rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')', - rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?', - rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?', - reOptMod = rsModifier + '?', - rsOptVar = '[' + rsVarRange + ']?', - rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*', - rsOrdLower = '\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])', - rsOrdUpper = '\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])', - rsSeq = rsOptVar + reOptMod + rsOptJoin, - rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq, - rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')'; - - /** Used to match apostrophes. */ - var reApos = RegExp(rsApos, 'g'); - - /** - * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and - * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). - */ - var reComboMark = RegExp(rsCombo, 'g'); - - /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */ - var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g'); - - /** Used to match complex or compound words. */ - var reUnicodeWord = RegExp([ - rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')', - rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')', - rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower, - rsUpper + '+' + rsOptContrUpper, - rsOrdUpper, - rsOrdLower, - rsDigits, - rsEmoji - ].join('|'), 'g'); - - /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */ - var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']'); - - /** Used to detect strings that need a more robust regexp to match words. */ - var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/; - - /** Used to assign default `context` object properties. */ - var contextProps = [ - 'Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array', - 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object', - 'Promise', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError', 'Uint8Array', - 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap', - '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout' - ]; - - /** Used to make template sourceURLs easier to identify. */ - var templateCounter = -1; - - /** Used to identify `toStringTag` values of typed arrays. */ - var typedArrayTags = {}; - typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = - typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = - typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = - typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = - typedArrayTags[uint32Tag] = true; - typedArrayTags[argsTag] = typedArrayTags[arrayTag] = - typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = - typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = - typedArrayTags[errorTag] = typedArrayTags[funcTag] = - typedArrayTags[mapTag] = typedArrayTags[numberTag] = - typedArrayTags[objectTag] = typedArrayTags[regexpTag] = - typedArrayTags[setTag] = typedArrayTags[stringTag] = - typedArrayTags[weakMapTag] = false; - - /** Used to identify `toStringTag` values supported by `_.clone`. */ - var cloneableTags = {}; - cloneableTags[argsTag] = cloneableTags[arrayTag] = - cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = - cloneableTags[boolTag] = cloneableTags[dateTag] = - cloneableTags[float32Tag] = cloneableTags[float64Tag] = - cloneableTags[int8Tag] = cloneableTags[int16Tag] = - cloneableTags[int32Tag] = cloneableTags[mapTag] = - cloneableTags[numberTag] = cloneableTags[objectTag] = - cloneableTags[regexpTag] = cloneableTags[setTag] = - cloneableTags[stringTag] = cloneableTags[symbolTag] = - cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = - cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; - cloneableTags[errorTag] = cloneableTags[funcTag] = - cloneableTags[weakMapTag] = false; - - /** Used to map Latin Unicode letters to basic Latin letters. */ - var deburredLetters = { - // Latin-1 Supplement block. - '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', - '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', - '\xc7': 'C', '\xe7': 'c', - '\xd0': 'D', '\xf0': 'd', - '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', - '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', - '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', - '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', - '\xd1': 'N', '\xf1': 'n', - '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', - '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', - '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', - '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', - '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', - '\xc6': 'Ae', '\xe6': 'ae', - '\xde': 'Th', '\xfe': 'th', - '\xdf': 'ss', - // Latin Extended-A block. - '\u0100': 'A', '\u0102': 'A', '\u0104': 'A', - '\u0101': 'a', '\u0103': 'a', '\u0105': 'a', - '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C', - '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c', - '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd', - '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E', - '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e', - '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G', - '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g', - '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h', - '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I', - '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i', - '\u0134': 'J', '\u0135': 'j', - '\u0136': 'K', '\u0137': 'k', '\u0138': 'k', - '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L', - '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l', - '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N', - '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n', - '\u014c': 'O', '\u014e': 'O', '\u0150': 'O', - '\u014d': 'o', '\u014f': 'o', '\u0151': 'o', - '\u0154': 'R', '\u0156': 'R', '\u0158': 'R', - '\u0155': 'r', '\u0157': 'r', '\u0159': 'r', - '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S', - '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's', - '\u0162': 'T', '\u0164': 'T', '\u0166': 'T', - '\u0163': 't', '\u0165': 't', '\u0167': 't', - '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U', - '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u', - '\u0174': 'W', '\u0175': 'w', - '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y', - '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z', - '\u017a': 'z', '\u017c': 'z', '\u017e': 'z', - '\u0132': 'IJ', '\u0133': 'ij', - '\u0152': 'Oe', '\u0153': 'oe', - '\u0149': "'n", '\u017f': 's' - }; - - /** Used to map characters to HTML entities. */ - var htmlEscapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - }; - - /** Used to map HTML entities to characters. */ - var htmlUnescapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - ''': "'" - }; - - /** Used to escape characters for inclusion in compiled string literals. */ - var stringEscapes = { - '\\': '\\', - "'": "'", - '\n': 'n', - '\r': 'r', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - /** Built-in method references without a dependency on `root`. */ - var freeParseFloat = parseFloat, - freeParseInt = parseInt; - - /** Detect free variable `global` from Node.js. */ - var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; - - /** Detect free variable `self`. */ - var freeSelf = typeof self == 'object' && self && self.Object === Object && self; - - /** Used as a reference to the global object. */ - var root = freeGlobal || freeSelf || Function('return this')(); - - /** Detect free variable `exports`. */ - var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; - - /** Detect free variable `module`. */ - var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; - - /** Detect the popular CommonJS extension `module.exports`. */ - var moduleExports = freeModule && freeModule.exports === freeExports; - - /** Detect free variable `process` from Node.js. */ - var freeProcess = moduleExports && freeGlobal.process; - - /** Used to access faster Node.js helpers. */ - var nodeUtil = (function() { - try { - // Use `util.types` for Node.js 10+. - var types = freeModule && freeModule.require && freeModule.require('util').types; - - if (types) { - return types; - } - - // Legacy `process.binding('util')` for Node.js < 10. - return freeProcess && freeProcess.binding && freeProcess.binding('util'); - } catch (e) {} - }()); - - /* Node.js helper references. */ - var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer, - nodeIsDate = nodeUtil && nodeUtil.isDate, - nodeIsMap = nodeUtil && nodeUtil.isMap, - nodeIsRegExp = nodeUtil && nodeUtil.isRegExp, - nodeIsSet = nodeUtil && nodeUtil.isSet, - nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; - - /*--------------------------------------------------------------------------*/ - - /** - * A faster alternative to `Function#apply`, this function invokes `func` - * with the `this` binding of `thisArg` and the arguments of `args`. - * - * @private - * @param {Function} func The function to invoke. - * @param {*} thisArg The `this` binding of `func`. - * @param {Array} args The arguments to invoke `func` with. - * @returns {*} Returns the result of `func`. - */ - function apply(func, thisArg, args) { - switch (args.length) { - case 0: return func.call(thisArg); - case 1: return func.call(thisArg, args[0]); - case 2: return func.call(thisArg, args[0], args[1]); - case 3: return func.call(thisArg, args[0], args[1], args[2]); - } - return func.apply(thisArg, args); - } - - /** - * A specialized version of `baseAggregator` for arrays. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} setter The function to set `accumulator` values. - * @param {Function} iteratee The iteratee to transform keys. - * @param {Object} accumulator The initial aggregated object. - * @returns {Function} Returns `accumulator`. - */ - function arrayAggregator(array, setter, iteratee, accumulator) { - var index = -1, - length = array == null ? 0 : array.length; - - while (++index < length) { - var value = array[index]; - setter(accumulator, value, iteratee(value), array); - } - return accumulator; - } - - /** - * A specialized version of `_.forEach` for arrays without support for - * iteratee shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns `array`. - */ - function arrayEach(array, iteratee) { - var index = -1, - length = array == null ? 0 : array.length; - - while (++index < length) { - if (iteratee(array[index], index, array) === false) { - break; - } - } - return array; - } - - /** - * A specialized version of `_.forEachRight` for arrays without support for - * iteratee shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns `array`. - */ - function arrayEachRight(array, iteratee) { - var length = array == null ? 0 : array.length; - - while (length--) { - if (iteratee(array[length], length, array) === false) { - break; - } - } - return array; - } - - /** - * A specialized version of `_.every` for arrays without support for - * iteratee shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if all elements pass the predicate check, - * else `false`. - */ - function arrayEvery(array, predicate) { - var index = -1, - length = array == null ? 0 : array.length; - - while (++index < length) { - if (!predicate(array[index], index, array)) { - return false; - } - } - return true; - } - - /** - * A specialized version of `_.filter` for arrays without support for - * iteratee shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - */ - function arrayFilter(array, predicate) { - var index = -1, - length = array == null ? 0 : array.length, - resIndex = 0, - result = []; - - while (++index < length) { - var value = array[index]; - if (predicate(value, index, array)) { - result[resIndex++] = value; - } - } - return result; - } - - /** - * A specialized version of `_.includes` for arrays without support for - * specifying an index to search from. - * - * @private - * @param {Array} [array] The array to inspect. - * @param {*} target The value to search for. - * @returns {boolean} Returns `true` if `target` is found, else `false`. - */ - function arrayIncludes(array, value) { - var length = array == null ? 0 : array.length; - return !!length && baseIndexOf(array, value, 0) > -1; - } - - /** - * This function is like `arrayIncludes` except that it accepts a comparator. - * - * @private - * @param {Array} [array] The array to inspect. - * @param {*} target The value to search for. - * @param {Function} comparator The comparator invoked per element. - * @returns {boolean} Returns `true` if `target` is found, else `false`. - */ - function arrayIncludesWith(array, value, comparator) { - var index = -1, - length = array == null ? 0 : array.length; - - while (++index < length) { - if (comparator(value, array[index])) { - return true; - } - } - return false; - } - - /** - * A specialized version of `_.map` for arrays without support for iteratee - * shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the new mapped array. - */ - function arrayMap(array, iteratee) { - var index = -1, - length = array == null ? 0 : array.length, - result = Array(length); - - while (++index < length) { - result[index] = iteratee(array[index], index, array); - } - return result; - } - - /** - * Appends the elements of `values` to `array`. - * - * @private - * @param {Array} array The array to modify. - * @param {Array} values The values to append. - * @returns {Array} Returns `array`. - */ - function arrayPush(array, values) { - var index = -1, - length = values.length, - offset = array.length; - - while (++index < length) { - array[offset + index] = values[index]; - } - return array; - } - - /** - * A specialized version of `_.reduce` for arrays without support for - * iteratee shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @param {boolean} [initAccum] Specify using the first element of `array` as - * the initial value. - * @returns {*} Returns the accumulated value. - */ - function arrayReduce(array, iteratee, accumulator, initAccum) { - var index = -1, - length = array == null ? 0 : array.length; - - if (initAccum && length) { - accumulator = array[++index]; - } - while (++index < length) { - accumulator = iteratee(accumulator, array[index], index, array); - } - return accumulator; - } - - /** - * A specialized version of `_.reduceRight` for arrays without support for - * iteratee shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @param {boolean} [initAccum] Specify using the last element of `array` as - * the initial value. - * @returns {*} Returns the accumulated value. - */ - function arrayReduceRight(array, iteratee, accumulator, initAccum) { - var length = array == null ? 0 : array.length; - if (initAccum && length) { - accumulator = array[--length]; - } - while (length--) { - accumulator = iteratee(accumulator, array[length], length, array); - } - return accumulator; - } - - /** - * A specialized version of `_.some` for arrays without support for iteratee - * shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - */ - function arraySome(array, predicate) { - var index = -1, - length = array == null ? 0 : array.length; - - while (++index < length) { - if (predicate(array[index], index, array)) { - return true; - } - } - return false; - } - - /** - * Gets the size of an ASCII `string`. - * - * @private - * @param {string} string The string inspect. - * @returns {number} Returns the string size. - */ - var asciiSize = baseProperty('length'); - - /** - * Converts an ASCII `string` to an array. - * - * @private - * @param {string} string The string to convert. - * @returns {Array} Returns the converted array. - */ - function asciiToArray(string) { - return string.split(''); - } - - /** - * Splits an ASCII `string` into an array of its words. - * - * @private - * @param {string} The string to inspect. - * @returns {Array} Returns the words of `string`. - */ - function asciiWords(string) { - return string.match(reAsciiWord) || []; - } - - /** - * The base implementation of methods like `_.findKey` and `_.findLastKey`, - * without support for iteratee shorthands, which iterates over `collection` - * using `eachFunc`. - * - * @private - * @param {Array|Object} collection The collection to inspect. - * @param {Function} predicate The function invoked per iteration. - * @param {Function} eachFunc The function to iterate over `collection`. - * @returns {*} Returns the found element or its key, else `undefined`. - */ - function baseFindKey(collection, predicate, eachFunc) { - var result; - eachFunc(collection, function(value, key, collection) { - if (predicate(value, key, collection)) { - result = key; - return false; - } - }); - return result; - } - - /** - * The base implementation of `_.findIndex` and `_.findLastIndex` without - * support for iteratee shorthands. - * - * @private - * @param {Array} array The array to inspect. - * @param {Function} predicate The function invoked per iteration. - * @param {number} fromIndex The index to search from. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {number} Returns the index of the matched value, else `-1`. - */ - function baseFindIndex(array, predicate, fromIndex, fromRight) { - var length = array.length, - index = fromIndex + (fromRight ? 1 : -1); - - while ((fromRight ? index-- : ++index < length)) { - if (predicate(array[index], index, array)) { - return index; - } - } - return -1; - } - - /** - * The base implementation of `_.indexOf` without `fromIndex` bounds checks. - * - * @private - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @param {number} fromIndex The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. - */ - function baseIndexOf(array, value, fromIndex) { - return value === value - ? strictIndexOf(array, value, fromIndex) - : baseFindIndex(array, baseIsNaN, fromIndex); - } - - /** - * This function is like `baseIndexOf` except that it accepts a comparator. - * - * @private - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @param {number} fromIndex The index to search from. - * @param {Function} comparator The comparator invoked per element. - * @returns {number} Returns the index of the matched value, else `-1`. - */ - function baseIndexOfWith(array, value, fromIndex, comparator) { - var index = fromIndex - 1, - length = array.length; - - while (++index < length) { - if (comparator(array[index], value)) { - return index; - } - } - return -1; - } - - /** - * The base implementation of `_.isNaN` without support for number objects. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. - */ - function baseIsNaN(value) { - return value !== value; - } - - /** - * The base implementation of `_.mean` and `_.meanBy` without support for - * iteratee shorthands. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {number} Returns the mean. - */ - function baseMean(array, iteratee) { - var length = array == null ? 0 : array.length; - return length ? (baseSum(array, iteratee) / length) : NAN; - } - - /** - * The base implementation of `_.property` without support for deep paths. - * - * @private - * @param {string} key The key of the property to get. - * @returns {Function} Returns the new accessor function. - */ - function baseProperty(key) { - return function(object) { - return object == null ? undefined : object[key]; - }; - } - - /** - * The base implementation of `_.propertyOf` without support for deep paths. - * - * @private - * @param {Object} object The object to query. - * @returns {Function} Returns the new accessor function. - */ - function basePropertyOf(object) { - return function(key) { - return object == null ? undefined : object[key]; - }; - } - - /** - * The base implementation of `_.reduce` and `_.reduceRight`, without support - * for iteratee shorthands, which iterates over `collection` using `eachFunc`. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {*} accumulator The initial value. - * @param {boolean} initAccum Specify using the first or last element of - * `collection` as the initial value. - * @param {Function} eachFunc The function to iterate over `collection`. - * @returns {*} Returns the accumulated value. - */ - function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) { - eachFunc(collection, function(value, index, collection) { - accumulator = initAccum - ? (initAccum = false, value) - : iteratee(accumulator, value, index, collection); - }); - return accumulator; - } - - /** - * The base implementation of `_.sortBy` which uses `comparer` to define the - * sort order of `array` and replaces criteria objects with their corresponding - * values. - * - * @private - * @param {Array} array The array to sort. - * @param {Function} comparer The function to define sort order. - * @returns {Array} Returns `array`. - */ - function baseSortBy(array, comparer) { - var length = array.length; - - array.sort(comparer); - while (length--) { - array[length] = array[length].value; - } - return array; - } - - /** - * The base implementation of `_.sum` and `_.sumBy` without support for - * iteratee shorthands. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {number} Returns the sum. - */ - function baseSum(array, iteratee) { - var result, - index = -1, - length = array.length; - - while (++index < length) { - var current = iteratee(array[index]); - if (current !== undefined) { - result = result === undefined ? current : (result + current); - } - } - return result; - } - - /** - * The base implementation of `_.times` without support for iteratee shorthands - * or max array length checks. - * - * @private - * @param {number} n The number of times to invoke `iteratee`. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the array of results. - */ - function baseTimes(n, iteratee) { - var index = -1, - result = Array(n); - - while (++index < n) { - result[index] = iteratee(index); - } - return result; - } - - /** - * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array - * of key-value pairs for `object` corresponding to the property names of `props`. - * - * @private - * @param {Object} object The object to query. - * @param {Array} props The property names to get values for. - * @returns {Object} Returns the key-value pairs. - */ - function baseToPairs(object, props) { - return arrayMap(props, function(key) { - return [key, object[key]]; - }); - } - - /** - * The base implementation of `_.unary` without support for storing metadata. - * - * @private - * @param {Function} func The function to cap arguments for. - * @returns {Function} Returns the new capped function. - */ - function baseUnary(func) { - return function(value) { - return func(value); - }; - } - - /** - * The base implementation of `_.values` and `_.valuesIn` which creates an - * array of `object` property values corresponding to the property names - * of `props`. - * - * @private - * @param {Object} object The object to query. - * @param {Array} props The property names to get values for. - * @returns {Object} Returns the array of property values. - */ - function baseValues(object, props) { - return arrayMap(props, function(key) { - return object[key]; - }); - } - - /** - * Checks if a `cache` value for `key` exists. - * - * @private - * @param {Object} cache The cache to query. - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function cacheHas(cache, key) { - return cache.has(key); - } - - /** - * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol - * that is not found in the character symbols. - * - * @private - * @param {Array} strSymbols The string symbols to inspect. - * @param {Array} chrSymbols The character symbols to find. - * @returns {number} Returns the index of the first unmatched string symbol. - */ - function charsStartIndex(strSymbols, chrSymbols) { - var index = -1, - length = strSymbols.length; - - while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {} - return index; - } - - /** - * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol - * that is not found in the character symbols. - * - * @private - * @param {Array} strSymbols The string symbols to inspect. - * @param {Array} chrSymbols The character symbols to find. - * @returns {number} Returns the index of the last unmatched string symbol. - */ - function charsEndIndex(strSymbols, chrSymbols) { - var index = strSymbols.length; - - while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {} - return index; - } - - /** - * Gets the number of `placeholder` occurrences in `array`. - * - * @private - * @param {Array} array The array to inspect. - * @param {*} placeholder The placeholder to search for. - * @returns {number} Returns the placeholder count. - */ - function countHolders(array, placeholder) { - var length = array.length, - result = 0; - - while (length--) { - if (array[length] === placeholder) { - ++result; - } - } - return result; - } - - /** - * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A - * letters to basic Latin letters. - * - * @private - * @param {string} letter The matched letter to deburr. - * @returns {string} Returns the deburred letter. - */ - var deburrLetter = basePropertyOf(deburredLetters); - - /** - * Used by `_.escape` to convert characters to HTML entities. - * - * @private - * @param {string} chr The matched character to escape. - * @returns {string} Returns the escaped character. - */ - var escapeHtmlChar = basePropertyOf(htmlEscapes); - - /** - * Used by `_.template` to escape characters for inclusion in compiled string literals. - * - * @private - * @param {string} chr The matched character to escape. - * @returns {string} Returns the escaped character. - */ - function escapeStringChar(chr) { - return '\\' + stringEscapes[chr]; - } - - /** - * Gets the value at `key` of `object`. - * - * @private - * @param {Object} [object] The object to query. - * @param {string} key The key of the property to get. - * @returns {*} Returns the property value. - */ - function getValue(object, key) { - return object == null ? undefined : object[key]; - } - - /** - * Checks if `string` contains Unicode symbols. - * - * @private - * @param {string} string The string to inspect. - * @returns {boolean} Returns `true` if a symbol is found, else `false`. - */ - function hasUnicode(string) { - return reHasUnicode.test(string); - } - - /** - * Checks if `string` contains a word composed of Unicode symbols. - * - * @private - * @param {string} string The string to inspect. - * @returns {boolean} Returns `true` if a word is found, else `false`. - */ - function hasUnicodeWord(string) { - return reHasUnicodeWord.test(string); - } - - /** - * Converts `iterator` to an array. - * - * @private - * @param {Object} iterator The iterator to convert. - * @returns {Array} Returns the converted array. - */ - function iteratorToArray(iterator) { - var data, - result = []; - - while (!(data = iterator.next()).done) { - result.push(data.value); - } - return result; - } - - /** - * Converts `map` to its key-value pairs. - * - * @private - * @param {Object} map The map to convert. - * @returns {Array} Returns the key-value pairs. - */ - function mapToArray(map) { - var index = -1, - result = Array(map.size); - - map.forEach(function(value, key) { - result[++index] = [key, value]; - }); - return result; - } - - /** - * Creates a unary function that invokes `func` with its argument transformed. - * - * @private - * @param {Function} func The function to wrap. - * @param {Function} transform The argument transform. - * @returns {Function} Returns the new function. - */ - function overArg(func, transform) { - return function(arg) { - return func(transform(arg)); - }; - } - - /** - * Replaces all `placeholder` elements in `array` with an internal placeholder - * and returns an array of their indexes. - * - * @private - * @param {Array} array The array to modify. - * @param {*} placeholder The placeholder to replace. - * @returns {Array} Returns the new array of placeholder indexes. - */ - function replaceHolders(array, placeholder) { - var index = -1, - length = array.length, - resIndex = 0, - result = []; - - while (++index < length) { - var value = array[index]; - if (value === placeholder || value === PLACEHOLDER) { - array[index] = PLACEHOLDER; - result[resIndex++] = index; - } - } - return result; - } - - /** - * Gets the value at `key`, unless `key` is "__proto__". - * - * @private - * @param {Object} object The object to query. - * @param {string} key The key of the property to get. - * @returns {*} Returns the property value. - */ - function safeGet(object, key) { - return key == '__proto__' - ? undefined - : object[key]; - } - - /** - * Converts `set` to an array of its values. - * - * @private - * @param {Object} set The set to convert. - * @returns {Array} Returns the values. - */ - function setToArray(set) { - var index = -1, - result = Array(set.size); - - set.forEach(function(value) { - result[++index] = value; - }); - return result; - } - - /** - * Converts `set` to its value-value pairs. - * - * @private - * @param {Object} set The set to convert. - * @returns {Array} Returns the value-value pairs. - */ - function setToPairs(set) { - var index = -1, - result = Array(set.size); - - set.forEach(function(value) { - result[++index] = [value, value]; - }); - return result; - } - - /** - * A specialized version of `_.indexOf` which performs strict equality - * comparisons of values, i.e. `===`. - * - * @private - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @param {number} fromIndex The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. - */ - function strictIndexOf(array, value, fromIndex) { - var index = fromIndex - 1, - length = array.length; - - while (++index < length) { - if (array[index] === value) { - return index; - } - } - return -1; - } - - /** - * A specialized version of `_.lastIndexOf` which performs strict equality - * comparisons of values, i.e. `===`. - * - * @private - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @param {number} fromIndex The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. - */ - function strictLastIndexOf(array, value, fromIndex) { - var index = fromIndex + 1; - while (index--) { - if (array[index] === value) { - return index; - } - } - return index; - } - - /** - * Gets the number of symbols in `string`. - * - * @private - * @param {string} string The string to inspect. - * @returns {number} Returns the string size. - */ - function stringSize(string) { - return hasUnicode(string) - ? unicodeSize(string) - : asciiSize(string); - } - - /** - * Converts `string` to an array. - * - * @private - * @param {string} string The string to convert. - * @returns {Array} Returns the converted array. - */ - function stringToArray(string) { - return hasUnicode(string) - ? unicodeToArray(string) - : asciiToArray(string); - } - - /** - * Used by `_.unescape` to convert HTML entities to characters. - * - * @private - * @param {string} chr The matched character to unescape. - * @returns {string} Returns the unescaped character. - */ - var unescapeHtmlChar = basePropertyOf(htmlUnescapes); - - /** - * Gets the size of a Unicode `string`. - * - * @private - * @param {string} string The string inspect. - * @returns {number} Returns the string size. - */ - function unicodeSize(string) { - var result = reUnicode.lastIndex = 0; - while (reUnicode.test(string)) { - ++result; - } - return result; - } - - /** - * Converts a Unicode `string` to an array. - * - * @private - * @param {string} string The string to convert. - * @returns {Array} Returns the converted array. - */ - function unicodeToArray(string) { - return string.match(reUnicode) || []; - } - - /** - * Splits a Unicode `string` into an array of its words. - * - * @private - * @param {string} The string to inspect. - * @returns {Array} Returns the words of `string`. - */ - function unicodeWords(string) { - return string.match(reUnicodeWord) || []; - } - - /*--------------------------------------------------------------------------*/ - - /** - * Create a new pristine `lodash` function using the `context` object. - * - * @static - * @memberOf _ - * @since 1.1.0 - * @category Util - * @param {Object} [context=root] The context object. - * @returns {Function} Returns a new `lodash` function. - * @example - * - * _.mixin({ 'foo': _.constant('foo') }); - * - * var lodash = _.runInContext(); - * lodash.mixin({ 'bar': lodash.constant('bar') }); - * - * _.isFunction(_.foo); - * // => true - * _.isFunction(_.bar); - * // => false - * - * lodash.isFunction(lodash.foo); - * // => false - * lodash.isFunction(lodash.bar); - * // => true - * - * // Create a suped-up `defer` in Node.js. - * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer; - */ - var runInContext = (function runInContext(context) { - context = context == null ? root : _.defaults(root.Object(), context, _.pick(root, contextProps)); - - /** Built-in constructor references. */ - var Array = context.Array, - Date = context.Date, - Error = context.Error, - Function = context.Function, - Math = context.Math, - Object = context.Object, - RegExp = context.RegExp, - String = context.String, - TypeError = context.TypeError; - - /** Used for built-in method references. */ - var arrayProto = Array.prototype, - funcProto = Function.prototype, - objectProto = Object.prototype; - - /** Used to detect overreaching core-js shims. */ - var coreJsData = context['__core-js_shared__']; - - /** Used to resolve the decompiled source of functions. */ - var funcToString = funcProto.toString; - - /** Used to check objects for own properties. */ - var hasOwnProperty = objectProto.hasOwnProperty; - - /** Used to generate unique IDs. */ - var idCounter = 0; - - /** Used to detect methods masquerading as native. */ - var maskSrcKey = (function() { - var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); - return uid ? ('Symbol(src)_1.' + uid) : ''; - }()); - - /** - * Used to resolve the - * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) - * of values. - */ - var nativeObjectToString = objectProto.toString; - - /** Used to infer the `Object` constructor. */ - var objectCtorString = funcToString.call(Object); - - /** Used to restore the original `_` reference in `_.noConflict`. */ - var oldDash = root._; - - /** Used to detect if a method is native. */ - var reIsNative = RegExp('^' + - funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') - .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' - ); - - /** Built-in value references. */ - var Buffer = moduleExports ? context.Buffer : undefined, - Symbol = context.Symbol, - Uint8Array = context.Uint8Array, - allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined, - getPrototype = overArg(Object.getPrototypeOf, Object), - objectCreate = Object.create, - propertyIsEnumerable = objectProto.propertyIsEnumerable, - splice = arrayProto.splice, - spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined, - symIterator = Symbol ? Symbol.iterator : undefined, - symToStringTag = Symbol ? Symbol.toStringTag : undefined; - - var defineProperty = (function() { - try { - var func = getNative(Object, 'defineProperty'); - func({}, '', {}); - return func; - } catch (e) {} - }()); - - /** Mocked built-ins. */ - var ctxClearTimeout = context.clearTimeout !== root.clearTimeout && context.clearTimeout, - ctxNow = Date && Date.now !== root.Date.now && Date.now, - ctxSetTimeout = context.setTimeout !== root.setTimeout && context.setTimeout; - - /* Built-in method references for those with the same name as other `lodash` methods. */ - var nativeCeil = Math.ceil, - nativeFloor = Math.floor, - nativeGetSymbols = Object.getOwnPropertySymbols, - nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined, - nativeIsFinite = context.isFinite, - nativeJoin = arrayProto.join, - nativeKeys = overArg(Object.keys, Object), - nativeMax = Math.max, - nativeMin = Math.min, - nativeNow = Date.now, - nativeParseInt = context.parseInt, - nativeRandom = Math.random, - nativeReverse = arrayProto.reverse; - - /* Built-in method references that are verified to be native. */ - var DataView = getNative(context, 'DataView'), - Map = getNative(context, 'Map'), - Promise = getNative(context, 'Promise'), - Set = getNative(context, 'Set'), - WeakMap = getNative(context, 'WeakMap'), - nativeCreate = getNative(Object, 'create'); - - /** Used to store function metadata. */ - var metaMap = WeakMap && new WeakMap; - - /** Used to lookup unminified function names. */ - var realNames = {}; - - /** Used to detect maps, sets, and weakmaps. */ - var dataViewCtorString = toSource(DataView), - mapCtorString = toSource(Map), - promiseCtorString = toSource(Promise), - setCtorString = toSource(Set), - weakMapCtorString = toSource(WeakMap); - - /** Used to convert symbols to primitives and strings. */ - var symbolProto = Symbol ? Symbol.prototype : undefined, - symbolValueOf = symbolProto ? symbolProto.valueOf : undefined, - symbolToString = symbolProto ? symbolProto.toString : undefined; - - /*------------------------------------------------------------------------*/ - - /** - * Creates a `lodash` object which wraps `value` to enable implicit method - * chain sequences. Methods that operate on and return arrays, collections, - * and functions can be chained together. Methods that retrieve a single value - * or may return a primitive value will automatically end the chain sequence - * and return the unwrapped value. Otherwise, the value must be unwrapped - * with `_#value`. - * - * Explicit chain sequences, which must be unwrapped with `_#value`, may be - * enabled using `_.chain`. - * - * The execution of chained methods is lazy, that is, it's deferred until - * `_#value` is implicitly or explicitly called. - * - * Lazy evaluation allows several methods to support shortcut fusion. - * Shortcut fusion is an optimization to merge iteratee calls; this avoids - * the creation of intermediate arrays and can greatly reduce the number of - * iteratee executions. Sections of a chain sequence qualify for shortcut - * fusion if the section is applied to an array and iteratees accept only - * one argument. The heuristic for whether a section qualifies for shortcut - * fusion is subject to change. - * - * Chaining is supported in custom builds as long as the `_#value` method is - * directly or indirectly included in the build. - * - * In addition to lodash methods, wrappers have `Array` and `String` methods. - * - * The wrapper `Array` methods are: - * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift` - * - * The wrapper `String` methods are: - * `replace` and `split` - * - * The wrapper methods that support shortcut fusion are: - * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`, - * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`, - * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray` - * - * The chainable wrapper methods are: - * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`, - * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`, - * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`, - * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`, - * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`, - * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`, - * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`, - * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`, - * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`, - * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`, - * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`, - * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`, - * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`, - * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`, - * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`, - * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`, - * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`, - * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`, - * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`, - * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`, - * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`, - * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`, - * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`, - * `zipObject`, `zipObjectDeep`, and `zipWith` - * - * The wrapper methods that are **not** chainable by default are: - * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`, - * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`, - * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`, - * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`, - * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`, - * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`, - * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`, - * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, - * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, - * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, - * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, - * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, - * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`, - * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`, - * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, - * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`, - * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, - * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`, - * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`, - * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`, - * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`, - * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`, - * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`, - * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`, - * `upperFirst`, `value`, and `words` - * - * @name _ - * @constructor - * @category Seq - * @param {*} value The value to wrap in a `lodash` instance. - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * function square(n) { - * return n * n; - * } - * - * var wrapped = _([1, 2, 3]); - * - * // Returns an unwrapped value. - * wrapped.reduce(_.add); - * // => 6 - * - * // Returns a wrapped value. - * var squares = wrapped.map(square); - * - * _.isArray(squares); - * // => false - * - * _.isArray(squares.value()); - * // => true - */ - function lodash(value) { - if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) { - if (value instanceof LodashWrapper) { - return value; - } - if (hasOwnProperty.call(value, '__wrapped__')) { - return wrapperClone(value); - } - } - return new LodashWrapper(value); - } - - /** - * The base implementation of `_.create` without support for assigning - * properties to the created object. - * - * @private - * @param {Object} proto The object to inherit from. - * @returns {Object} Returns the new object. - */ - var baseCreate = (function() { - function object() {} - return function(proto) { - if (!isObject(proto)) { - return {}; - } - if (objectCreate) { - return objectCreate(proto); - } - object.prototype = proto; - var result = new object; - object.prototype = undefined; - return result; - }; - }()); - - /** - * The function whose prototype chain sequence wrappers inherit from. - * - * @private - */ - function baseLodash() { - // No operation performed. - } - - /** - * The base constructor for creating `lodash` wrapper objects. - * - * @private - * @param {*} value The value to wrap. - * @param {boolean} [chainAll] Enable explicit method chain sequences. - */ - function LodashWrapper(value, chainAll) { - this.__wrapped__ = value; - this.__actions__ = []; - this.__chain__ = !!chainAll; - this.__index__ = 0; - this.__values__ = undefined; - } - - /** - * By default, the template delimiters used by lodash are like those in - * embedded Ruby (ERB) as well as ES2015 template strings. Change the - * following template settings to use alternative delimiters. - * - * @static - * @memberOf _ - * @type {Object} - */ - lodash.templateSettings = { - - /** - * Used to detect `data` property values to be HTML-escaped. - * - * @memberOf _.templateSettings - * @type {RegExp} - */ - 'escape': reEscape, - - /** - * Used to detect code to be evaluated. - * - * @memberOf _.templateSettings - * @type {RegExp} - */ - 'evaluate': reEvaluate, - - /** - * Used to detect `data` property values to inject. - * - * @memberOf _.templateSettings - * @type {RegExp} - */ - 'interpolate': reInterpolate, - - /** - * Used to reference the data object in the template text. - * - * @memberOf _.templateSettings - * @type {string} - */ - 'variable': '', - - /** - * Used to import variables into the compiled template. - * - * @memberOf _.templateSettings - * @type {Object} - */ - 'imports': { - - /** - * A reference to the `lodash` function. - * - * @memberOf _.templateSettings.imports - * @type {Function} - */ - '_': lodash - } - }; - - // Ensure wrappers are instances of `baseLodash`. - lodash.prototype = baseLodash.prototype; - lodash.prototype.constructor = lodash; - - LodashWrapper.prototype = baseCreate(baseLodash.prototype); - LodashWrapper.prototype.constructor = LodashWrapper; - - /*------------------------------------------------------------------------*/ - - /** - * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. - * - * @private - * @constructor - * @param {*} value The value to wrap. - */ - function LazyWrapper(value) { - this.__wrapped__ = value; - this.__actions__ = []; - this.__dir__ = 1; - this.__filtered__ = false; - this.__iteratees__ = []; - this.__takeCount__ = MAX_ARRAY_LENGTH; - this.__views__ = []; - } - - /** - * Creates a clone of the lazy wrapper object. - * - * @private - * @name clone - * @memberOf LazyWrapper - * @returns {Object} Returns the cloned `LazyWrapper` object. - */ - function lazyClone() { - var result = new LazyWrapper(this.__wrapped__); - result.__actions__ = copyArray(this.__actions__); - result.__dir__ = this.__dir__; - result.__filtered__ = this.__filtered__; - result.__iteratees__ = copyArray(this.__iteratees__); - result.__takeCount__ = this.__takeCount__; - result.__views__ = copyArray(this.__views__); - return result; - } - - /** - * Reverses the direction of lazy iteration. - * - * @private - * @name reverse - * @memberOf LazyWrapper - * @returns {Object} Returns the new reversed `LazyWrapper` object. - */ - function lazyReverse() { - if (this.__filtered__) { - var result = new LazyWrapper(this); - result.__dir__ = -1; - result.__filtered__ = true; - } else { - result = this.clone(); - result.__dir__ *= -1; - } - return result; - } - - /** - * Extracts the unwrapped value from its lazy wrapper. - * - * @private - * @name value - * @memberOf LazyWrapper - * @returns {*} Returns the unwrapped value. - */ - function lazyValue() { - var array = this.__wrapped__.value(), - dir = this.__dir__, - isArr = isArray(array), - isRight = dir < 0, - arrLength = isArr ? array.length : 0, - view = getView(0, arrLength, this.__views__), - start = view.start, - end = view.end, - length = end - start, - index = isRight ? end : (start - 1), - iteratees = this.__iteratees__, - iterLength = iteratees.length, - resIndex = 0, - takeCount = nativeMin(length, this.__takeCount__); - - if (!isArr || (!isRight && arrLength == length && takeCount == length)) { - return baseWrapperValue(array, this.__actions__); - } - var result = []; - - outer: - while (length-- && resIndex < takeCount) { - index += dir; - - var iterIndex = -1, - value = array[index]; - - while (++iterIndex < iterLength) { - var data = iteratees[iterIndex], - iteratee = data.iteratee, - type = data.type, - computed = iteratee(value); - - if (type == LAZY_MAP_FLAG) { - value = computed; - } else if (!computed) { - if (type == LAZY_FILTER_FLAG) { - continue outer; - } else { - break outer; - } - } - } - result[resIndex++] = value; - } - return result; - } - - // Ensure `LazyWrapper` is an instance of `baseLodash`. - LazyWrapper.prototype = baseCreate(baseLodash.prototype); - LazyWrapper.prototype.constructor = LazyWrapper; - - /*------------------------------------------------------------------------*/ - - /** - * Creates a hash object. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ - function Hash(entries) { - var index = -1, - length = entries == null ? 0 : entries.length; - - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } - } - - /** - * Removes all key-value entries from the hash. - * - * @private - * @name clear - * @memberOf Hash - */ - function hashClear() { - this.__data__ = nativeCreate ? nativeCreate(null) : {}; - this.size = 0; - } - - /** - * Removes `key` and its value from the hash. - * - * @private - * @name delete - * @memberOf Hash - * @param {Object} hash The hash to modify. - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ - function hashDelete(key) { - var result = this.has(key) && delete this.__data__[key]; - this.size -= result ? 1 : 0; - return result; - } - - /** - * Gets the hash value for `key`. - * - * @private - * @name get - * @memberOf Hash - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ - function hashGet(key) { - var data = this.__data__; - if (nativeCreate) { - var result = data[key]; - return result === HASH_UNDEFINED ? undefined : result; - } - return hasOwnProperty.call(data, key) ? data[key] : undefined; - } - - /** - * Checks if a hash value for `key` exists. - * - * @private - * @name has - * @memberOf Hash - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function hashHas(key) { - var data = this.__data__; - return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key); - } - - /** - * Sets the hash `key` to `value`. - * - * @private - * @name set - * @memberOf Hash - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the hash instance. - */ - function hashSet(key, value) { - var data = this.__data__; - this.size += this.has(key) ? 0 : 1; - data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; - return this; - } - - // Add methods to `Hash`. - Hash.prototype.clear = hashClear; - Hash.prototype['delete'] = hashDelete; - Hash.prototype.get = hashGet; - Hash.prototype.has = hashHas; - Hash.prototype.set = hashSet; - - /*------------------------------------------------------------------------*/ - - /** - * Creates an list cache object. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ - function ListCache(entries) { - var index = -1, - length = entries == null ? 0 : entries.length; - - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } - } - - /** - * Removes all key-value entries from the list cache. - * - * @private - * @name clear - * @memberOf ListCache - */ - function listCacheClear() { - this.__data__ = []; - this.size = 0; - } - - /** - * Removes `key` and its value from the list cache. - * - * @private - * @name delete - * @memberOf ListCache - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ - function listCacheDelete(key) { - var data = this.__data__, - index = assocIndexOf(data, key); - - if (index < 0) { - return false; - } - var lastIndex = data.length - 1; - if (index == lastIndex) { - data.pop(); - } else { - splice.call(data, index, 1); - } - --this.size; - return true; - } - - /** - * Gets the list cache value for `key`. - * - * @private - * @name get - * @memberOf ListCache - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ - function listCacheGet(key) { - var data = this.__data__, - index = assocIndexOf(data, key); - - return index < 0 ? undefined : data[index][1]; - } - - /** - * Checks if a list cache value for `key` exists. - * - * @private - * @name has - * @memberOf ListCache - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function listCacheHas(key) { - return assocIndexOf(this.__data__, key) > -1; - } - - /** - * Sets the list cache `key` to `value`. - * - * @private - * @name set - * @memberOf ListCache - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the list cache instance. - */ - function listCacheSet(key, value) { - var data = this.__data__, - index = assocIndexOf(data, key); - - if (index < 0) { - ++this.size; - data.push([key, value]); - } else { - data[index][1] = value; - } - return this; - } - - // Add methods to `ListCache`. - ListCache.prototype.clear = listCacheClear; - ListCache.prototype['delete'] = listCacheDelete; - ListCache.prototype.get = listCacheGet; - ListCache.prototype.has = listCacheHas; - ListCache.prototype.set = listCacheSet; - - /*------------------------------------------------------------------------*/ - - /** - * Creates a map cache object to store key-value pairs. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ - function MapCache(entries) { - var index = -1, - length = entries == null ? 0 : entries.length; - - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } - } - - /** - * Removes all key-value entries from the map. - * - * @private - * @name clear - * @memberOf MapCache - */ - function mapCacheClear() { - this.size = 0; - this.__data__ = { - 'hash': new Hash, - 'map': new (Map || ListCache), - 'string': new Hash - }; - } - - /** - * Removes `key` and its value from the map. - * - * @private - * @name delete - * @memberOf MapCache - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ - function mapCacheDelete(key) { - var result = getMapData(this, key)['delete'](key); - this.size -= result ? 1 : 0; - return result; - } - - /** - * Gets the map value for `key`. - * - * @private - * @name get - * @memberOf MapCache - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ - function mapCacheGet(key) { - return getMapData(this, key).get(key); - } - - /** - * Checks if a map value for `key` exists. - * - * @private - * @name has - * @memberOf MapCache - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function mapCacheHas(key) { - return getMapData(this, key).has(key); - } - - /** - * Sets the map `key` to `value`. - * - * @private - * @name set - * @memberOf MapCache - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the map cache instance. - */ - function mapCacheSet(key, value) { - var data = getMapData(this, key), - size = data.size; - - data.set(key, value); - this.size += data.size == size ? 0 : 1; - return this; - } - - // Add methods to `MapCache`. - MapCache.prototype.clear = mapCacheClear; - MapCache.prototype['delete'] = mapCacheDelete; - MapCache.prototype.get = mapCacheGet; - MapCache.prototype.has = mapCacheHas; - MapCache.prototype.set = mapCacheSet; - - /*------------------------------------------------------------------------*/ - - /** - * - * Creates an array cache object to store unique values. - * - * @private - * @constructor - * @param {Array} [values] The values to cache. - */ - function SetCache(values) { - var index = -1, - length = values == null ? 0 : values.length; - - this.__data__ = new MapCache; - while (++index < length) { - this.add(values[index]); - } - } - - /** - * Adds `value` to the array cache. - * - * @private - * @name add - * @memberOf SetCache - * @alias push - * @param {*} value The value to cache. - * @returns {Object} Returns the cache instance. - */ - function setCacheAdd(value) { - this.__data__.set(value, HASH_UNDEFINED); - return this; - } - - /** - * Checks if `value` is in the array cache. - * - * @private - * @name has - * @memberOf SetCache - * @param {*} value The value to search for. - * @returns {number} Returns `true` if `value` is found, else `false`. - */ - function setCacheHas(value) { - return this.__data__.has(value); - } - - // Add methods to `SetCache`. - SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; - SetCache.prototype.has = setCacheHas; - - /*------------------------------------------------------------------------*/ - - /** - * Creates a stack cache object to store key-value pairs. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ - function Stack(entries) { - var data = this.__data__ = new ListCache(entries); - this.size = data.size; - } - - /** - * Removes all key-value entries from the stack. - * - * @private - * @name clear - * @memberOf Stack - */ - function stackClear() { - this.__data__ = new ListCache; - this.size = 0; - } - - /** - * Removes `key` and its value from the stack. - * - * @private - * @name delete - * @memberOf Stack - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ - function stackDelete(key) { - var data = this.__data__, - result = data['delete'](key); - - this.size = data.size; - return result; - } - - /** - * Gets the stack value for `key`. - * - * @private - * @name get - * @memberOf Stack - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ - function stackGet(key) { - return this.__data__.get(key); - } - - /** - * Checks if a stack value for `key` exists. - * - * @private - * @name has - * @memberOf Stack - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function stackHas(key) { - return this.__data__.has(key); - } - - /** - * Sets the stack `key` to `value`. - * - * @private - * @name set - * @memberOf Stack - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the stack cache instance. - */ - function stackSet(key, value) { - var data = this.__data__; - if (data instanceof ListCache) { - var pairs = data.__data__; - if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { - pairs.push([key, value]); - this.size = ++data.size; - return this; - } - data = this.__data__ = new MapCache(pairs); - } - data.set(key, value); - this.size = data.size; - return this; - } - - // Add methods to `Stack`. - Stack.prototype.clear = stackClear; - Stack.prototype['delete'] = stackDelete; - Stack.prototype.get = stackGet; - Stack.prototype.has = stackHas; - Stack.prototype.set = stackSet; - - /*------------------------------------------------------------------------*/ - - /** - * Creates an array of the enumerable property names of the array-like `value`. - * - * @private - * @param {*} value The value to query. - * @param {boolean} inherited Specify returning inherited property names. - * @returns {Array} Returns the array of property names. - */ - function arrayLikeKeys(value, inherited) { - var isArr = isArray(value), - isArg = !isArr && isArguments(value), - isBuff = !isArr && !isArg && isBuffer(value), - isType = !isArr && !isArg && !isBuff && isTypedArray(value), - skipIndexes = isArr || isArg || isBuff || isType, - result = skipIndexes ? baseTimes(value.length, String) : [], - length = result.length; - - for (var key in value) { - if ((inherited || hasOwnProperty.call(value, key)) && - !(skipIndexes && ( - // Safari 9 has enumerable `arguments.length` in strict mode. - key == 'length' || - // Node.js 0.10 has enumerable non-index properties on buffers. - (isBuff && (key == 'offset' || key == 'parent')) || - // PhantomJS 2 has enumerable non-index properties on typed arrays. - (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) || - // Skip index properties. - isIndex(key, length) - ))) { - result.push(key); - } - } - return result; - } - - /** - * A specialized version of `_.sample` for arrays. - * - * @private - * @param {Array} array The array to sample. - * @returns {*} Returns the random element. - */ - function arraySample(array) { - var length = array.length; - return length ? array[baseRandom(0, length - 1)] : undefined; - } - - /** - * A specialized version of `_.sampleSize` for arrays. - * - * @private - * @param {Array} array The array to sample. - * @param {number} n The number of elements to sample. - * @returns {Array} Returns the random elements. - */ - function arraySampleSize(array, n) { - return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length)); - } - - /** - * A specialized version of `_.shuffle` for arrays. - * - * @private - * @param {Array} array The array to shuffle. - * @returns {Array} Returns the new shuffled array. - */ - function arrayShuffle(array) { - return shuffleSelf(copyArray(array)); - } - - /** - * This function is like `assignValue` except that it doesn't assign - * `undefined` values. - * - * @private - * @param {Object} object The object to modify. - * @param {string} key The key of the property to assign. - * @param {*} value The value to assign. - */ - function assignMergeValue(object, key, value) { - if ((value !== undefined && !eq(object[key], value)) || - (value === undefined && !(key in object))) { - baseAssignValue(object, key, value); - } - } - - /** - * Assigns `value` to `key` of `object` if the existing value is not equivalent - * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. - * - * @private - * @param {Object} object The object to modify. - * @param {string} key The key of the property to assign. - * @param {*} value The value to assign. - */ - function assignValue(object, key, value) { - var objValue = object[key]; - if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || - (value === undefined && !(key in object))) { - baseAssignValue(object, key, value); - } - } - - /** - * Gets the index at which the `key` is found in `array` of key-value pairs. - * - * @private - * @param {Array} array The array to inspect. - * @param {*} key The key to search for. - * @returns {number} Returns the index of the matched value, else `-1`. - */ - function assocIndexOf(array, key) { - var length = array.length; - while (length--) { - if (eq(array[length][0], key)) { - return length; - } - } - return -1; - } - - /** - * Aggregates elements of `collection` on `accumulator` with keys transformed - * by `iteratee` and values set by `setter`. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} setter The function to set `accumulator` values. - * @param {Function} iteratee The iteratee to transform keys. - * @param {Object} accumulator The initial aggregated object. - * @returns {Function} Returns `accumulator`. - */ - function baseAggregator(collection, setter, iteratee, accumulator) { - baseEach(collection, function(value, key, collection) { - setter(accumulator, value, iteratee(value), collection); - }); - return accumulator; - } - - /** - * The base implementation of `_.assign` without support for multiple sources - * or `customizer` functions. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @returns {Object} Returns `object`. - */ - function baseAssign(object, source) { - return object && copyObject(source, keys(source), object); - } - - /** - * The base implementation of `_.assignIn` without support for multiple sources - * or `customizer` functions. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @returns {Object} Returns `object`. - */ - function baseAssignIn(object, source) { - return object && copyObject(source, keysIn(source), object); - } - - /** - * The base implementation of `assignValue` and `assignMergeValue` without - * value checks. - * - * @private - * @param {Object} object The object to modify. - * @param {string} key The key of the property to assign. - * @param {*} value The value to assign. - */ - function baseAssignValue(object, key, value) { - if (key == '__proto__' && defineProperty) { - defineProperty(object, key, { - 'configurable': true, - 'enumerable': true, - 'value': value, - 'writable': true - }); - } else { - object[key] = value; - } - } - - /** - * The base implementation of `_.at` without support for individual paths. - * - * @private - * @param {Object} object The object to iterate over. - * @param {string[]} paths The property paths to pick. - * @returns {Array} Returns the picked elements. - */ - function baseAt(object, paths) { - var index = -1, - length = paths.length, - result = Array(length), - skip = object == null; - - while (++index < length) { - result[index] = skip ? undefined : get(object, paths[index]); - } - return result; - } - - /** - * The base implementation of `_.clamp` which doesn't coerce arguments. - * - * @private - * @param {number} number The number to clamp. - * @param {number} [lower] The lower bound. - * @param {number} upper The upper bound. - * @returns {number} Returns the clamped number. - */ - function baseClamp(number, lower, upper) { - if (number === number) { - if (upper !== undefined) { - number = number <= upper ? number : upper; - } - if (lower !== undefined) { - number = number >= lower ? number : lower; - } - } - return number; - } - - /** - * The base implementation of `_.clone` and `_.cloneDeep` which tracks - * traversed objects. - * - * @private - * @param {*} value The value to clone. - * @param {boolean} bitmask The bitmask flags. - * 1 - Deep clone - * 2 - Flatten inherited properties - * 4 - Clone symbols - * @param {Function} [customizer] The function to customize cloning. - * @param {string} [key] The key of `value`. - * @param {Object} [object] The parent object of `value`. - * @param {Object} [stack] Tracks traversed objects and their clone counterparts. - * @returns {*} Returns the cloned value. - */ - function baseClone(value, bitmask, customizer, key, object, stack) { - var result, - isDeep = bitmask & CLONE_DEEP_FLAG, - isFlat = bitmask & CLONE_FLAT_FLAG, - isFull = bitmask & CLONE_SYMBOLS_FLAG; - - if (customizer) { - result = object ? customizer(value, key, object, stack) : customizer(value); - } - if (result !== undefined) { - return result; - } - if (!isObject(value)) { - return value; - } - var isArr = isArray(value); - if (isArr) { - result = initCloneArray(value); - if (!isDeep) { - return copyArray(value, result); - } - } else { - var tag = getTag(value), - isFunc = tag == funcTag || tag == genTag; - - if (isBuffer(value)) { - return cloneBuffer(value, isDeep); - } - if (tag == objectTag || tag == argsTag || (isFunc && !object)) { - result = (isFlat || isFunc) ? {} : initCloneObject(value); - if (!isDeep) { - return isFlat - ? copySymbolsIn(value, baseAssignIn(result, value)) - : copySymbols(value, baseAssign(result, value)); - } - } else { - if (!cloneableTags[tag]) { - return object ? value : {}; - } - result = initCloneByTag(value, tag, isDeep); - } - } - // Check for circular references and return its corresponding clone. - stack || (stack = new Stack); - var stacked = stack.get(value); - if (stacked) { - return stacked; - } - stack.set(value, result); - - if (isSet(value)) { - value.forEach(function(subValue) { - result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack)); - }); - - return result; - } - - if (isMap(value)) { - value.forEach(function(subValue, key) { - result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack)); - }); - - return result; - } - - var keysFunc = isFull - ? (isFlat ? getAllKeysIn : getAllKeys) - : (isFlat ? keysIn : keys); - - var props = isArr ? undefined : keysFunc(value); - arrayEach(props || value, function(subValue, key) { - if (props) { - key = subValue; - subValue = value[key]; - } - // Recursively populate clone (susceptible to call stack limits). - assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack)); - }); - return result; - } - - /** - * The base implementation of `_.conforms` which doesn't clone `source`. - * - * @private - * @param {Object} source The object of property predicates to conform to. - * @returns {Function} Returns the new spec function. - */ - function baseConforms(source) { - var props = keys(source); - return function(object) { - return baseConformsTo(object, source, props); - }; - } - - /** - * The base implementation of `_.conformsTo` which accepts `props` to check. - * - * @private - * @param {Object} object The object to inspect. - * @param {Object} source The object of property predicates to conform to. - * @returns {boolean} Returns `true` if `object` conforms, else `false`. - */ - function baseConformsTo(object, source, props) { - var length = props.length; - if (object == null) { - return !length; - } - object = Object(object); - while (length--) { - var key = props[length], - predicate = source[key], - value = object[key]; - - if ((value === undefined && !(key in object)) || !predicate(value)) { - return false; - } - } - return true; - } - - /** - * The base implementation of `_.delay` and `_.defer` which accepts `args` - * to provide to `func`. - * - * @private - * @param {Function} func The function to delay. - * @param {number} wait The number of milliseconds to delay invocation. - * @param {Array} args The arguments to provide to `func`. - * @returns {number|Object} Returns the timer id or timeout object. - */ - function baseDelay(func, wait, args) { - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - return setTimeout(function() { func.apply(undefined, args); }, wait); - } - - /** - * The base implementation of methods like `_.difference` without support - * for excluding multiple arrays or iteratee shorthands. - * - * @private - * @param {Array} array The array to inspect. - * @param {Array} values The values to exclude. - * @param {Function} [iteratee] The iteratee invoked per element. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of filtered values. - */ - function baseDifference(array, values, iteratee, comparator) { - var index = -1, - includes = arrayIncludes, - isCommon = true, - length = array.length, - result = [], - valuesLength = values.length; - - if (!length) { - return result; - } - if (iteratee) { - values = arrayMap(values, baseUnary(iteratee)); - } - if (comparator) { - includes = arrayIncludesWith; - isCommon = false; - } - else if (values.length >= LARGE_ARRAY_SIZE) { - includes = cacheHas; - isCommon = false; - values = new SetCache(values); - } - outer: - while (++index < length) { - var value = array[index], - computed = iteratee == null ? value : iteratee(value); - - value = (comparator || value !== 0) ? value : 0; - if (isCommon && computed === computed) { - var valuesIndex = valuesLength; - while (valuesIndex--) { - if (values[valuesIndex] === computed) { - continue outer; - } - } - result.push(value); - } - else if (!includes(values, computed, comparator)) { - result.push(value); - } - } - return result; - } - - /** - * The base implementation of `_.forEach` without support for iteratee shorthands. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array|Object} Returns `collection`. - */ - var baseEach = createBaseEach(baseForOwn); - - /** - * The base implementation of `_.forEachRight` without support for iteratee shorthands. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array|Object} Returns `collection`. - */ - var baseEachRight = createBaseEach(baseForOwnRight, true); - - /** - * The base implementation of `_.every` without support for iteratee shorthands. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if all elements pass the predicate check, - * else `false` - */ - function baseEvery(collection, predicate) { - var result = true; - baseEach(collection, function(value, index, collection) { - result = !!predicate(value, index, collection); - return result; - }); - return result; - } - - /** - * The base implementation of methods like `_.max` and `_.min` which accepts a - * `comparator` to determine the extremum value. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The iteratee invoked per iteration. - * @param {Function} comparator The comparator used to compare values. - * @returns {*} Returns the extremum value. - */ - function baseExtremum(array, iteratee, comparator) { - var index = -1, - length = array.length; - - while (++index < length) { - var value = array[index], - current = iteratee(value); - - if (current != null && (computed === undefined - ? (current === current && !isSymbol(current)) - : comparator(current, computed) - )) { - var computed = current, - result = value; - } - } - return result; - } - - /** - * The base implementation of `_.fill` without an iteratee call guard. - * - * @private - * @param {Array} array The array to fill. - * @param {*} value The value to fill `array` with. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns `array`. - */ - function baseFill(array, value, start, end) { - var length = array.length; - - start = toInteger(start); - if (start < 0) { - start = -start > length ? 0 : (length + start); - } - end = (end === undefined || end > length) ? length : toInteger(end); - if (end < 0) { - end += length; - } - end = start > end ? 0 : toLength(end); - while (start < end) { - array[start++] = value; - } - return array; - } - - /** - * The base implementation of `_.filter` without support for iteratee shorthands. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - */ - function baseFilter(collection, predicate) { - var result = []; - baseEach(collection, function(value, index, collection) { - if (predicate(value, index, collection)) { - result.push(value); - } - }); - return result; - } - - /** - * The base implementation of `_.flatten` with support for restricting flattening. - * - * @private - * @param {Array} array The array to flatten. - * @param {number} depth The maximum recursion depth. - * @param {boolean} [predicate=isFlattenable] The function invoked per iteration. - * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks. - * @param {Array} [result=[]] The initial result value. - * @returns {Array} Returns the new flattened array. - */ - function baseFlatten(array, depth, predicate, isStrict, result) { - var index = -1, - length = array.length; - - predicate || (predicate = isFlattenable); - result || (result = []); - - while (++index < length) { - var value = array[index]; - if (depth > 0 && predicate(value)) { - if (depth > 1) { - // Recursively flatten arrays (susceptible to call stack limits). - baseFlatten(value, depth - 1, predicate, isStrict, result); - } else { - arrayPush(result, value); - } - } else if (!isStrict) { - result[result.length] = value; - } - } - return result; - } - - /** - * The base implementation of `baseForOwn` which iterates over `object` - * properties returned by `keysFunc` and invokes `iteratee` for each property. - * Iteratee functions may exit iteration early by explicitly returning `false`. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {Function} keysFunc The function to get the keys of `object`. - * @returns {Object} Returns `object`. - */ - var baseFor = createBaseFor(); - - /** - * This function is like `baseFor` except that it iterates over properties - * in the opposite order. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {Function} keysFunc The function to get the keys of `object`. - * @returns {Object} Returns `object`. - */ - var baseForRight = createBaseFor(true); - - /** - * The base implementation of `_.forOwn` without support for iteratee shorthands. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Object} Returns `object`. - */ - function baseForOwn(object, iteratee) { - return object && baseFor(object, iteratee, keys); - } - - /** - * The base implementation of `_.forOwnRight` without support for iteratee shorthands. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Object} Returns `object`. - */ - function baseForOwnRight(object, iteratee) { - return object && baseForRight(object, iteratee, keys); - } - - /** - * The base implementation of `_.functions` which creates an array of - * `object` function property names filtered from `props`. - * - * @private - * @param {Object} object The object to inspect. - * @param {Array} props The property names to filter. - * @returns {Array} Returns the function names. - */ - function baseFunctions(object, props) { - return arrayFilter(props, function(key) { - return isFunction(object[key]); - }); - } - - /** - * The base implementation of `_.get` without support for default values. - * - * @private - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to get. - * @returns {*} Returns the resolved value. - */ - function baseGet(object, path) { - path = castPath(path, object); - - var index = 0, - length = path.length; - - while (object != null && index < length) { - object = object[toKey(path[index++])]; - } - return (index && index == length) ? object : undefined; - } - - /** - * The base implementation of `getAllKeys` and `getAllKeysIn` which uses - * `keysFunc` and `symbolsFunc` to get the enumerable property names and - * symbols of `object`. - * - * @private - * @param {Object} object The object to query. - * @param {Function} keysFunc The function to get the keys of `object`. - * @param {Function} symbolsFunc The function to get the symbols of `object`. - * @returns {Array} Returns the array of property names and symbols. - */ - function baseGetAllKeys(object, keysFunc, symbolsFunc) { - var result = keysFunc(object); - return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); - } - - /** - * The base implementation of `getTag` without fallbacks for buggy environments. - * - * @private - * @param {*} value The value to query. - * @returns {string} Returns the `toStringTag`. - */ - function baseGetTag(value) { - if (value == null) { - return value === undefined ? undefinedTag : nullTag; - } - return (symToStringTag && symToStringTag in Object(value)) - ? getRawTag(value) - : objectToString(value); - } - - /** - * The base implementation of `_.gt` which doesn't coerce arguments. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is greater than `other`, - * else `false`. - */ - function baseGt(value, other) { - return value > other; - } - - /** - * The base implementation of `_.has` without support for deep paths. - * - * @private - * @param {Object} [object] The object to query. - * @param {Array|string} key The key to check. - * @returns {boolean} Returns `true` if `key` exists, else `false`. - */ - function baseHas(object, key) { - return object != null && hasOwnProperty.call(object, key); - } - - /** - * The base implementation of `_.hasIn` without support for deep paths. - * - * @private - * @param {Object} [object] The object to query. - * @param {Array|string} key The key to check. - * @returns {boolean} Returns `true` if `key` exists, else `false`. - */ - function baseHasIn(object, key) { - return object != null && key in Object(object); - } - - /** - * The base implementation of `_.inRange` which doesn't coerce arguments. - * - * @private - * @param {number} number The number to check. - * @param {number} start The start of the range. - * @param {number} end The end of the range. - * @returns {boolean} Returns `true` if `number` is in the range, else `false`. - */ - function baseInRange(number, start, end) { - return number >= nativeMin(start, end) && number < nativeMax(start, end); - } - - /** - * The base implementation of methods like `_.intersection`, without support - * for iteratee shorthands, that accepts an array of arrays to inspect. - * - * @private - * @param {Array} arrays The arrays to inspect. - * @param {Function} [iteratee] The iteratee invoked per element. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of shared values. - */ - function baseIntersection(arrays, iteratee, comparator) { - var includes = comparator ? arrayIncludesWith : arrayIncludes, - length = arrays[0].length, - othLength = arrays.length, - othIndex = othLength, - caches = Array(othLength), - maxLength = Infinity, - result = []; - - while (othIndex--) { - var array = arrays[othIndex]; - if (othIndex && iteratee) { - array = arrayMap(array, baseUnary(iteratee)); - } - maxLength = nativeMin(array.length, maxLength); - caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120)) - ? new SetCache(othIndex && array) - : undefined; - } - array = arrays[0]; - - var index = -1, - seen = caches[0]; - - outer: - while (++index < length && result.length < maxLength) { - var value = array[index], - computed = iteratee ? iteratee(value) : value; - - value = (comparator || value !== 0) ? value : 0; - if (!(seen - ? cacheHas(seen, computed) - : includes(result, computed, comparator) - )) { - othIndex = othLength; - while (--othIndex) { - var cache = caches[othIndex]; - if (!(cache - ? cacheHas(cache, computed) - : includes(arrays[othIndex], computed, comparator)) - ) { - continue outer; - } - } - if (seen) { - seen.push(computed); - } - result.push(value); - } - } - return result; - } - - /** - * The base implementation of `_.invert` and `_.invertBy` which inverts - * `object` with values transformed by `iteratee` and set by `setter`. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} setter The function to set `accumulator` values. - * @param {Function} iteratee The iteratee to transform values. - * @param {Object} accumulator The initial inverted object. - * @returns {Function} Returns `accumulator`. - */ - function baseInverter(object, setter, iteratee, accumulator) { - baseForOwn(object, function(value, key, object) { - setter(accumulator, iteratee(value), key, object); - }); - return accumulator; - } - - /** - * The base implementation of `_.invoke` without support for individual - * method arguments. - * - * @private - * @param {Object} object The object to query. - * @param {Array|string} path The path of the method to invoke. - * @param {Array} args The arguments to invoke the method with. - * @returns {*} Returns the result of the invoked method. - */ - function baseInvoke(object, path, args) { - path = castPath(path, object); - object = parent(object, path); - var func = object == null ? object : object[toKey(last(path))]; - return func == null ? undefined : apply(func, object, args); - } - - /** - * The base implementation of `_.isArguments`. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an `arguments` object, - */ - function baseIsArguments(value) { - return isObjectLike(value) && baseGetTag(value) == argsTag; - } - - /** - * The base implementation of `_.isArrayBuffer` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. - */ - function baseIsArrayBuffer(value) { - return isObjectLike(value) && baseGetTag(value) == arrayBufferTag; - } - - /** - * The base implementation of `_.isDate` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a date object, else `false`. - */ - function baseIsDate(value) { - return isObjectLike(value) && baseGetTag(value) == dateTag; - } - - /** - * The base implementation of `_.isEqual` which supports partial comparisons - * and tracks traversed objects. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @param {boolean} bitmask The bitmask flags. - * 1 - Unordered comparison - * 2 - Partial comparison - * @param {Function} [customizer] The function to customize comparisons. - * @param {Object} [stack] Tracks traversed `value` and `other` objects. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - */ - function baseIsEqual(value, other, bitmask, customizer, stack) { - if (value === other) { - return true; - } - if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) { - return value !== value && other !== other; - } - return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack); - } - - /** - * A specialized version of `baseIsEqual` for arrays and objects which performs - * deep comparisons and tracks traversed objects enabling objects with circular - * references to be compared. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} [stack] Tracks traversed `object` and `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) { - var objIsArr = isArray(object), - othIsArr = isArray(other), - objTag = objIsArr ? arrayTag : getTag(object), - othTag = othIsArr ? arrayTag : getTag(other); - - objTag = objTag == argsTag ? objectTag : objTag; - othTag = othTag == argsTag ? objectTag : othTag; - - var objIsObj = objTag == objectTag, - othIsObj = othTag == objectTag, - isSameTag = objTag == othTag; - - if (isSameTag && isBuffer(object)) { - if (!isBuffer(other)) { - return false; - } - objIsArr = true; - objIsObj = false; - } - if (isSameTag && !objIsObj) { - stack || (stack = new Stack); - return (objIsArr || isTypedArray(object)) - ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) - : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack); - } - if (!(bitmask & COMPARE_PARTIAL_FLAG)) { - var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), - othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); - - if (objIsWrapped || othIsWrapped) { - var objUnwrapped = objIsWrapped ? object.value() : object, - othUnwrapped = othIsWrapped ? other.value() : other; - - stack || (stack = new Stack); - return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack); - } - } - if (!isSameTag) { - return false; - } - stack || (stack = new Stack); - return equalObjects(object, other, bitmask, customizer, equalFunc, stack); - } - - /** - * The base implementation of `_.isMap` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a map, else `false`. - */ - function baseIsMap(value) { - return isObjectLike(value) && getTag(value) == mapTag; - } - - /** - * The base implementation of `_.isMatch` without support for iteratee shorthands. - * - * @private - * @param {Object} object The object to inspect. - * @param {Object} source The object of property values to match. - * @param {Array} matchData The property names, values, and compare flags to match. - * @param {Function} [customizer] The function to customize comparisons. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - */ - function baseIsMatch(object, source, matchData, customizer) { - var index = matchData.length, - length = index, - noCustomizer = !customizer; - - if (object == null) { - return !length; - } - object = Object(object); - while (index--) { - var data = matchData[index]; - if ((noCustomizer && data[2]) - ? data[1] !== object[data[0]] - : !(data[0] in object) - ) { - return false; - } - } - while (++index < length) { - data = matchData[index]; - var key = data[0], - objValue = object[key], - srcValue = data[1]; - - if (noCustomizer && data[2]) { - if (objValue === undefined && !(key in object)) { - return false; - } - } else { - var stack = new Stack; - if (customizer) { - var result = customizer(objValue, srcValue, key, object, source, stack); - } - if (!(result === undefined - ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack) - : result - )) { - return false; - } - } - } - return true; - } - - /** - * The base implementation of `_.isNative` without bad shim checks. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a native function, - * else `false`. - */ - function baseIsNative(value) { - if (!isObject(value) || isMasked(value)) { - return false; - } - var pattern = isFunction(value) ? reIsNative : reIsHostCtor; - return pattern.test(toSource(value)); - } - - /** - * The base implementation of `_.isRegExp` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. - */ - function baseIsRegExp(value) { - return isObjectLike(value) && baseGetTag(value) == regexpTag; - } - - /** - * The base implementation of `_.isSet` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a set, else `false`. - */ - function baseIsSet(value) { - return isObjectLike(value) && getTag(value) == setTag; - } - - /** - * The base implementation of `_.isTypedArray` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. - */ - function baseIsTypedArray(value) { - return isObjectLike(value) && - isLength(value.length) && !!typedArrayTags[baseGetTag(value)]; - } - - /** - * The base implementation of `_.iteratee`. - * - * @private - * @param {*} [value=_.identity] The value to convert to an iteratee. - * @returns {Function} Returns the iteratee. - */ - function baseIteratee(value) { - // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. - // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. - if (typeof value == 'function') { - return value; - } - if (value == null) { - return identity; - } - if (typeof value == 'object') { - return isArray(value) - ? baseMatchesProperty(value[0], value[1]) - : baseMatches(value); - } - return property(value); - } - - /** - * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - */ - function baseKeys(object) { - if (!isPrototype(object)) { - return nativeKeys(object); - } - var result = []; - for (var key in Object(object)) { - if (hasOwnProperty.call(object, key) && key != 'constructor') { - result.push(key); - } - } - return result; - } - - /** - * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - */ - function baseKeysIn(object) { - if (!isObject(object)) { - return nativeKeysIn(object); - } - var isProto = isPrototype(object), - result = []; - - for (var key in object) { - if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { - result.push(key); - } - } - return result; - } - - /** - * The base implementation of `_.lt` which doesn't coerce arguments. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is less than `other`, - * else `false`. - */ - function baseLt(value, other) { - return value < other; - } - - /** - * The base implementation of `_.map` without support for iteratee shorthands. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the new mapped array. - */ - function baseMap(collection, iteratee) { - var index = -1, - result = isArrayLike(collection) ? Array(collection.length) : []; - - baseEach(collection, function(value, key, collection) { - result[++index] = iteratee(value, key, collection); - }); - return result; - } - - /** - * The base implementation of `_.matches` which doesn't clone `source`. - * - * @private - * @param {Object} source The object of property values to match. - * @returns {Function} Returns the new spec function. - */ - function baseMatches(source) { - var matchData = getMatchData(source); - if (matchData.length == 1 && matchData[0][2]) { - return matchesStrictComparable(matchData[0][0], matchData[0][1]); - } - return function(object) { - return object === source || baseIsMatch(object, source, matchData); - }; - } - - /** - * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`. - * - * @private - * @param {string} path The path of the property to get. - * @param {*} srcValue The value to match. - * @returns {Function} Returns the new spec function. - */ - function baseMatchesProperty(path, srcValue) { - if (isKey(path) && isStrictComparable(srcValue)) { - return matchesStrictComparable(toKey(path), srcValue); - } - return function(object) { - var objValue = get(object, path); - return (objValue === undefined && objValue === srcValue) - ? hasIn(object, path) - : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG); - }; - } - - /** - * The base implementation of `_.merge` without support for multiple sources. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @param {number} srcIndex The index of `source`. - * @param {Function} [customizer] The function to customize merged values. - * @param {Object} [stack] Tracks traversed source values and their merged - * counterparts. - */ - function baseMerge(object, source, srcIndex, customizer, stack) { - if (object === source) { - return; - } - baseFor(source, function(srcValue, key) { - if (isObject(srcValue)) { - stack || (stack = new Stack); - baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack); - } - else { - var newValue = customizer - ? customizer(safeGet(object, key), srcValue, (key + ''), object, source, stack) - : undefined; - - if (newValue === undefined) { - newValue = srcValue; - } - assignMergeValue(object, key, newValue); - } - }, keysIn); - } - - /** - * A specialized version of `baseMerge` for arrays and objects which performs - * deep merges and tracks traversed objects enabling objects with circular - * references to be merged. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @param {string} key The key of the value to merge. - * @param {number} srcIndex The index of `source`. - * @param {Function} mergeFunc The function to merge values. - * @param {Function} [customizer] The function to customize assigned values. - * @param {Object} [stack] Tracks traversed source values and their merged - * counterparts. - */ - function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) { - var objValue = safeGet(object, key), - srcValue = safeGet(source, key), - stacked = stack.get(srcValue); - - if (stacked) { - assignMergeValue(object, key, stacked); - return; - } - var newValue = customizer - ? customizer(objValue, srcValue, (key + ''), object, source, stack) - : undefined; - - var isCommon = newValue === undefined; - - if (isCommon) { - var isArr = isArray(srcValue), - isBuff = !isArr && isBuffer(srcValue), - isTyped = !isArr && !isBuff && isTypedArray(srcValue); - - newValue = srcValue; - if (isArr || isBuff || isTyped) { - if (isArray(objValue)) { - newValue = objValue; - } - else if (isArrayLikeObject(objValue)) { - newValue = copyArray(objValue); - } - else if (isBuff) { - isCommon = false; - newValue = cloneBuffer(srcValue, true); - } - else if (isTyped) { - isCommon = false; - newValue = cloneTypedArray(srcValue, true); - } - else { - newValue = []; - } - } - else if (isPlainObject(srcValue) || isArguments(srcValue)) { - newValue = objValue; - if (isArguments(objValue)) { - newValue = toPlainObject(objValue); - } - else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) { - newValue = initCloneObject(srcValue); - } - } - else { - isCommon = false; - } - } - if (isCommon) { - // Recursively merge objects and arrays (susceptible to call stack limits). - stack.set(srcValue, newValue); - mergeFunc(newValue, srcValue, srcIndex, customizer, stack); - stack['delete'](srcValue); - } - assignMergeValue(object, key, newValue); - } - - /** - * The base implementation of `_.nth` which doesn't coerce arguments. - * - * @private - * @param {Array} array The array to query. - * @param {number} n The index of the element to return. - * @returns {*} Returns the nth element of `array`. - */ - function baseNth(array, n) { - var length = array.length; - if (!length) { - return; - } - n += n < 0 ? length : 0; - return isIndex(n, length) ? array[n] : undefined; - } - - /** - * The base implementation of `_.orderBy` without param guards. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. - * @param {string[]} orders The sort orders of `iteratees`. - * @returns {Array} Returns the new sorted array. - */ - function baseOrderBy(collection, iteratees, orders) { - var index = -1; - iteratees = arrayMap(iteratees.length ? iteratees : [identity], baseUnary(getIteratee())); - - var result = baseMap(collection, function(value, key, collection) { - var criteria = arrayMap(iteratees, function(iteratee) { - return iteratee(value); - }); - return { 'criteria': criteria, 'index': ++index, 'value': value }; - }); - - return baseSortBy(result, function(object, other) { - return compareMultiple(object, other, orders); - }); - } - - /** - * The base implementation of `_.pick` without support for individual - * property identifiers. - * - * @private - * @param {Object} object The source object. - * @param {string[]} paths The property paths to pick. - * @returns {Object} Returns the new object. - */ - function basePick(object, paths) { - return basePickBy(object, paths, function(value, path) { - return hasIn(object, path); - }); - } - - /** - * The base implementation of `_.pickBy` without support for iteratee shorthands. - * - * @private - * @param {Object} object The source object. - * @param {string[]} paths The property paths to pick. - * @param {Function} predicate The function invoked per property. - * @returns {Object} Returns the new object. - */ - function basePickBy(object, paths, predicate) { - var index = -1, - length = paths.length, - result = {}; - - while (++index < length) { - var path = paths[index], - value = baseGet(object, path); - - if (predicate(value, path)) { - baseSet(result, castPath(path, object), value); - } - } - return result; - } - - /** - * A specialized version of `baseProperty` which supports deep paths. - * - * @private - * @param {Array|string} path The path of the property to get. - * @returns {Function} Returns the new accessor function. - */ - function basePropertyDeep(path) { - return function(object) { - return baseGet(object, path); - }; - } - - /** - * The base implementation of `_.pullAllBy` without support for iteratee - * shorthands. - * - * @private - * @param {Array} array The array to modify. - * @param {Array} values The values to remove. - * @param {Function} [iteratee] The iteratee invoked per element. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns `array`. - */ - function basePullAll(array, values, iteratee, comparator) { - var indexOf = comparator ? baseIndexOfWith : baseIndexOf, - index = -1, - length = values.length, - seen = array; - - if (array === values) { - values = copyArray(values); - } - if (iteratee) { - seen = arrayMap(array, baseUnary(iteratee)); - } - while (++index < length) { - var fromIndex = 0, - value = values[index], - computed = iteratee ? iteratee(value) : value; - - while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) { - if (seen !== array) { - splice.call(seen, fromIndex, 1); - } - splice.call(array, fromIndex, 1); - } - } - return array; - } - - /** - * The base implementation of `_.pullAt` without support for individual - * indexes or capturing the removed elements. - * - * @private - * @param {Array} array The array to modify. - * @param {number[]} indexes The indexes of elements to remove. - * @returns {Array} Returns `array`. - */ - function basePullAt(array, indexes) { - var length = array ? indexes.length : 0, - lastIndex = length - 1; - - while (length--) { - var index = indexes[length]; - if (length == lastIndex || index !== previous) { - var previous = index; - if (isIndex(index)) { - splice.call(array, index, 1); - } else { - baseUnset(array, index); - } - } - } - return array; - } - - /** - * The base implementation of `_.random` without support for returning - * floating-point numbers. - * - * @private - * @param {number} lower The lower bound. - * @param {number} upper The upper bound. - * @returns {number} Returns the random number. - */ - function baseRandom(lower, upper) { - return lower + nativeFloor(nativeRandom() * (upper - lower + 1)); - } - - /** - * The base implementation of `_.range` and `_.rangeRight` which doesn't - * coerce arguments. - * - * @private - * @param {number} start The start of the range. - * @param {number} end The end of the range. - * @param {number} step The value to increment or decrement by. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Array} Returns the range of numbers. - */ - function baseRange(start, end, step, fromRight) { - var index = -1, - length = nativeMax(nativeCeil((end - start) / (step || 1)), 0), - result = Array(length); - - while (length--) { - result[fromRight ? length : ++index] = start; - start += step; - } - return result; - } - - /** - * The base implementation of `_.repeat` which doesn't coerce arguments. - * - * @private - * @param {string} string The string to repeat. - * @param {number} n The number of times to repeat the string. - * @returns {string} Returns the repeated string. - */ - function baseRepeat(string, n) { - var result = ''; - if (!string || n < 1 || n > MAX_SAFE_INTEGER) { - return result; - } - // Leverage the exponentiation by squaring algorithm for a faster repeat. - // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. - do { - if (n % 2) { - result += string; - } - n = nativeFloor(n / 2); - if (n) { - string += string; - } - } while (n); - - return result; - } - - /** - * The base implementation of `_.rest` which doesn't validate or coerce arguments. - * - * @private - * @param {Function} func The function to apply a rest parameter to. - * @param {number} [start=func.length-1] The start position of the rest parameter. - * @returns {Function} Returns the new function. - */ - function baseRest(func, start) { - return setToString(overRest(func, start, identity), func + ''); - } - - /** - * The base implementation of `_.sample`. - * - * @private - * @param {Array|Object} collection The collection to sample. - * @returns {*} Returns the random element. - */ - function baseSample(collection) { - return arraySample(values(collection)); - } - - /** - * The base implementation of `_.sampleSize` without param guards. - * - * @private - * @param {Array|Object} collection The collection to sample. - * @param {number} n The number of elements to sample. - * @returns {Array} Returns the random elements. - */ - function baseSampleSize(collection, n) { - var array = values(collection); - return shuffleSelf(array, baseClamp(n, 0, array.length)); - } - - /** - * The base implementation of `_.set`. - * - * @private - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {*} value The value to set. - * @param {Function} [customizer] The function to customize path creation. - * @returns {Object} Returns `object`. - */ - function baseSet(object, path, value, customizer) { - if (!isObject(object)) { - return object; - } - path = castPath(path, object); - - var index = -1, - length = path.length, - lastIndex = length - 1, - nested = object; - - while (nested != null && ++index < length) { - var key = toKey(path[index]), - newValue = value; - - if (index != lastIndex) { - var objValue = nested[key]; - newValue = customizer ? customizer(objValue, key, nested) : undefined; - if (newValue === undefined) { - newValue = isObject(objValue) - ? objValue - : (isIndex(path[index + 1]) ? [] : {}); - } - } - assignValue(nested, key, newValue); - nested = nested[key]; - } - return object; - } - - /** - * The base implementation of `setData` without support for hot loop shorting. - * - * @private - * @param {Function} func The function to associate metadata with. - * @param {*} data The metadata. - * @returns {Function} Returns `func`. - */ - var baseSetData = !metaMap ? identity : function(func, data) { - metaMap.set(func, data); - return func; - }; - - /** - * The base implementation of `setToString` without support for hot loop shorting. - * - * @private - * @param {Function} func The function to modify. - * @param {Function} string The `toString` result. - * @returns {Function} Returns `func`. - */ - var baseSetToString = !defineProperty ? identity : function(func, string) { - return defineProperty(func, 'toString', { - 'configurable': true, - 'enumerable': false, - 'value': constant(string), - 'writable': true - }); - }; - - /** - * The base implementation of `_.shuffle`. - * - * @private - * @param {Array|Object} collection The collection to shuffle. - * @returns {Array} Returns the new shuffled array. - */ - function baseShuffle(collection) { - return shuffleSelf(values(collection)); - } - - /** - * The base implementation of `_.slice` without an iteratee call guard. - * - * @private - * @param {Array} array The array to slice. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns the slice of `array`. - */ - function baseSlice(array, start, end) { - var index = -1, - length = array.length; - - if (start < 0) { - start = -start > length ? 0 : (length + start); - } - end = end > length ? length : end; - if (end < 0) { - end += length; - } - length = start > end ? 0 : ((end - start) >>> 0); - start >>>= 0; - - var result = Array(length); - while (++index < length) { - result[index] = array[index + start]; - } - return result; - } - - /** - * The base implementation of `_.some` without support for iteratee shorthands. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - */ - function baseSome(collection, predicate) { - var result; - - baseEach(collection, function(value, index, collection) { - result = predicate(value, index, collection); - return !result; - }); - return !!result; - } - - /** - * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which - * performs a binary search of `array` to determine the index at which `value` - * should be inserted into `array` in order to maintain its sort order. - * - * @private - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {boolean} [retHighest] Specify returning the highest qualified index. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - */ - function baseSortedIndex(array, value, retHighest) { - var low = 0, - high = array == null ? low : array.length; - - if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) { - while (low < high) { - var mid = (low + high) >>> 1, - computed = array[mid]; - - if (computed !== null && !isSymbol(computed) && - (retHighest ? (computed <= value) : (computed < value))) { - low = mid + 1; - } else { - high = mid; - } - } - return high; - } - return baseSortedIndexBy(array, value, identity, retHighest); - } - - /** - * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy` - * which invokes `iteratee` for `value` and each element of `array` to compute - * their sort ranking. The iteratee is invoked with one argument; (value). - * - * @private - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {Function} iteratee The iteratee invoked per element. - * @param {boolean} [retHighest] Specify returning the highest qualified index. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - */ - function baseSortedIndexBy(array, value, iteratee, retHighest) { - value = iteratee(value); - - var low = 0, - high = array == null ? 0 : array.length, - valIsNaN = value !== value, - valIsNull = value === null, - valIsSymbol = isSymbol(value), - valIsUndefined = value === undefined; - - while (low < high) { - var mid = nativeFloor((low + high) / 2), - computed = iteratee(array[mid]), - othIsDefined = computed !== undefined, - othIsNull = computed === null, - othIsReflexive = computed === computed, - othIsSymbol = isSymbol(computed); - - if (valIsNaN) { - var setLow = retHighest || othIsReflexive; - } else if (valIsUndefined) { - setLow = othIsReflexive && (retHighest || othIsDefined); - } else if (valIsNull) { - setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull); - } else if (valIsSymbol) { - setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol); - } else if (othIsNull || othIsSymbol) { - setLow = false; - } else { - setLow = retHighest ? (computed <= value) : (computed < value); - } - if (setLow) { - low = mid + 1; - } else { - high = mid; - } - } - return nativeMin(high, MAX_ARRAY_INDEX); - } - - /** - * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without - * support for iteratee shorthands. - * - * @private - * @param {Array} array The array to inspect. - * @param {Function} [iteratee] The iteratee invoked per element. - * @returns {Array} Returns the new duplicate free array. - */ - function baseSortedUniq(array, iteratee) { - var index = -1, - length = array.length, - resIndex = 0, - result = []; - - while (++index < length) { - var value = array[index], - computed = iteratee ? iteratee(value) : value; - - if (!index || !eq(computed, seen)) { - var seen = computed; - result[resIndex++] = value === 0 ? 0 : value; - } - } - return result; - } - - /** - * The base implementation of `_.toNumber` which doesn't ensure correct - * conversions of binary, hexadecimal, or octal string values. - * - * @private - * @param {*} value The value to process. - * @returns {number} Returns the number. - */ - function baseToNumber(value) { - if (typeof value == 'number') { - return value; - } - if (isSymbol(value)) { - return NAN; - } - return +value; - } - - /** - * The base implementation of `_.toString` which doesn't convert nullish - * values to empty strings. - * - * @private - * @param {*} value The value to process. - * @returns {string} Returns the string. - */ - function baseToString(value) { - // Exit early for strings to avoid a performance hit in some environments. - if (typeof value == 'string') { - return value; - } - if (isArray(value)) { - // Recursively convert values (susceptible to call stack limits). - return arrayMap(value, baseToString) + ''; - } - if (isSymbol(value)) { - return symbolToString ? symbolToString.call(value) : ''; - } - var result = (value + ''); - return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; - } - - /** - * The base implementation of `_.uniqBy` without support for iteratee shorthands. - * - * @private - * @param {Array} array The array to inspect. - * @param {Function} [iteratee] The iteratee invoked per element. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new duplicate free array. - */ - function baseUniq(array, iteratee, comparator) { - var index = -1, - includes = arrayIncludes, - length = array.length, - isCommon = true, - result = [], - seen = result; - - if (comparator) { - isCommon = false; - includes = arrayIncludesWith; - } - else if (length >= LARGE_ARRAY_SIZE) { - var set = iteratee ? null : createSet(array); - if (set) { - return setToArray(set); - } - isCommon = false; - includes = cacheHas; - seen = new SetCache; - } - else { - seen = iteratee ? [] : result; - } - outer: - while (++index < length) { - var value = array[index], - computed = iteratee ? iteratee(value) : value; - - value = (comparator || value !== 0) ? value : 0; - if (isCommon && computed === computed) { - var seenIndex = seen.length; - while (seenIndex--) { - if (seen[seenIndex] === computed) { - continue outer; - } - } - if (iteratee) { - seen.push(computed); - } - result.push(value); - } - else if (!includes(seen, computed, comparator)) { - if (seen !== result) { - seen.push(computed); - } - result.push(value); - } - } - return result; - } - - /** - * The base implementation of `_.unset`. - * - * @private - * @param {Object} object The object to modify. - * @param {Array|string} path The property path to unset. - * @returns {boolean} Returns `true` if the property is deleted, else `false`. - */ - function baseUnset(object, path) { - path = castPath(path, object); - object = parent(object, path); - return object == null || delete object[toKey(last(path))]; - } - - /** - * The base implementation of `_.update`. - * - * @private - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to update. - * @param {Function} updater The function to produce the updated value. - * @param {Function} [customizer] The function to customize path creation. - * @returns {Object} Returns `object`. - */ - function baseUpdate(object, path, updater, customizer) { - return baseSet(object, path, updater(baseGet(object, path)), customizer); - } - - /** - * The base implementation of methods like `_.dropWhile` and `_.takeWhile` - * without support for iteratee shorthands. - * - * @private - * @param {Array} array The array to query. - * @param {Function} predicate The function invoked per iteration. - * @param {boolean} [isDrop] Specify dropping elements instead of taking them. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Array} Returns the slice of `array`. - */ - function baseWhile(array, predicate, isDrop, fromRight) { - var length = array.length, - index = fromRight ? length : -1; - - while ((fromRight ? index-- : ++index < length) && - predicate(array[index], index, array)) {} - - return isDrop - ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length)) - : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index)); - } - - /** - * The base implementation of `wrapperValue` which returns the result of - * performing a sequence of actions on the unwrapped `value`, where each - * successive action is supplied the return value of the previous. - * - * @private - * @param {*} value The unwrapped value. - * @param {Array} actions Actions to perform to resolve the unwrapped value. - * @returns {*} Returns the resolved value. - */ - function baseWrapperValue(value, actions) { - var result = value; - if (result instanceof LazyWrapper) { - result = result.value(); - } - return arrayReduce(actions, function(result, action) { - return action.func.apply(action.thisArg, arrayPush([result], action.args)); - }, result); - } - - /** - * The base implementation of methods like `_.xor`, without support for - * iteratee shorthands, that accepts an array of arrays to inspect. - * - * @private - * @param {Array} arrays The arrays to inspect. - * @param {Function} [iteratee] The iteratee invoked per element. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of values. - */ - function baseXor(arrays, iteratee, comparator) { - var length = arrays.length; - if (length < 2) { - return length ? baseUniq(arrays[0]) : []; - } - var index = -1, - result = Array(length); - - while (++index < length) { - var array = arrays[index], - othIndex = -1; - - while (++othIndex < length) { - if (othIndex != index) { - result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator); - } - } - } - return baseUniq(baseFlatten(result, 1), iteratee, comparator); - } - - /** - * This base implementation of `_.zipObject` which assigns values using `assignFunc`. - * - * @private - * @param {Array} props The property identifiers. - * @param {Array} values The property values. - * @param {Function} assignFunc The function to assign values. - * @returns {Object} Returns the new object. - */ - function baseZipObject(props, values, assignFunc) { - var index = -1, - length = props.length, - valsLength = values.length, - result = {}; - - while (++index < length) { - var value = index < valsLength ? values[index] : undefined; - assignFunc(result, props[index], value); - } - return result; - } - - /** - * Casts `value` to an empty array if it's not an array like object. - * - * @private - * @param {*} value The value to inspect. - * @returns {Array|Object} Returns the cast array-like object. - */ - function castArrayLikeObject(value) { - return isArrayLikeObject(value) ? value : []; - } - - /** - * Casts `value` to `identity` if it's not a function. - * - * @private - * @param {*} value The value to inspect. - * @returns {Function} Returns cast function. - */ - function castFunction(value) { - return typeof value == 'function' ? value : identity; - } - - /** - * Casts `value` to a path array if it's not one. - * - * @private - * @param {*} value The value to inspect. - * @param {Object} [object] The object to query keys on. - * @returns {Array} Returns the cast property path array. - */ - function castPath(value, object) { - if (isArray(value)) { - return value; - } - return isKey(value, object) ? [value] : stringToPath(toString(value)); - } - - /** - * A `baseRest` alias which can be replaced with `identity` by module - * replacement plugins. - * - * @private - * @type {Function} - * @param {Function} func The function to apply a rest parameter to. - * @returns {Function} Returns the new function. - */ - var castRest = baseRest; - - /** - * Casts `array` to a slice if it's needed. - * - * @private - * @param {Array} array The array to inspect. - * @param {number} start The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns the cast slice. - */ - function castSlice(array, start, end) { - var length = array.length; - end = end === undefined ? length : end; - return (!start && end >= length) ? array : baseSlice(array, start, end); - } - - /** - * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout). - * - * @private - * @param {number|Object} id The timer id or timeout object of the timer to clear. - */ - var clearTimeout = ctxClearTimeout || function(id) { - return root.clearTimeout(id); - }; - - /** - * Creates a clone of `buffer`. - * - * @private - * @param {Buffer} buffer The buffer to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Buffer} Returns the cloned buffer. - */ - function cloneBuffer(buffer, isDeep) { - if (isDeep) { - return buffer.slice(); - } - var length = buffer.length, - result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length); - - buffer.copy(result); - return result; - } - - /** - * Creates a clone of `arrayBuffer`. - * - * @private - * @param {ArrayBuffer} arrayBuffer The array buffer to clone. - * @returns {ArrayBuffer} Returns the cloned array buffer. - */ - function cloneArrayBuffer(arrayBuffer) { - var result = new arrayBuffer.constructor(arrayBuffer.byteLength); - new Uint8Array(result).set(new Uint8Array(arrayBuffer)); - return result; - } - - /** - * Creates a clone of `dataView`. - * - * @private - * @param {Object} dataView The data view to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the cloned data view. - */ - function cloneDataView(dataView, isDeep) { - var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer; - return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength); - } - - /** - * Creates a clone of `regexp`. - * - * @private - * @param {Object} regexp The regexp to clone. - * @returns {Object} Returns the cloned regexp. - */ - function cloneRegExp(regexp) { - var result = new regexp.constructor(regexp.source, reFlags.exec(regexp)); - result.lastIndex = regexp.lastIndex; - return result; - } - - /** - * Creates a clone of the `symbol` object. - * - * @private - * @param {Object} symbol The symbol object to clone. - * @returns {Object} Returns the cloned symbol object. - */ - function cloneSymbol(symbol) { - return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {}; - } - - /** - * Creates a clone of `typedArray`. - * - * @private - * @param {Object} typedArray The typed array to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the cloned typed array. - */ - function cloneTypedArray(typedArray, isDeep) { - var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; - return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); - } - - /** - * Compares values to sort them in ascending order. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {number} Returns the sort order indicator for `value`. - */ - function compareAscending(value, other) { - if (value !== other) { - var valIsDefined = value !== undefined, - valIsNull = value === null, - valIsReflexive = value === value, - valIsSymbol = isSymbol(value); - - var othIsDefined = other !== undefined, - othIsNull = other === null, - othIsReflexive = other === other, - othIsSymbol = isSymbol(other); - - if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) || - (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) || - (valIsNull && othIsDefined && othIsReflexive) || - (!valIsDefined && othIsReflexive) || - !valIsReflexive) { - return 1; - } - if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) || - (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) || - (othIsNull && valIsDefined && valIsReflexive) || - (!othIsDefined && valIsReflexive) || - !othIsReflexive) { - return -1; - } - } - return 0; - } - - /** - * Used by `_.orderBy` to compare multiple properties of a value to another - * and stable sort them. - * - * If `orders` is unspecified, all values are sorted in ascending order. Otherwise, - * specify an order of "desc" for descending or "asc" for ascending sort order - * of corresponding values. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {boolean[]|string[]} orders The order to sort by for each property. - * @returns {number} Returns the sort order indicator for `object`. - */ - function compareMultiple(object, other, orders) { - var index = -1, - objCriteria = object.criteria, - othCriteria = other.criteria, - length = objCriteria.length, - ordersLength = orders.length; - - while (++index < length) { - var result = compareAscending(objCriteria[index], othCriteria[index]); - if (result) { - if (index >= ordersLength) { - return result; - } - var order = orders[index]; - return result * (order == 'desc' ? -1 : 1); - } - } - // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications - // that causes it, under certain circumstances, to provide the same value for - // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 - // for more details. - // - // This also ensures a stable sort in V8 and other engines. - // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details. - return object.index - other.index; - } - - /** - * Creates an array that is the composition of partially applied arguments, - * placeholders, and provided arguments into a single array of arguments. - * - * @private - * @param {Array} args The provided arguments. - * @param {Array} partials The arguments to prepend to those provided. - * @param {Array} holders The `partials` placeholder indexes. - * @params {boolean} [isCurried] Specify composing for a curried function. - * @returns {Array} Returns the new array of composed arguments. - */ - function composeArgs(args, partials, holders, isCurried) { - var argsIndex = -1, - argsLength = args.length, - holdersLength = holders.length, - leftIndex = -1, - leftLength = partials.length, - rangeLength = nativeMax(argsLength - holdersLength, 0), - result = Array(leftLength + rangeLength), - isUncurried = !isCurried; - - while (++leftIndex < leftLength) { - result[leftIndex] = partials[leftIndex]; - } - while (++argsIndex < holdersLength) { - if (isUncurried || argsIndex < argsLength) { - result[holders[argsIndex]] = args[argsIndex]; - } - } - while (rangeLength--) { - result[leftIndex++] = args[argsIndex++]; - } - return result; - } - - /** - * This function is like `composeArgs` except that the arguments composition - * is tailored for `_.partialRight`. - * - * @private - * @param {Array} args The provided arguments. - * @param {Array} partials The arguments to append to those provided. - * @param {Array} holders The `partials` placeholder indexes. - * @params {boolean} [isCurried] Specify composing for a curried function. - * @returns {Array} Returns the new array of composed arguments. - */ - function composeArgsRight(args, partials, holders, isCurried) { - var argsIndex = -1, - argsLength = args.length, - holdersIndex = -1, - holdersLength = holders.length, - rightIndex = -1, - rightLength = partials.length, - rangeLength = nativeMax(argsLength - holdersLength, 0), - result = Array(rangeLength + rightLength), - isUncurried = !isCurried; - - while (++argsIndex < rangeLength) { - result[argsIndex] = args[argsIndex]; - } - var offset = argsIndex; - while (++rightIndex < rightLength) { - result[offset + rightIndex] = partials[rightIndex]; - } - while (++holdersIndex < holdersLength) { - if (isUncurried || argsIndex < argsLength) { - result[offset + holders[holdersIndex]] = args[argsIndex++]; - } - } - return result; - } - - /** - * Copies the values of `source` to `array`. - * - * @private - * @param {Array} source The array to copy values from. - * @param {Array} [array=[]] The array to copy values to. - * @returns {Array} Returns `array`. - */ - function copyArray(source, array) { - var index = -1, - length = source.length; - - array || (array = Array(length)); - while (++index < length) { - array[index] = source[index]; - } - return array; - } - - /** - * Copies properties of `source` to `object`. - * - * @private - * @param {Object} source The object to copy properties from. - * @param {Array} props The property identifiers to copy. - * @param {Object} [object={}] The object to copy properties to. - * @param {Function} [customizer] The function to customize copied values. - * @returns {Object} Returns `object`. - */ - function copyObject(source, props, object, customizer) { - var isNew = !object; - object || (object = {}); - - var index = -1, - length = props.length; - - while (++index < length) { - var key = props[index]; - - var newValue = customizer - ? customizer(object[key], source[key], key, object, source) - : undefined; - - if (newValue === undefined) { - newValue = source[key]; - } - if (isNew) { - baseAssignValue(object, key, newValue); - } else { - assignValue(object, key, newValue); - } - } - return object; - } - - /** - * Copies own symbols of `source` to `object`. - * - * @private - * @param {Object} source The object to copy symbols from. - * @param {Object} [object={}] The object to copy symbols to. - * @returns {Object} Returns `object`. - */ - function copySymbols(source, object) { - return copyObject(source, getSymbols(source), object); - } - - /** - * Copies own and inherited symbols of `source` to `object`. - * - * @private - * @param {Object} source The object to copy symbols from. - * @param {Object} [object={}] The object to copy symbols to. - * @returns {Object} Returns `object`. - */ - function copySymbolsIn(source, object) { - return copyObject(source, getSymbolsIn(source), object); - } - - /** - * Creates a function like `_.groupBy`. - * - * @private - * @param {Function} setter The function to set accumulator values. - * @param {Function} [initializer] The accumulator object initializer. - * @returns {Function} Returns the new aggregator function. - */ - function createAggregator(setter, initializer) { - return function(collection, iteratee) { - var func = isArray(collection) ? arrayAggregator : baseAggregator, - accumulator = initializer ? initializer() : {}; - - return func(collection, setter, getIteratee(iteratee, 2), accumulator); - }; - } - - /** - * Creates a function like `_.assign`. - * - * @private - * @param {Function} assigner The function to assign values. - * @returns {Function} Returns the new assigner function. - */ - function createAssigner(assigner) { - return baseRest(function(object, sources) { - var index = -1, - length = sources.length, - customizer = length > 1 ? sources[length - 1] : undefined, - guard = length > 2 ? sources[2] : undefined; - - customizer = (assigner.length > 3 && typeof customizer == 'function') - ? (length--, customizer) - : undefined; - - if (guard && isIterateeCall(sources[0], sources[1], guard)) { - customizer = length < 3 ? undefined : customizer; - length = 1; - } - object = Object(object); - while (++index < length) { - var source = sources[index]; - if (source) { - assigner(object, source, index, customizer); - } - } - return object; - }); - } - - /** - * Creates a `baseEach` or `baseEachRight` function. - * - * @private - * @param {Function} eachFunc The function to iterate over a collection. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new base function. - */ - function createBaseEach(eachFunc, fromRight) { - return function(collection, iteratee) { - if (collection == null) { - return collection; - } - if (!isArrayLike(collection)) { - return eachFunc(collection, iteratee); - } - var length = collection.length, - index = fromRight ? length : -1, - iterable = Object(collection); - - while ((fromRight ? index-- : ++index < length)) { - if (iteratee(iterable[index], index, iterable) === false) { - break; - } - } - return collection; - }; - } - - /** - * Creates a base function for methods like `_.forIn` and `_.forOwn`. - * - * @private - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new base function. - */ - function createBaseFor(fromRight) { - return function(object, iteratee, keysFunc) { - var index = -1, - iterable = Object(object), - props = keysFunc(object), - length = props.length; - - while (length--) { - var key = props[fromRight ? length : ++index]; - if (iteratee(iterable[key], key, iterable) === false) { - break; - } - } - return object; - }; - } - - /** - * Creates a function that wraps `func` to invoke it with the optional `this` - * binding of `thisArg`. - * - * @private - * @param {Function} func The function to wrap. - * @param {number} bitmask The bitmask flags. See `createWrap` for more details. - * @param {*} [thisArg] The `this` binding of `func`. - * @returns {Function} Returns the new wrapped function. - */ - function createBind(func, bitmask, thisArg) { - var isBind = bitmask & WRAP_BIND_FLAG, - Ctor = createCtor(func); - - function wrapper() { - var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; - return fn.apply(isBind ? thisArg : this, arguments); - } - return wrapper; - } - - /** - * Creates a function like `_.lowerFirst`. - * - * @private - * @param {string} methodName The name of the `String` case method to use. - * @returns {Function} Returns the new case function. - */ - function createCaseFirst(methodName) { - return function(string) { - string = toString(string); - - var strSymbols = hasUnicode(string) - ? stringToArray(string) - : undefined; - - var chr = strSymbols - ? strSymbols[0] - : string.charAt(0); - - var trailing = strSymbols - ? castSlice(strSymbols, 1).join('') - : string.slice(1); - - return chr[methodName]() + trailing; - }; - } - - /** - * Creates a function like `_.camelCase`. - * - * @private - * @param {Function} callback The function to combine each word. - * @returns {Function} Returns the new compounder function. - */ - function createCompounder(callback) { - return function(string) { - return arrayReduce(words(deburr(string).replace(reApos, '')), callback, ''); - }; - } - - /** - * Creates a function that produces an instance of `Ctor` regardless of - * whether it was invoked as part of a `new` expression or by `call` or `apply`. - * - * @private - * @param {Function} Ctor The constructor to wrap. - * @returns {Function} Returns the new wrapped function. - */ - function createCtor(Ctor) { - return function() { - // Use a `switch` statement to work with class constructors. See - // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist - // for more details. - var args = arguments; - switch (args.length) { - case 0: return new Ctor; - case 1: return new Ctor(args[0]); - case 2: return new Ctor(args[0], args[1]); - case 3: return new Ctor(args[0], args[1], args[2]); - case 4: return new Ctor(args[0], args[1], args[2], args[3]); - case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]); - case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]); - case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); - } - var thisBinding = baseCreate(Ctor.prototype), - result = Ctor.apply(thisBinding, args); - - // Mimic the constructor's `return` behavior. - // See https://es5.github.io/#x13.2.2 for more details. - return isObject(result) ? result : thisBinding; - }; - } - - /** - * Creates a function that wraps `func` to enable currying. - * - * @private - * @param {Function} func The function to wrap. - * @param {number} bitmask The bitmask flags. See `createWrap` for more details. - * @param {number} arity The arity of `func`. - * @returns {Function} Returns the new wrapped function. - */ - function createCurry(func, bitmask, arity) { - var Ctor = createCtor(func); - - function wrapper() { - var length = arguments.length, - args = Array(length), - index = length, - placeholder = getHolder(wrapper); - - while (index--) { - args[index] = arguments[index]; - } - var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder) - ? [] - : replaceHolders(args, placeholder); - - length -= holders.length; - if (length < arity) { - return createRecurry( - func, bitmask, createHybrid, wrapper.placeholder, undefined, - args, holders, undefined, undefined, arity - length); - } - var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; - return apply(fn, this, args); - } - return wrapper; - } - - /** - * Creates a `_.find` or `_.findLast` function. - * - * @private - * @param {Function} findIndexFunc The function to find the collection index. - * @returns {Function} Returns the new find function. - */ - function createFind(findIndexFunc) { - return function(collection, predicate, fromIndex) { - var iterable = Object(collection); - if (!isArrayLike(collection)) { - var iteratee = getIteratee(predicate, 3); - collection = keys(collection); - predicate = function(key) { return iteratee(iterable[key], key, iterable); }; - } - var index = findIndexFunc(collection, predicate, fromIndex); - return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined; - }; - } - - /** - * Creates a `_.flow` or `_.flowRight` function. - * - * @private - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new flow function. - */ - function createFlow(fromRight) { - return flatRest(function(funcs) { - var length = funcs.length, - index = length, - prereq = LodashWrapper.prototype.thru; - - if (fromRight) { - funcs.reverse(); - } - while (index--) { - var func = funcs[index]; - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - if (prereq && !wrapper && getFuncName(func) == 'wrapper') { - var wrapper = new LodashWrapper([], true); - } - } - index = wrapper ? index : length; - while (++index < length) { - func = funcs[index]; - - var funcName = getFuncName(func), - data = funcName == 'wrapper' ? getData(func) : undefined; - - if (data && isLaziable(data[0]) && - data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) && - !data[4].length && data[9] == 1 - ) { - wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]); - } else { - wrapper = (func.length == 1 && isLaziable(func)) - ? wrapper[funcName]() - : wrapper.thru(func); - } - } - return function() { - var args = arguments, - value = args[0]; - - if (wrapper && args.length == 1 && isArray(value)) { - return wrapper.plant(value).value(); - } - var index = 0, - result = length ? funcs[index].apply(this, args) : value; - - while (++index < length) { - result = funcs[index].call(this, result); - } - return result; - }; - }); - } - - /** - * Creates a function that wraps `func` to invoke it with optional `this` - * binding of `thisArg`, partial application, and currying. - * - * @private - * @param {Function|string} func The function or method name to wrap. - * @param {number} bitmask The bitmask flags. See `createWrap` for more details. - * @param {*} [thisArg] The `this` binding of `func`. - * @param {Array} [partials] The arguments to prepend to those provided to - * the new function. - * @param {Array} [holders] The `partials` placeholder indexes. - * @param {Array} [partialsRight] The arguments to append to those provided - * to the new function. - * @param {Array} [holdersRight] The `partialsRight` placeholder indexes. - * @param {Array} [argPos] The argument positions of the new function. - * @param {number} [ary] The arity cap of `func`. - * @param {number} [arity] The arity of `func`. - * @returns {Function} Returns the new wrapped function. - */ - function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) { - var isAry = bitmask & WRAP_ARY_FLAG, - isBind = bitmask & WRAP_BIND_FLAG, - isBindKey = bitmask & WRAP_BIND_KEY_FLAG, - isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG), - isFlip = bitmask & WRAP_FLIP_FLAG, - Ctor = isBindKey ? undefined : createCtor(func); - - function wrapper() { - var length = arguments.length, - args = Array(length), - index = length; - - while (index--) { - args[index] = arguments[index]; - } - if (isCurried) { - var placeholder = getHolder(wrapper), - holdersCount = countHolders(args, placeholder); - } - if (partials) { - args = composeArgs(args, partials, holders, isCurried); - } - if (partialsRight) { - args = composeArgsRight(args, partialsRight, holdersRight, isCurried); - } - length -= holdersCount; - if (isCurried && length < arity) { - var newHolders = replaceHolders(args, placeholder); - return createRecurry( - func, bitmask, createHybrid, wrapper.placeholder, thisArg, - args, newHolders, argPos, ary, arity - length - ); - } - var thisBinding = isBind ? thisArg : this, - fn = isBindKey ? thisBinding[func] : func; - - length = args.length; - if (argPos) { - args = reorder(args, argPos); - } else if (isFlip && length > 1) { - args.reverse(); - } - if (isAry && ary < length) { - args.length = ary; - } - if (this && this !== root && this instanceof wrapper) { - fn = Ctor || createCtor(fn); - } - return fn.apply(thisBinding, args); - } - return wrapper; - } - - /** - * Creates a function like `_.invertBy`. - * - * @private - * @param {Function} setter The function to set accumulator values. - * @param {Function} toIteratee The function to resolve iteratees. - * @returns {Function} Returns the new inverter function. - */ - function createInverter(setter, toIteratee) { - return function(object, iteratee) { - return baseInverter(object, setter, toIteratee(iteratee), {}); - }; - } - - /** - * Creates a function that performs a mathematical operation on two values. - * - * @private - * @param {Function} operator The function to perform the operation. - * @param {number} [defaultValue] The value used for `undefined` arguments. - * @returns {Function} Returns the new mathematical operation function. - */ - function createMathOperation(operator, defaultValue) { - return function(value, other) { - var result; - if (value === undefined && other === undefined) { - return defaultValue; - } - if (value !== undefined) { - result = value; - } - if (other !== undefined) { - if (result === undefined) { - return other; - } - if (typeof value == 'string' || typeof other == 'string') { - value = baseToString(value); - other = baseToString(other); - } else { - value = baseToNumber(value); - other = baseToNumber(other); - } - result = operator(value, other); - } - return result; - }; - } - - /** - * Creates a function like `_.over`. - * - * @private - * @param {Function} arrayFunc The function to iterate over iteratees. - * @returns {Function} Returns the new over function. - */ - function createOver(arrayFunc) { - return flatRest(function(iteratees) { - iteratees = arrayMap(iteratees, baseUnary(getIteratee())); - return baseRest(function(args) { - var thisArg = this; - return arrayFunc(iteratees, function(iteratee) { - return apply(iteratee, thisArg, args); - }); - }); - }); - } - - /** - * Creates the padding for `string` based on `length`. The `chars` string - * is truncated if the number of characters exceeds `length`. - * - * @private - * @param {number} length The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padding for `string`. - */ - function createPadding(length, chars) { - chars = chars === undefined ? ' ' : baseToString(chars); - - var charsLength = chars.length; - if (charsLength < 2) { - return charsLength ? baseRepeat(chars, length) : chars; - } - var result = baseRepeat(chars, nativeCeil(length / stringSize(chars))); - return hasUnicode(chars) - ? castSlice(stringToArray(result), 0, length).join('') - : result.slice(0, length); - } - - /** - * Creates a function that wraps `func` to invoke it with the `this` binding - * of `thisArg` and `partials` prepended to the arguments it receives. - * - * @private - * @param {Function} func The function to wrap. - * @param {number} bitmask The bitmask flags. See `createWrap` for more details. - * @param {*} thisArg The `this` binding of `func`. - * @param {Array} partials The arguments to prepend to those provided to - * the new function. - * @returns {Function} Returns the new wrapped function. - */ - function createPartial(func, bitmask, thisArg, partials) { - var isBind = bitmask & WRAP_BIND_FLAG, - Ctor = createCtor(func); - - function wrapper() { - var argsIndex = -1, - argsLength = arguments.length, - leftIndex = -1, - leftLength = partials.length, - args = Array(leftLength + argsLength), - fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; - - while (++leftIndex < leftLength) { - args[leftIndex] = partials[leftIndex]; - } - while (argsLength--) { - args[leftIndex++] = arguments[++argsIndex]; - } - return apply(fn, isBind ? thisArg : this, args); - } - return wrapper; - } - - /** - * Creates a `_.range` or `_.rangeRight` function. - * - * @private - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new range function. - */ - function createRange(fromRight) { - return function(start, end, step) { - if (step && typeof step != 'number' && isIterateeCall(start, end, step)) { - end = step = undefined; - } - // Ensure the sign of `-0` is preserved. - start = toFinite(start); - if (end === undefined) { - end = start; - start = 0; - } else { - end = toFinite(end); - } - step = step === undefined ? (start < end ? 1 : -1) : toFinite(step); - return baseRange(start, end, step, fromRight); - }; - } - - /** - * Creates a function that performs a relational operation on two values. - * - * @private - * @param {Function} operator The function to perform the operation. - * @returns {Function} Returns the new relational operation function. - */ - function createRelationalOperation(operator) { - return function(value, other) { - if (!(typeof value == 'string' && typeof other == 'string')) { - value = toNumber(value); - other = toNumber(other); - } - return operator(value, other); - }; - } - - /** - * Creates a function that wraps `func` to continue currying. - * - * @private - * @param {Function} func The function to wrap. - * @param {number} bitmask The bitmask flags. See `createWrap` for more details. - * @param {Function} wrapFunc The function to create the `func` wrapper. - * @param {*} placeholder The placeholder value. - * @param {*} [thisArg] The `this` binding of `func`. - * @param {Array} [partials] The arguments to prepend to those provided to - * the new function. - * @param {Array} [holders] The `partials` placeholder indexes. - * @param {Array} [argPos] The argument positions of the new function. - * @param {number} [ary] The arity cap of `func`. - * @param {number} [arity] The arity of `func`. - * @returns {Function} Returns the new wrapped function. - */ - function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) { - var isCurry = bitmask & WRAP_CURRY_FLAG, - newHolders = isCurry ? holders : undefined, - newHoldersRight = isCurry ? undefined : holders, - newPartials = isCurry ? partials : undefined, - newPartialsRight = isCurry ? undefined : partials; - - bitmask |= (isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG); - bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG); - - if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) { - bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG); - } - var newData = [ - func, bitmask, thisArg, newPartials, newHolders, newPartialsRight, - newHoldersRight, argPos, ary, arity - ]; - - var result = wrapFunc.apply(undefined, newData); - if (isLaziable(func)) { - setData(result, newData); - } - result.placeholder = placeholder; - return setWrapToString(result, func, bitmask); - } - - /** - * Creates a function like `_.round`. - * - * @private - * @param {string} methodName The name of the `Math` method to use when rounding. - * @returns {Function} Returns the new round function. - */ - function createRound(methodName) { - var func = Math[methodName]; - return function(number, precision) { - number = toNumber(number); - precision = precision == null ? 0 : nativeMin(toInteger(precision), 292); - if (precision) { - // Shift with exponential notation to avoid floating-point issues. - // See [MDN](https://mdn.io/round#Examples) for more details. - var pair = (toString(number) + 'e').split('e'), - value = func(pair[0] + 'e' + (+pair[1] + precision)); - - pair = (toString(value) + 'e').split('e'); - return +(pair[0] + 'e' + (+pair[1] - precision)); - } - return func(number); - }; - } - - /** - * Creates a set object of `values`. - * - * @private - * @param {Array} values The values to add to the set. - * @returns {Object} Returns the new set. - */ - var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) { - return new Set(values); - }; - - /** - * Creates a `_.toPairs` or `_.toPairsIn` function. - * - * @private - * @param {Function} keysFunc The function to get the keys of a given object. - * @returns {Function} Returns the new pairs function. - */ - function createToPairs(keysFunc) { - return function(object) { - var tag = getTag(object); - if (tag == mapTag) { - return mapToArray(object); - } - if (tag == setTag) { - return setToPairs(object); - } - return baseToPairs(object, keysFunc(object)); - }; - } - - /** - * Creates a function that either curries or invokes `func` with optional - * `this` binding and partially applied arguments. - * - * @private - * @param {Function|string} func The function or method name to wrap. - * @param {number} bitmask The bitmask flags. - * 1 - `_.bind` - * 2 - `_.bindKey` - * 4 - `_.curry` or `_.curryRight` of a bound function - * 8 - `_.curry` - * 16 - `_.curryRight` - * 32 - `_.partial` - * 64 - `_.partialRight` - * 128 - `_.rearg` - * 256 - `_.ary` - * 512 - `_.flip` - * @param {*} [thisArg] The `this` binding of `func`. - * @param {Array} [partials] The arguments to be partially applied. - * @param {Array} [holders] The `partials` placeholder indexes. - * @param {Array} [argPos] The argument positions of the new function. - * @param {number} [ary] The arity cap of `func`. - * @param {number} [arity] The arity of `func`. - * @returns {Function} Returns the new wrapped function. - */ - function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) { - var isBindKey = bitmask & WRAP_BIND_KEY_FLAG; - if (!isBindKey && typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - var length = partials ? partials.length : 0; - if (!length) { - bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG); - partials = holders = undefined; - } - ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0); - arity = arity === undefined ? arity : toInteger(arity); - length -= holders ? holders.length : 0; - - if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) { - var partialsRight = partials, - holdersRight = holders; - - partials = holders = undefined; - } - var data = isBindKey ? undefined : getData(func); - - var newData = [ - func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, - argPos, ary, arity - ]; - - if (data) { - mergeData(newData, data); - } - func = newData[0]; - bitmask = newData[1]; - thisArg = newData[2]; - partials = newData[3]; - holders = newData[4]; - arity = newData[9] = newData[9] === undefined - ? (isBindKey ? 0 : func.length) - : nativeMax(newData[9] - length, 0); - - if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) { - bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG); - } - if (!bitmask || bitmask == WRAP_BIND_FLAG) { - var result = createBind(func, bitmask, thisArg); - } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) { - result = createCurry(func, bitmask, arity); - } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) { - result = createPartial(func, bitmask, thisArg, partials); - } else { - result = createHybrid.apply(undefined, newData); - } - var setter = data ? baseSetData : setData; - return setWrapToString(setter(result, newData), func, bitmask); - } - - /** - * Used by `_.defaults` to customize its `_.assignIn` use to assign properties - * of source objects to the destination object for all destination properties - * that resolve to `undefined`. - * - * @private - * @param {*} objValue The destination value. - * @param {*} srcValue The source value. - * @param {string} key The key of the property to assign. - * @param {Object} object The parent object of `objValue`. - * @returns {*} Returns the value to assign. - */ - function customDefaultsAssignIn(objValue, srcValue, key, object) { - if (objValue === undefined || - (eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key))) { - return srcValue; - } - return objValue; - } - - /** - * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source - * objects into destination objects that are passed thru. - * - * @private - * @param {*} objValue The destination value. - * @param {*} srcValue The source value. - * @param {string} key The key of the property to merge. - * @param {Object} object The parent object of `objValue`. - * @param {Object} source The parent object of `srcValue`. - * @param {Object} [stack] Tracks traversed source values and their merged - * counterparts. - * @returns {*} Returns the value to assign. - */ - function customDefaultsMerge(objValue, srcValue, key, object, source, stack) { - if (isObject(objValue) && isObject(srcValue)) { - // Recursively merge objects and arrays (susceptible to call stack limits). - stack.set(srcValue, objValue); - baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack); - stack['delete'](srcValue); - } - return objValue; - } - - /** - * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain - * objects. - * - * @private - * @param {*} value The value to inspect. - * @param {string} key The key of the property to inspect. - * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`. - */ - function customOmitClone(value) { - return isPlainObject(value) ? undefined : value; - } - - /** - * A specialized version of `baseIsEqualDeep` for arrays with support for - * partial deep comparisons. - * - * @private - * @param {Array} array The array to compare. - * @param {Array} other The other array to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} stack Tracks traversed `array` and `other` objects. - * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. - */ - function equalArrays(array, other, bitmask, customizer, equalFunc, stack) { - var isPartial = bitmask & COMPARE_PARTIAL_FLAG, - arrLength = array.length, - othLength = other.length; - - if (arrLength != othLength && !(isPartial && othLength > arrLength)) { - return false; - } - // Assume cyclic values are equal. - var stacked = stack.get(array); - if (stacked && stack.get(other)) { - return stacked == other; - } - var index = -1, - result = true, - seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined; - - stack.set(array, other); - stack.set(other, array); - - // Ignore non-index properties. - while (++index < arrLength) { - var arrValue = array[index], - othValue = other[index]; - - if (customizer) { - var compared = isPartial - ? customizer(othValue, arrValue, index, other, array, stack) - : customizer(arrValue, othValue, index, array, other, stack); - } - if (compared !== undefined) { - if (compared) { - continue; - } - result = false; - break; - } - // Recursively compare arrays (susceptible to call stack limits). - if (seen) { - if (!arraySome(other, function(othValue, othIndex) { - if (!cacheHas(seen, othIndex) && - (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) { - return seen.push(othIndex); - } - })) { - result = false; - break; - } - } else if (!( - arrValue === othValue || - equalFunc(arrValue, othValue, bitmask, customizer, stack) - )) { - result = false; - break; - } - } - stack['delete'](array); - stack['delete'](other); - return result; - } - - /** - * A specialized version of `baseIsEqualDeep` for comparing objects of - * the same `toStringTag`. - * - * **Note:** This function only supports comparing values with tags of - * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {string} tag The `toStringTag` of the objects to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} stack Tracks traversed `object` and `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) { - switch (tag) { - case dataViewTag: - if ((object.byteLength != other.byteLength) || - (object.byteOffset != other.byteOffset)) { - return false; - } - object = object.buffer; - other = other.buffer; - - case arrayBufferTag: - if ((object.byteLength != other.byteLength) || - !equalFunc(new Uint8Array(object), new Uint8Array(other))) { - return false; - } - return true; - - case boolTag: - case dateTag: - case numberTag: - // Coerce booleans to `1` or `0` and dates to milliseconds. - // Invalid dates are coerced to `NaN`. - return eq(+object, +other); - - case errorTag: - return object.name == other.name && object.message == other.message; - - case regexpTag: - case stringTag: - // Coerce regexes to strings and treat strings, primitives and objects, - // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring - // for more details. - return object == (other + ''); - - case mapTag: - var convert = mapToArray; - - case setTag: - var isPartial = bitmask & COMPARE_PARTIAL_FLAG; - convert || (convert = setToArray); - - if (object.size != other.size && !isPartial) { - return false; - } - // Assume cyclic values are equal. - var stacked = stack.get(object); - if (stacked) { - return stacked == other; - } - bitmask |= COMPARE_UNORDERED_FLAG; - - // Recursively compare objects (susceptible to call stack limits). - stack.set(object, other); - var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack); - stack['delete'](object); - return result; - - case symbolTag: - if (symbolValueOf) { - return symbolValueOf.call(object) == symbolValueOf.call(other); - } - } - return false; - } - - /** - * A specialized version of `baseIsEqualDeep` for objects with support for - * partial deep comparisons. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} stack Tracks traversed `object` and `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function equalObjects(object, other, bitmask, customizer, equalFunc, stack) { - var isPartial = bitmask & COMPARE_PARTIAL_FLAG, - objProps = getAllKeys(object), - objLength = objProps.length, - othProps = getAllKeys(other), - othLength = othProps.length; - - if (objLength != othLength && !isPartial) { - return false; - } - var index = objLength; - while (index--) { - var key = objProps[index]; - if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { - return false; - } - } - // Assume cyclic values are equal. - var stacked = stack.get(object); - if (stacked && stack.get(other)) { - return stacked == other; - } - var result = true; - stack.set(object, other); - stack.set(other, object); - - var skipCtor = isPartial; - while (++index < objLength) { - key = objProps[index]; - var objValue = object[key], - othValue = other[key]; - - if (customizer) { - var compared = isPartial - ? customizer(othValue, objValue, key, other, object, stack) - : customizer(objValue, othValue, key, object, other, stack); - } - // Recursively compare objects (susceptible to call stack limits). - if (!(compared === undefined - ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack)) - : compared - )) { - result = false; - break; - } - skipCtor || (skipCtor = key == 'constructor'); - } - if (result && !skipCtor) { - var objCtor = object.constructor, - othCtor = other.constructor; - - // Non `Object` object instances with different constructors are not equal. - if (objCtor != othCtor && - ('constructor' in object && 'constructor' in other) && - !(typeof objCtor == 'function' && objCtor instanceof objCtor && - typeof othCtor == 'function' && othCtor instanceof othCtor)) { - result = false; - } - } - stack['delete'](object); - stack['delete'](other); - return result; - } - - /** - * A specialized version of `baseRest` which flattens the rest array. - * - * @private - * @param {Function} func The function to apply a rest parameter to. - * @returns {Function} Returns the new function. - */ - function flatRest(func) { - return setToString(overRest(func, undefined, flatten), func + ''); - } - - /** - * Creates an array of own enumerable property names and symbols of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names and symbols. - */ - function getAllKeys(object) { - return baseGetAllKeys(object, keys, getSymbols); - } - - /** - * Creates an array of own and inherited enumerable property names and - * symbols of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names and symbols. - */ - function getAllKeysIn(object) { - return baseGetAllKeys(object, keysIn, getSymbolsIn); - } - - /** - * Gets metadata for `func`. - * - * @private - * @param {Function} func The function to query. - * @returns {*} Returns the metadata for `func`. - */ - var getData = !metaMap ? noop : function(func) { - return metaMap.get(func); - }; - - /** - * Gets the name of `func`. - * - * @private - * @param {Function} func The function to query. - * @returns {string} Returns the function name. - */ - function getFuncName(func) { - var result = (func.name + ''), - array = realNames[result], - length = hasOwnProperty.call(realNames, result) ? array.length : 0; - - while (length--) { - var data = array[length], - otherFunc = data.func; - if (otherFunc == null || otherFunc == func) { - return data.name; - } - } - return result; - } - - /** - * Gets the argument placeholder value for `func`. - * - * @private - * @param {Function} func The function to inspect. - * @returns {*} Returns the placeholder value. - */ - function getHolder(func) { - var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func; - return object.placeholder; - } - - /** - * Gets the appropriate "iteratee" function. If `_.iteratee` is customized, - * this function returns the custom method, otherwise it returns `baseIteratee`. - * If arguments are provided, the chosen function is invoked with them and - * its result is returned. - * - * @private - * @param {*} [value] The value to convert to an iteratee. - * @param {number} [arity] The arity of the created iteratee. - * @returns {Function} Returns the chosen function or its result. - */ - function getIteratee() { - var result = lodash.iteratee || iteratee; - result = result === iteratee ? baseIteratee : result; - return arguments.length ? result(arguments[0], arguments[1]) : result; - } - - /** - * Gets the data for `map`. - * - * @private - * @param {Object} map The map to query. - * @param {string} key The reference key. - * @returns {*} Returns the map data. - */ - function getMapData(map, key) { - var data = map.__data__; - return isKeyable(key) - ? data[typeof key == 'string' ? 'string' : 'hash'] - : data.map; - } - - /** - * Gets the property names, values, and compare flags of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the match data of `object`. - */ - function getMatchData(object) { - var result = keys(object), - length = result.length; - - while (length--) { - var key = result[length], - value = object[key]; - - result[length] = [key, value, isStrictComparable(value)]; - } - return result; - } - - /** - * Gets the native function at `key` of `object`. - * - * @private - * @param {Object} object The object to query. - * @param {string} key The key of the method to get. - * @returns {*} Returns the function if it's native, else `undefined`. - */ - function getNative(object, key) { - var value = getValue(object, key); - return baseIsNative(value) ? value : undefined; - } - - /** - * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. - * - * @private - * @param {*} value The value to query. - * @returns {string} Returns the raw `toStringTag`. - */ - function getRawTag(value) { - var isOwn = hasOwnProperty.call(value, symToStringTag), - tag = value[symToStringTag]; - - try { - value[symToStringTag] = undefined; - var unmasked = true; - } catch (e) {} - - var result = nativeObjectToString.call(value); - if (unmasked) { - if (isOwn) { - value[symToStringTag] = tag; - } else { - delete value[symToStringTag]; - } - } - return result; - } - - /** - * Creates an array of the own enumerable symbols of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of symbols. - */ - var getSymbols = !nativeGetSymbols ? stubArray : function(object) { - if (object == null) { - return []; - } - object = Object(object); - return arrayFilter(nativeGetSymbols(object), function(symbol) { - return propertyIsEnumerable.call(object, symbol); - }); - }; - - /** - * Creates an array of the own and inherited enumerable symbols of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of symbols. - */ - var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) { - var result = []; - while (object) { - arrayPush(result, getSymbols(object)); - object = getPrototype(object); - } - return result; - }; - - /** - * Gets the `toStringTag` of `value`. - * - * @private - * @param {*} value The value to query. - * @returns {string} Returns the `toStringTag`. - */ - var getTag = baseGetTag; - - // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6. - if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || - (Map && getTag(new Map) != mapTag) || - (Promise && getTag(Promise.resolve()) != promiseTag) || - (Set && getTag(new Set) != setTag) || - (WeakMap && getTag(new WeakMap) != weakMapTag)) { - getTag = function(value) { - var result = baseGetTag(value), - Ctor = result == objectTag ? value.constructor : undefined, - ctorString = Ctor ? toSource(Ctor) : ''; - - if (ctorString) { - switch (ctorString) { - case dataViewCtorString: return dataViewTag; - case mapCtorString: return mapTag; - case promiseCtorString: return promiseTag; - case setCtorString: return setTag; - case weakMapCtorString: return weakMapTag; - } - } - return result; - }; - } - - /** - * Gets the view, applying any `transforms` to the `start` and `end` positions. - * - * @private - * @param {number} start The start of the view. - * @param {number} end The end of the view. - * @param {Array} transforms The transformations to apply to the view. - * @returns {Object} Returns an object containing the `start` and `end` - * positions of the view. - */ - function getView(start, end, transforms) { - var index = -1, - length = transforms.length; - - while (++index < length) { - var data = transforms[index], - size = data.size; - - switch (data.type) { - case 'drop': start += size; break; - case 'dropRight': end -= size; break; - case 'take': end = nativeMin(end, start + size); break; - case 'takeRight': start = nativeMax(start, end - size); break; - } - } - return { 'start': start, 'end': end }; - } - - /** - * Extracts wrapper details from the `source` body comment. - * - * @private - * @param {string} source The source to inspect. - * @returns {Array} Returns the wrapper details. - */ - function getWrapDetails(source) { - var match = source.match(reWrapDetails); - return match ? match[1].split(reSplitDetails) : []; - } - - /** - * Checks if `path` exists on `object`. - * - * @private - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @param {Function} hasFunc The function to check properties. - * @returns {boolean} Returns `true` if `path` exists, else `false`. - */ - function hasPath(object, path, hasFunc) { - path = castPath(path, object); - - var index = -1, - length = path.length, - result = false; - - while (++index < length) { - var key = toKey(path[index]); - if (!(result = object != null && hasFunc(object, key))) { - break; - } - object = object[key]; - } - if (result || ++index != length) { - return result; - } - length = object == null ? 0 : object.length; - return !!length && isLength(length) && isIndex(key, length) && - (isArray(object) || isArguments(object)); - } - - /** - * Initializes an array clone. - * - * @private - * @param {Array} array The array to clone. - * @returns {Array} Returns the initialized clone. - */ - function initCloneArray(array) { - var length = array.length, - result = new array.constructor(length); - - // Add properties assigned by `RegExp#exec`. - if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { - result.index = array.index; - result.input = array.input; - } - return result; - } - - /** - * Initializes an object clone. - * - * @private - * @param {Object} object The object to clone. - * @returns {Object} Returns the initialized clone. - */ - function initCloneObject(object) { - return (typeof object.constructor == 'function' && !isPrototype(object)) - ? baseCreate(getPrototype(object)) - : {}; - } - - /** - * Initializes an object clone based on its `toStringTag`. - * - * **Note:** This function only supports cloning values with tags of - * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`. - * - * @private - * @param {Object} object The object to clone. - * @param {string} tag The `toStringTag` of the object to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the initialized clone. - */ - function initCloneByTag(object, tag, isDeep) { - var Ctor = object.constructor; - switch (tag) { - case arrayBufferTag: - return cloneArrayBuffer(object); - - case boolTag: - case dateTag: - return new Ctor(+object); - - case dataViewTag: - return cloneDataView(object, isDeep); - - case float32Tag: case float64Tag: - case int8Tag: case int16Tag: case int32Tag: - case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: - return cloneTypedArray(object, isDeep); - - case mapTag: - return new Ctor; - - case numberTag: - case stringTag: - return new Ctor(object); - - case regexpTag: - return cloneRegExp(object); - - case setTag: - return new Ctor; - - case symbolTag: - return cloneSymbol(object); - } - } - - /** - * Inserts wrapper `details` in a comment at the top of the `source` body. - * - * @private - * @param {string} source The source to modify. - * @returns {Array} details The details to insert. - * @returns {string} Returns the modified source. - */ - function insertWrapDetails(source, details) { - var length = details.length; - if (!length) { - return source; - } - var lastIndex = length - 1; - details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex]; - details = details.join(length > 2 ? ', ' : ' '); - return source.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n'); - } - - /** - * Checks if `value` is a flattenable `arguments` object or array. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is flattenable, else `false`. - */ - function isFlattenable(value) { - return isArray(value) || isArguments(value) || - !!(spreadableSymbol && value && value[spreadableSymbol]); - } - - /** - * Checks if `value` is a valid array-like index. - * - * @private - * @param {*} value The value to check. - * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. - * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. - */ - function isIndex(value, length) { - var type = typeof value; - length = length == null ? MAX_SAFE_INTEGER : length; - - return !!length && - (type == 'number' || - (type != 'symbol' && reIsUint.test(value))) && - (value > -1 && value % 1 == 0 && value < length); - } - - /** - * Checks if the given arguments are from an iteratee call. - * - * @private - * @param {*} value The potential iteratee value argument. - * @param {*} index The potential iteratee index or key argument. - * @param {*} object The potential iteratee object argument. - * @returns {boolean} Returns `true` if the arguments are from an iteratee call, - * else `false`. - */ - function isIterateeCall(value, index, object) { - if (!isObject(object)) { - return false; - } - var type = typeof index; - if (type == 'number' - ? (isArrayLike(object) && isIndex(index, object.length)) - : (type == 'string' && index in object) - ) { - return eq(object[index], value); - } - return false; - } - - /** - * Checks if `value` is a property name and not a property path. - * - * @private - * @param {*} value The value to check. - * @param {Object} [object] The object to query keys on. - * @returns {boolean} Returns `true` if `value` is a property name, else `false`. - */ - function isKey(value, object) { - if (isArray(value)) { - return false; - } - var type = typeof value; - if (type == 'number' || type == 'symbol' || type == 'boolean' || - value == null || isSymbol(value)) { - return true; - } - return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || - (object != null && value in Object(object)); - } - - /** - * Checks if `value` is suitable for use as unique object key. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is suitable, else `false`. - */ - function isKeyable(value) { - var type = typeof value; - return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') - ? (value !== '__proto__') - : (value === null); - } - - /** - * Checks if `func` has a lazy counterpart. - * - * @private - * @param {Function} func The function to check. - * @returns {boolean} Returns `true` if `func` has a lazy counterpart, - * else `false`. - */ - function isLaziable(func) { - var funcName = getFuncName(func), - other = lodash[funcName]; - - if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) { - return false; - } - if (func === other) { - return true; - } - var data = getData(other); - return !!data && func === data[0]; - } - - /** - * Checks if `func` has its source masked. - * - * @private - * @param {Function} func The function to check. - * @returns {boolean} Returns `true` if `func` is masked, else `false`. - */ - function isMasked(func) { - return !!maskSrcKey && (maskSrcKey in func); - } - - /** - * Checks if `func` is capable of being masked. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `func` is maskable, else `false`. - */ - var isMaskable = coreJsData ? isFunction : stubFalse; - - /** - * Checks if `value` is likely a prototype object. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. - */ - function isPrototype(value) { - var Ctor = value && value.constructor, - proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; - - return value === proto; - } - - /** - * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` if suitable for strict - * equality comparisons, else `false`. - */ - function isStrictComparable(value) { - return value === value && !isObject(value); - } - - /** - * A specialized version of `matchesProperty` for source values suitable - * for strict equality comparisons, i.e. `===`. - * - * @private - * @param {string} key The key of the property to get. - * @param {*} srcValue The value to match. - * @returns {Function} Returns the new spec function. - */ - function matchesStrictComparable(key, srcValue) { - return function(object) { - if (object == null) { - return false; - } - return object[key] === srcValue && - (srcValue !== undefined || (key in Object(object))); - }; - } - - /** - * A specialized version of `_.memoize` which clears the memoized function's - * cache when it exceeds `MAX_MEMOIZE_SIZE`. - * - * @private - * @param {Function} func The function to have its output memoized. - * @returns {Function} Returns the new memoized function. - */ - function memoizeCapped(func) { - var result = memoize(func, function(key) { - if (cache.size === MAX_MEMOIZE_SIZE) { - cache.clear(); - } - return key; - }); - - var cache = result.cache; - return result; - } - - /** - * Merges the function metadata of `source` into `data`. - * - * Merging metadata reduces the number of wrappers used to invoke a function. - * This is possible because methods like `_.bind`, `_.curry`, and `_.partial` - * may be applied regardless of execution order. Methods like `_.ary` and - * `_.rearg` modify function arguments, making the order in which they are - * executed important, preventing the merging of metadata. However, we make - * an exception for a safe combined case where curried functions have `_.ary` - * and or `_.rearg` applied. - * - * @private - * @param {Array} data The destination metadata. - * @param {Array} source The source metadata. - * @returns {Array} Returns `data`. - */ - function mergeData(data, source) { - var bitmask = data[1], - srcBitmask = source[1], - newBitmask = bitmask | srcBitmask, - isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG); - - var isCombo = - ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_CURRY_FLAG)) || - ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_REARG_FLAG) && (data[7].length <= source[8])) || - ((srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == WRAP_CURRY_FLAG)); - - // Exit early if metadata can't be merged. - if (!(isCommon || isCombo)) { - return data; - } - // Use source `thisArg` if available. - if (srcBitmask & WRAP_BIND_FLAG) { - data[2] = source[2]; - // Set when currying a bound function. - newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG; - } - // Compose partial arguments. - var value = source[3]; - if (value) { - var partials = data[3]; - data[3] = partials ? composeArgs(partials, value, source[4]) : value; - data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4]; - } - // Compose partial right arguments. - value = source[5]; - if (value) { - partials = data[5]; - data[5] = partials ? composeArgsRight(partials, value, source[6]) : value; - data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6]; - } - // Use source `argPos` if available. - value = source[7]; - if (value) { - data[7] = value; - } - // Use source `ary` if it's smaller. - if (srcBitmask & WRAP_ARY_FLAG) { - data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]); - } - // Use source `arity` if one is not provided. - if (data[9] == null) { - data[9] = source[9]; - } - // Use source `func` and merge bitmasks. - data[0] = source[0]; - data[1] = newBitmask; - - return data; - } - - /** - * This function is like - * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) - * except that it includes inherited enumerable properties. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - */ - function nativeKeysIn(object) { - var result = []; - if (object != null) { - for (var key in Object(object)) { - result.push(key); - } - } - return result; - } - - /** - * Converts `value` to a string using `Object.prototype.toString`. - * - * @private - * @param {*} value The value to convert. - * @returns {string} Returns the converted string. - */ - function objectToString(value) { - return nativeObjectToString.call(value); - } - - /** - * A specialized version of `baseRest` which transforms the rest array. - * - * @private - * @param {Function} func The function to apply a rest parameter to. - * @param {number} [start=func.length-1] The start position of the rest parameter. - * @param {Function} transform The rest array transform. - * @returns {Function} Returns the new function. - */ - function overRest(func, start, transform) { - start = nativeMax(start === undefined ? (func.length - 1) : start, 0); - return function() { - var args = arguments, - index = -1, - length = nativeMax(args.length - start, 0), - array = Array(length); - - while (++index < length) { - array[index] = args[start + index]; - } - index = -1; - var otherArgs = Array(start + 1); - while (++index < start) { - otherArgs[index] = args[index]; - } - otherArgs[start] = transform(array); - return apply(func, this, otherArgs); - }; - } - - /** - * Gets the parent value at `path` of `object`. - * - * @private - * @param {Object} object The object to query. - * @param {Array} path The path to get the parent value of. - * @returns {*} Returns the parent value. - */ - function parent(object, path) { - return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1)); - } - - /** - * Reorder `array` according to the specified indexes where the element at - * the first index is assigned as the first element, the element at - * the second index is assigned as the second element, and so on. - * - * @private - * @param {Array} array The array to reorder. - * @param {Array} indexes The arranged array indexes. - * @returns {Array} Returns `array`. - */ - function reorder(array, indexes) { - var arrLength = array.length, - length = nativeMin(indexes.length, arrLength), - oldArray = copyArray(array); - - while (length--) { - var index = indexes[length]; - array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined; - } - return array; - } - - /** - * Sets metadata for `func`. - * - * **Note:** If this function becomes hot, i.e. is invoked a lot in a short - * period of time, it will trip its breaker and transition to an identity - * function to avoid garbage collection pauses in V8. See - * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070) - * for more details. - * - * @private - * @param {Function} func The function to associate metadata with. - * @param {*} data The metadata. - * @returns {Function} Returns `func`. - */ - var setData = shortOut(baseSetData); - - /** - * A simple wrapper around the global [`setTimeout`](https://mdn.io/setTimeout). - * - * @private - * @param {Function} func The function to delay. - * @param {number} wait The number of milliseconds to delay invocation. - * @returns {number|Object} Returns the timer id or timeout object. - */ - var setTimeout = ctxSetTimeout || function(func, wait) { - return root.setTimeout(func, wait); - }; - - /** - * Sets the `toString` method of `func` to return `string`. - * - * @private - * @param {Function} func The function to modify. - * @param {Function} string The `toString` result. - * @returns {Function} Returns `func`. - */ - var setToString = shortOut(baseSetToString); - - /** - * Sets the `toString` method of `wrapper` to mimic the source of `reference` - * with wrapper details in a comment at the top of the source body. - * - * @private - * @param {Function} wrapper The function to modify. - * @param {Function} reference The reference function. - * @param {number} bitmask The bitmask flags. See `createWrap` for more details. - * @returns {Function} Returns `wrapper`. - */ - function setWrapToString(wrapper, reference, bitmask) { - var source = (reference + ''); - return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask))); - } - - /** - * Creates a function that'll short out and invoke `identity` instead - * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN` - * milliseconds. - * - * @private - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new shortable function. - */ - function shortOut(func) { - var count = 0, - lastCalled = 0; - - return function() { - var stamp = nativeNow(), - remaining = HOT_SPAN - (stamp - lastCalled); - - lastCalled = stamp; - if (remaining > 0) { - if (++count >= HOT_COUNT) { - return arguments[0]; - } - } else { - count = 0; - } - return func.apply(undefined, arguments); - }; - } - - /** - * A specialized version of `_.shuffle` which mutates and sets the size of `array`. - * - * @private - * @param {Array} array The array to shuffle. - * @param {number} [size=array.length] The size of `array`. - * @returns {Array} Returns `array`. - */ - function shuffleSelf(array, size) { - var index = -1, - length = array.length, - lastIndex = length - 1; - - size = size === undefined ? length : size; - while (++index < size) { - var rand = baseRandom(index, lastIndex), - value = array[rand]; - - array[rand] = array[index]; - array[index] = value; - } - array.length = size; - return array; - } - - /** - * Converts `string` to a property path array. - * - * @private - * @param {string} string The string to convert. - * @returns {Array} Returns the property path array. - */ - var stringToPath = memoizeCapped(function(string) { - var result = []; - if (string.charCodeAt(0) === 46 /* . */) { - result.push(''); - } - string.replace(rePropName, function(match, number, quote, subString) { - result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match)); - }); - return result; - }); - - /** - * Converts `value` to a string key if it's not a string or symbol. - * - * @private - * @param {*} value The value to inspect. - * @returns {string|symbol} Returns the key. - */ - function toKey(value) { - if (typeof value == 'string' || isSymbol(value)) { - return value; - } - var result = (value + ''); - return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; - } - - /** - * Converts `func` to its source code. - * - * @private - * @param {Function} func The function to convert. - * @returns {string} Returns the source code. - */ - function toSource(func) { - if (func != null) { - try { - return funcToString.call(func); - } catch (e) {} - try { - return (func + ''); - } catch (e) {} - } - return ''; - } - - /** - * Updates wrapper `details` based on `bitmask` flags. - * - * @private - * @returns {Array} details The details to modify. - * @param {number} bitmask The bitmask flags. See `createWrap` for more details. - * @returns {Array} Returns `details`. - */ - function updateWrapDetails(details, bitmask) { - arrayEach(wrapFlags, function(pair) { - var value = '_.' + pair[0]; - if ((bitmask & pair[1]) && !arrayIncludes(details, value)) { - details.push(value); - } - }); - return details.sort(); - } - - /** - * Creates a clone of `wrapper`. - * - * @private - * @param {Object} wrapper The wrapper to clone. - * @returns {Object} Returns the cloned wrapper. - */ - function wrapperClone(wrapper) { - if (wrapper instanceof LazyWrapper) { - return wrapper.clone(); - } - var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__); - result.__actions__ = copyArray(wrapper.__actions__); - result.__index__ = wrapper.__index__; - result.__values__ = wrapper.__values__; - return result; - } - - /*------------------------------------------------------------------------*/ - - /** - * Creates an array of elements split into groups the length of `size`. - * If `array` can't be split evenly, the final chunk will be the remaining - * elements. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to process. - * @param {number} [size=1] The length of each chunk - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Array} Returns the new array of chunks. - * @example - * - * _.chunk(['a', 'b', 'c', 'd'], 2); - * // => [['a', 'b'], ['c', 'd']] - * - * _.chunk(['a', 'b', 'c', 'd'], 3); - * // => [['a', 'b', 'c'], ['d']] - */ - function chunk(array, size, guard) { - if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) { - size = 1; - } else { - size = nativeMax(toInteger(size), 0); - } - var length = array == null ? 0 : array.length; - if (!length || size < 1) { - return []; - } - var index = 0, - resIndex = 0, - result = Array(nativeCeil(length / size)); - - while (index < length) { - result[resIndex++] = baseSlice(array, index, (index += size)); - } - return result; - } - - /** - * Creates an array with all falsey values removed. The values `false`, `null`, - * `0`, `""`, `undefined`, and `NaN` are falsey. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to compact. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * _.compact([0, 1, false, 2, '', 3]); - * // => [1, 2, 3] - */ - function compact(array) { - var index = -1, - length = array == null ? 0 : array.length, - resIndex = 0, - result = []; - - while (++index < length) { - var value = array[index]; - if (value) { - result[resIndex++] = value; - } - } - return result; - } - - /** - * Creates a new array concatenating `array` with any additional arrays - * and/or values. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to concatenate. - * @param {...*} [values] The values to concatenate. - * @returns {Array} Returns the new concatenated array. - * @example - * - * var array = [1]; - * var other = _.concat(array, 2, [3], [[4]]); - * - * console.log(other); - * // => [1, 2, 3, [4]] - * - * console.log(array); - * // => [1] - */ - function concat() { - var length = arguments.length; - if (!length) { - return []; - } - var args = Array(length - 1), - array = arguments[0], - index = length; - - while (index--) { - args[index - 1] = arguments[index]; - } - return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1)); - } - - /** - * Creates an array of `array` values not included in the other given arrays - * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. The order and references of result values are - * determined by the first array. - * - * **Note:** Unlike `_.pullAll`, this method returns a new array. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {...Array} [values] The values to exclude. - * @returns {Array} Returns the new array of filtered values. - * @see _.without, _.xor - * @example - * - * _.difference([2, 1], [2, 3]); - * // => [1] - */ - var difference = baseRest(function(array, values) { - return isArrayLikeObject(array) - ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) - : []; - }); - - /** - * This method is like `_.difference` except that it accepts `iteratee` which - * is invoked for each element of `array` and `values` to generate the criterion - * by which they're compared. The order and references of result values are - * determined by the first array. The iteratee is invoked with one argument: - * (value). - * - * **Note:** Unlike `_.pullAllBy`, this method returns a new array. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {...Array} [values] The values to exclude. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); - * // => [1.2] - * - * // The `_.property` iteratee shorthand. - * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x'); - * // => [{ 'x': 2 }] - */ - var differenceBy = baseRest(function(array, values) { - var iteratee = last(values); - if (isArrayLikeObject(iteratee)) { - iteratee = undefined; - } - return isArrayLikeObject(array) - ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)) - : []; - }); - - /** - * This method is like `_.difference` except that it accepts `comparator` - * which is invoked to compare elements of `array` to `values`. The order and - * references of result values are determined by the first array. The comparator - * is invoked with two arguments: (arrVal, othVal). - * - * **Note:** Unlike `_.pullAllWith`, this method returns a new array. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {...Array} [values] The values to exclude. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; - * - * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual); - * // => [{ 'x': 2, 'y': 1 }] - */ - var differenceWith = baseRest(function(array, values) { - var comparator = last(values); - if (isArrayLikeObject(comparator)) { - comparator = undefined; - } - return isArrayLikeObject(array) - ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator) - : []; - }); - - /** - * Creates a slice of `array` with `n` elements dropped from the beginning. - * - * @static - * @memberOf _ - * @since 0.5.0 - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to drop. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.drop([1, 2, 3]); - * // => [2, 3] - * - * _.drop([1, 2, 3], 2); - * // => [3] - * - * _.drop([1, 2, 3], 5); - * // => [] - * - * _.drop([1, 2, 3], 0); - * // => [1, 2, 3] - */ - function drop(array, n, guard) { - var length = array == null ? 0 : array.length; - if (!length) { - return []; - } - n = (guard || n === undefined) ? 1 : toInteger(n); - return baseSlice(array, n < 0 ? 0 : n, length); - } - - /** - * Creates a slice of `array` with `n` elements dropped from the end. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to drop. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.dropRight([1, 2, 3]); - * // => [1, 2] - * - * _.dropRight([1, 2, 3], 2); - * // => [1] - * - * _.dropRight([1, 2, 3], 5); - * // => [] - * - * _.dropRight([1, 2, 3], 0); - * // => [1, 2, 3] - */ - function dropRight(array, n, guard) { - var length = array == null ? 0 : array.length; - if (!length) { - return []; - } - n = (guard || n === undefined) ? 1 : toInteger(n); - n = length - n; - return baseSlice(array, 0, n < 0 ? 0 : n); - } - - /** - * Creates a slice of `array` excluding elements dropped from the end. - * Elements are dropped until `predicate` returns falsey. The predicate is - * invoked with three arguments: (value, index, array). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the slice of `array`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': false } - * ]; - * - * _.dropRightWhile(users, function(o) { return !o.active; }); - * // => objects for ['barney'] - * - * // The `_.matches` iteratee shorthand. - * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false }); - * // => objects for ['barney', 'fred'] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.dropRightWhile(users, ['active', false]); - * // => objects for ['barney'] - * - * // The `_.property` iteratee shorthand. - * _.dropRightWhile(users, 'active'); - * // => objects for ['barney', 'fred', 'pebbles'] - */ - function dropRightWhile(array, predicate) { - return (array && array.length) - ? baseWhile(array, getIteratee(predicate, 3), true, true) - : []; - } - - /** - * Creates a slice of `array` excluding elements dropped from the beginning. - * Elements are dropped until `predicate` returns falsey. The predicate is - * invoked with three arguments: (value, index, array). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the slice of `array`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': true } - * ]; - * - * _.dropWhile(users, function(o) { return !o.active; }); - * // => objects for ['pebbles'] - * - * // The `_.matches` iteratee shorthand. - * _.dropWhile(users, { 'user': 'barney', 'active': false }); - * // => objects for ['fred', 'pebbles'] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.dropWhile(users, ['active', false]); - * // => objects for ['pebbles'] - * - * // The `_.property` iteratee shorthand. - * _.dropWhile(users, 'active'); - * // => objects for ['barney', 'fred', 'pebbles'] - */ - function dropWhile(array, predicate) { - return (array && array.length) - ? baseWhile(array, getIteratee(predicate, 3), true) - : []; - } - - /** - * Fills elements of `array` with `value` from `start` up to, but not - * including, `end`. - * - * **Note:** This method mutates `array`. - * - * @static - * @memberOf _ - * @since 3.2.0 - * @category Array - * @param {Array} array The array to fill. - * @param {*} value The value to fill `array` with. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns `array`. - * @example - * - * var array = [1, 2, 3]; - * - * _.fill(array, 'a'); - * console.log(array); - * // => ['a', 'a', 'a'] - * - * _.fill(Array(3), 2); - * // => [2, 2, 2] - * - * _.fill([4, 6, 8, 10], '*', 1, 3); - * // => [4, '*', '*', 10] - */ - function fill(array, value, start, end) { - var length = array == null ? 0 : array.length; - if (!length) { - return []; - } - if (start && typeof start != 'number' && isIterateeCall(array, value, start)) { - start = 0; - end = length; - } - return baseFill(array, value, start, end); - } - - /** - * This method is like `_.find` except that it returns the index of the first - * element `predicate` returns truthy for instead of the element itself. - * - * @static - * @memberOf _ - * @since 1.1.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @param {number} [fromIndex=0] The index to search from. - * @returns {number} Returns the index of the found element, else `-1`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': true } - * ]; - * - * _.findIndex(users, function(o) { return o.user == 'barney'; }); - * // => 0 - * - * // The `_.matches` iteratee shorthand. - * _.findIndex(users, { 'user': 'fred', 'active': false }); - * // => 1 - * - * // The `_.matchesProperty` iteratee shorthand. - * _.findIndex(users, ['active', false]); - * // => 0 - * - * // The `_.property` iteratee shorthand. - * _.findIndex(users, 'active'); - * // => 2 - */ - function findIndex(array, predicate, fromIndex) { - var length = array == null ? 0 : array.length; - if (!length) { - return -1; - } - var index = fromIndex == null ? 0 : toInteger(fromIndex); - if (index < 0) { - index = nativeMax(length + index, 0); - } - return baseFindIndex(array, getIteratee(predicate, 3), index); - } - - /** - * This method is like `_.findIndex` except that it iterates over elements - * of `collection` from right to left. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @param {number} [fromIndex=array.length-1] The index to search from. - * @returns {number} Returns the index of the found element, else `-1`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': false } - * ]; - * - * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; }); - * // => 2 - * - * // The `_.matches` iteratee shorthand. - * _.findLastIndex(users, { 'user': 'barney', 'active': true }); - * // => 0 - * - * // The `_.matchesProperty` iteratee shorthand. - * _.findLastIndex(users, ['active', false]); - * // => 2 - * - * // The `_.property` iteratee shorthand. - * _.findLastIndex(users, 'active'); - * // => 0 - */ - function findLastIndex(array, predicate, fromIndex) { - var length = array == null ? 0 : array.length; - if (!length) { - return -1; - } - var index = length - 1; - if (fromIndex !== undefined) { - index = toInteger(fromIndex); - index = fromIndex < 0 - ? nativeMax(length + index, 0) - : nativeMin(index, length - 1); - } - return baseFindIndex(array, getIteratee(predicate, 3), index, true); - } - - /** - * Flattens `array` a single level deep. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to flatten. - * @returns {Array} Returns the new flattened array. - * @example - * - * _.flatten([1, [2, [3, [4]], 5]]); - * // => [1, 2, [3, [4]], 5] - */ - function flatten(array) { - var length = array == null ? 0 : array.length; - return length ? baseFlatten(array, 1) : []; - } - - /** - * Recursively flattens `array`. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to flatten. - * @returns {Array} Returns the new flattened array. - * @example - * - * _.flattenDeep([1, [2, [3, [4]], 5]]); - * // => [1, 2, 3, 4, 5] - */ - function flattenDeep(array) { - var length = array == null ? 0 : array.length; - return length ? baseFlatten(array, INFINITY) : []; - } - - /** - * Recursively flatten `array` up to `depth` times. - * - * @static - * @memberOf _ - * @since 4.4.0 - * @category Array - * @param {Array} array The array to flatten. - * @param {number} [depth=1] The maximum recursion depth. - * @returns {Array} Returns the new flattened array. - * @example - * - * var array = [1, [2, [3, [4]], 5]]; - * - * _.flattenDepth(array, 1); - * // => [1, 2, [3, [4]], 5] - * - * _.flattenDepth(array, 2); - * // => [1, 2, 3, [4], 5] - */ - function flattenDepth(array, depth) { - var length = array == null ? 0 : array.length; - if (!length) { - return []; - } - depth = depth === undefined ? 1 : toInteger(depth); - return baseFlatten(array, depth); - } - - /** - * The inverse of `_.toPairs`; this method returns an object composed - * from key-value `pairs`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} pairs The key-value pairs. - * @returns {Object} Returns the new object. - * @example - * - * _.fromPairs([['a', 1], ['b', 2]]); - * // => { 'a': 1, 'b': 2 } - */ - function fromPairs(pairs) { - var index = -1, - length = pairs == null ? 0 : pairs.length, - result = {}; - - while (++index < length) { - var pair = pairs[index]; - result[pair[0]] = pair[1]; - } - return result; - } - - /** - * Gets the first element of `array`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @alias first - * @category Array - * @param {Array} array The array to query. - * @returns {*} Returns the first element of `array`. - * @example - * - * _.head([1, 2, 3]); - * // => 1 - * - * _.head([]); - * // => undefined - */ - function head(array) { - return (array && array.length) ? array[0] : undefined; - } - - /** - * Gets the index at which the first occurrence of `value` is found in `array` - * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. If `fromIndex` is negative, it's used as the - * offset from the end of `array`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @param {number} [fromIndex=0] The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * _.indexOf([1, 2, 1, 2], 2); - * // => 1 - * - * // Search from the `fromIndex`. - * _.indexOf([1, 2, 1, 2], 2, 2); - * // => 3 - */ - function indexOf(array, value, fromIndex) { - var length = array == null ? 0 : array.length; - if (!length) { - return -1; - } - var index = fromIndex == null ? 0 : toInteger(fromIndex); - if (index < 0) { - index = nativeMax(length + index, 0); - } - return baseIndexOf(array, value, index); - } - - /** - * Gets all but the last element of `array`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to query. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.initial([1, 2, 3]); - * // => [1, 2] - */ - function initial(array) { - var length = array == null ? 0 : array.length; - return length ? baseSlice(array, 0, -1) : []; - } - - /** - * Creates an array of unique values that are included in all given arrays - * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. The order and references of result values are - * determined by the first array. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of intersecting values. - * @example - * - * _.intersection([2, 1], [2, 3]); - * // => [2] - */ - var intersection = baseRest(function(arrays) { - var mapped = arrayMap(arrays, castArrayLikeObject); - return (mapped.length && mapped[0] === arrays[0]) - ? baseIntersection(mapped) - : []; - }); - - /** - * This method is like `_.intersection` except that it accepts `iteratee` - * which is invoked for each element of each `arrays` to generate the criterion - * by which they're compared. The order and references of result values are - * determined by the first array. The iteratee is invoked with one argument: - * (value). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {Array} Returns the new array of intersecting values. - * @example - * - * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); - * // => [2.1] - * - * // The `_.property` iteratee shorthand. - * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); - * // => [{ 'x': 1 }] - */ - var intersectionBy = baseRest(function(arrays) { - var iteratee = last(arrays), - mapped = arrayMap(arrays, castArrayLikeObject); - - if (iteratee === last(mapped)) { - iteratee = undefined; - } else { - mapped.pop(); - } - return (mapped.length && mapped[0] === arrays[0]) - ? baseIntersection(mapped, getIteratee(iteratee, 2)) - : []; - }); - - /** - * This method is like `_.intersection` except that it accepts `comparator` - * which is invoked to compare elements of `arrays`. The order and references - * of result values are determined by the first array. The comparator is - * invoked with two arguments: (arrVal, othVal). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of intersecting values. - * @example - * - * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; - * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; - * - * _.intersectionWith(objects, others, _.isEqual); - * // => [{ 'x': 1, 'y': 2 }] - */ - var intersectionWith = baseRest(function(arrays) { - var comparator = last(arrays), - mapped = arrayMap(arrays, castArrayLikeObject); - - comparator = typeof comparator == 'function' ? comparator : undefined; - if (comparator) { - mapped.pop(); - } - return (mapped.length && mapped[0] === arrays[0]) - ? baseIntersection(mapped, undefined, comparator) - : []; - }); - - /** - * Converts all elements in `array` into a string separated by `separator`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to convert. - * @param {string} [separator=','] The element separator. - * @returns {string} Returns the joined string. - * @example - * - * _.join(['a', 'b', 'c'], '~'); - * // => 'a~b~c' - */ - function join(array, separator) { - return array == null ? '' : nativeJoin.call(array, separator); - } - - /** - * Gets the last element of `array`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to query. - * @returns {*} Returns the last element of `array`. - * @example - * - * _.last([1, 2, 3]); - * // => 3 - */ - function last(array) { - var length = array == null ? 0 : array.length; - return length ? array[length - 1] : undefined; - } - - /** - * This method is like `_.indexOf` except that it iterates over elements of - * `array` from right to left. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @param {number} [fromIndex=array.length-1] The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * _.lastIndexOf([1, 2, 1, 2], 2); - * // => 3 - * - * // Search from the `fromIndex`. - * _.lastIndexOf([1, 2, 1, 2], 2, 2); - * // => 1 - */ - function lastIndexOf(array, value, fromIndex) { - var length = array == null ? 0 : array.length; - if (!length) { - return -1; - } - var index = length; - if (fromIndex !== undefined) { - index = toInteger(fromIndex); - index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1); - } - return value === value - ? strictLastIndexOf(array, value, index) - : baseFindIndex(array, baseIsNaN, index, true); - } - - /** - * Gets the element at index `n` of `array`. If `n` is negative, the nth - * element from the end is returned. - * - * @static - * @memberOf _ - * @since 4.11.0 - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=0] The index of the element to return. - * @returns {*} Returns the nth element of `array`. - * @example - * - * var array = ['a', 'b', 'c', 'd']; - * - * _.nth(array, 1); - * // => 'b' - * - * _.nth(array, -2); - * // => 'c'; - */ - function nth(array, n) { - return (array && array.length) ? baseNth(array, toInteger(n)) : undefined; - } - - /** - * Removes all given values from `array` using - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. - * - * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove` - * to remove elements from an array by predicate. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Array - * @param {Array} array The array to modify. - * @param {...*} [values] The values to remove. - * @returns {Array} Returns `array`. - * @example - * - * var array = ['a', 'b', 'c', 'a', 'b', 'c']; - * - * _.pull(array, 'a', 'c'); - * console.log(array); - * // => ['b', 'b'] - */ - var pull = baseRest(pullAll); - - /** - * This method is like `_.pull` except that it accepts an array of values to remove. - * - * **Note:** Unlike `_.difference`, this method mutates `array`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to modify. - * @param {Array} values The values to remove. - * @returns {Array} Returns `array`. - * @example - * - * var array = ['a', 'b', 'c', 'a', 'b', 'c']; - * - * _.pullAll(array, ['a', 'c']); - * console.log(array); - * // => ['b', 'b'] - */ - function pullAll(array, values) { - return (array && array.length && values && values.length) - ? basePullAll(array, values) - : array; - } - - /** - * This method is like `_.pullAll` except that it accepts `iteratee` which is - * invoked for each element of `array` and `values` to generate the criterion - * by which they're compared. The iteratee is invoked with one argument: (value). - * - * **Note:** Unlike `_.differenceBy`, this method mutates `array`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to modify. - * @param {Array} values The values to remove. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {Array} Returns `array`. - * @example - * - * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }]; - * - * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x'); - * console.log(array); - * // => [{ 'x': 2 }] - */ - function pullAllBy(array, values, iteratee) { - return (array && array.length && values && values.length) - ? basePullAll(array, values, getIteratee(iteratee, 2)) - : array; - } - - /** - * This method is like `_.pullAll` except that it accepts `comparator` which - * is invoked to compare elements of `array` to `values`. The comparator is - * invoked with two arguments: (arrVal, othVal). - * - * **Note:** Unlike `_.differenceWith`, this method mutates `array`. - * - * @static - * @memberOf _ - * @since 4.6.0 - * @category Array - * @param {Array} array The array to modify. - * @param {Array} values The values to remove. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns `array`. - * @example - * - * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }]; - * - * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual); - * console.log(array); - * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }] - */ - function pullAllWith(array, values, comparator) { - return (array && array.length && values && values.length) - ? basePullAll(array, values, undefined, comparator) - : array; - } - - /** - * Removes elements from `array` corresponding to `indexes` and returns an - * array of removed elements. - * - * **Note:** Unlike `_.at`, this method mutates `array`. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to modify. - * @param {...(number|number[])} [indexes] The indexes of elements to remove. - * @returns {Array} Returns the new array of removed elements. - * @example - * - * var array = ['a', 'b', 'c', 'd']; - * var pulled = _.pullAt(array, [1, 3]); - * - * console.log(array); - * // => ['a', 'c'] - * - * console.log(pulled); - * // => ['b', 'd'] - */ - var pullAt = flatRest(function(array, indexes) { - var length = array == null ? 0 : array.length, - result = baseAt(array, indexes); - - basePullAt(array, arrayMap(indexes, function(index) { - return isIndex(index, length) ? +index : index; - }).sort(compareAscending)); - - return result; - }); - - /** - * Removes all elements from `array` that `predicate` returns truthy for - * and returns an array of the removed elements. The predicate is invoked - * with three arguments: (value, index, array). - * - * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull` - * to pull elements from an array by value. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Array - * @param {Array} array The array to modify. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the new array of removed elements. - * @example - * - * var array = [1, 2, 3, 4]; - * var evens = _.remove(array, function(n) { - * return n % 2 == 0; - * }); - * - * console.log(array); - * // => [1, 3] - * - * console.log(evens); - * // => [2, 4] - */ - function remove(array, predicate) { - var result = []; - if (!(array && array.length)) { - return result; - } - var index = -1, - indexes = [], - length = array.length; - - predicate = getIteratee(predicate, 3); - while (++index < length) { - var value = array[index]; - if (predicate(value, index, array)) { - result.push(value); - indexes.push(index); - } - } - basePullAt(array, indexes); - return result; - } - - /** - * Reverses `array` so that the first element becomes the last, the second - * element becomes the second to last, and so on. - * - * **Note:** This method mutates `array` and is based on - * [`Array#reverse`](https://mdn.io/Array/reverse). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to modify. - * @returns {Array} Returns `array`. - * @example - * - * var array = [1, 2, 3]; - * - * _.reverse(array); - * // => [3, 2, 1] - * - * console.log(array); - * // => [3, 2, 1] - */ - function reverse(array) { - return array == null ? array : nativeReverse.call(array); - } - - /** - * Creates a slice of `array` from `start` up to, but not including, `end`. - * - * **Note:** This method is used instead of - * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are - * returned. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to slice. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns the slice of `array`. - */ - function slice(array, start, end) { - var length = array == null ? 0 : array.length; - if (!length) { - return []; - } - if (end && typeof end != 'number' && isIterateeCall(array, start, end)) { - start = 0; - end = length; - } - else { - start = start == null ? 0 : toInteger(start); - end = end === undefined ? length : toInteger(end); - } - return baseSlice(array, start, end); - } - - /** - * Uses a binary search to determine the lowest index at which `value` - * should be inserted into `array` in order to maintain its sort order. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * _.sortedIndex([30, 50], 40); - * // => 1 - */ - function sortedIndex(array, value) { - return baseSortedIndex(array, value); - } - - /** - * This method is like `_.sortedIndex` except that it accepts `iteratee` - * which is invoked for `value` and each element of `array` to compute their - * sort ranking. The iteratee is invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * var objects = [{ 'x': 4 }, { 'x': 5 }]; - * - * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); - * // => 0 - * - * // The `_.property` iteratee shorthand. - * _.sortedIndexBy(objects, { 'x': 4 }, 'x'); - * // => 0 - */ - function sortedIndexBy(array, value, iteratee) { - return baseSortedIndexBy(array, value, getIteratee(iteratee, 2)); - } - - /** - * This method is like `_.indexOf` except that it performs a binary - * search on a sorted `array`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * _.sortedIndexOf([4, 5, 5, 5, 6], 5); - * // => 1 - */ - function sortedIndexOf(array, value) { - var length = array == null ? 0 : array.length; - if (length) { - var index = baseSortedIndex(array, value); - if (index < length && eq(array[index], value)) { - return index; - } - } - return -1; - } - - /** - * This method is like `_.sortedIndex` except that it returns the highest - * index at which `value` should be inserted into `array` in order to - * maintain its sort order. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * _.sortedLastIndex([4, 5, 5, 5, 6], 5); - * // => 4 - */ - function sortedLastIndex(array, value) { - return baseSortedIndex(array, value, true); - } - - /** - * This method is like `_.sortedLastIndex` except that it accepts `iteratee` - * which is invoked for `value` and each element of `array` to compute their - * sort ranking. The iteratee is invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * var objects = [{ 'x': 4 }, { 'x': 5 }]; - * - * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); - * // => 1 - * - * // The `_.property` iteratee shorthand. - * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x'); - * // => 1 - */ - function sortedLastIndexBy(array, value, iteratee) { - return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true); - } - - /** - * This method is like `_.lastIndexOf` except that it performs a binary - * search on a sorted `array`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5); - * // => 3 - */ - function sortedLastIndexOf(array, value) { - var length = array == null ? 0 : array.length; - if (length) { - var index = baseSortedIndex(array, value, true) - 1; - if (eq(array[index], value)) { - return index; - } - } - return -1; - } - - /** - * This method is like `_.uniq` except that it's designed and optimized - * for sorted arrays. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @returns {Array} Returns the new duplicate free array. - * @example - * - * _.sortedUniq([1, 1, 2]); - * // => [1, 2] - */ - function sortedUniq(array) { - return (array && array.length) - ? baseSortedUniq(array) - : []; - } - - /** - * This method is like `_.uniqBy` except that it's designed and optimized - * for sorted arrays. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {Function} [iteratee] The iteratee invoked per element. - * @returns {Array} Returns the new duplicate free array. - * @example - * - * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor); - * // => [1.1, 2.3] - */ - function sortedUniqBy(array, iteratee) { - return (array && array.length) - ? baseSortedUniq(array, getIteratee(iteratee, 2)) - : []; - } - - /** - * Gets all but the first element of `array`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to query. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.tail([1, 2, 3]); - * // => [2, 3] - */ - function tail(array) { - var length = array == null ? 0 : array.length; - return length ? baseSlice(array, 1, length) : []; - } - - /** - * Creates a slice of `array` with `n` elements taken from the beginning. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to take. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.take([1, 2, 3]); - * // => [1] - * - * _.take([1, 2, 3], 2); - * // => [1, 2] - * - * _.take([1, 2, 3], 5); - * // => [1, 2, 3] - * - * _.take([1, 2, 3], 0); - * // => [] - */ - function take(array, n, guard) { - if (!(array && array.length)) { - return []; - } - n = (guard || n === undefined) ? 1 : toInteger(n); - return baseSlice(array, 0, n < 0 ? 0 : n); - } - - /** - * Creates a slice of `array` with `n` elements taken from the end. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to take. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.takeRight([1, 2, 3]); - * // => [3] - * - * _.takeRight([1, 2, 3], 2); - * // => [2, 3] - * - * _.takeRight([1, 2, 3], 5); - * // => [1, 2, 3] - * - * _.takeRight([1, 2, 3], 0); - * // => [] - */ - function takeRight(array, n, guard) { - var length = array == null ? 0 : array.length; - if (!length) { - return []; - } - n = (guard || n === undefined) ? 1 : toInteger(n); - n = length - n; - return baseSlice(array, n < 0 ? 0 : n, length); - } - - /** - * Creates a slice of `array` with elements taken from the end. Elements are - * taken until `predicate` returns falsey. The predicate is invoked with - * three arguments: (value, index, array). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the slice of `array`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': false } - * ]; - * - * _.takeRightWhile(users, function(o) { return !o.active; }); - * // => objects for ['fred', 'pebbles'] - * - * // The `_.matches` iteratee shorthand. - * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false }); - * // => objects for ['pebbles'] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.takeRightWhile(users, ['active', false]); - * // => objects for ['fred', 'pebbles'] - * - * // The `_.property` iteratee shorthand. - * _.takeRightWhile(users, 'active'); - * // => [] - */ - function takeRightWhile(array, predicate) { - return (array && array.length) - ? baseWhile(array, getIteratee(predicate, 3), false, true) - : []; - } - - /** - * Creates a slice of `array` with elements taken from the beginning. Elements - * are taken until `predicate` returns falsey. The predicate is invoked with - * three arguments: (value, index, array). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the slice of `array`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': true } - * ]; - * - * _.takeWhile(users, function(o) { return !o.active; }); - * // => objects for ['barney', 'fred'] - * - * // The `_.matches` iteratee shorthand. - * _.takeWhile(users, { 'user': 'barney', 'active': false }); - * // => objects for ['barney'] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.takeWhile(users, ['active', false]); - * // => objects for ['barney', 'fred'] - * - * // The `_.property` iteratee shorthand. - * _.takeWhile(users, 'active'); - * // => [] - */ - function takeWhile(array, predicate) { - return (array && array.length) - ? baseWhile(array, getIteratee(predicate, 3)) - : []; - } - - /** - * Creates an array of unique values, in order, from all given arrays using - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of combined values. - * @example - * - * _.union([2], [1, 2]); - * // => [2, 1] - */ - var union = baseRest(function(arrays) { - return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true)); - }); - - /** - * This method is like `_.union` except that it accepts `iteratee` which is - * invoked for each element of each `arrays` to generate the criterion by - * which uniqueness is computed. Result values are chosen from the first - * array in which the value occurs. The iteratee is invoked with one argument: - * (value). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {Array} Returns the new array of combined values. - * @example - * - * _.unionBy([2.1], [1.2, 2.3], Math.floor); - * // => [2.1, 1.2] - * - * // The `_.property` iteratee shorthand. - * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); - * // => [{ 'x': 1 }, { 'x': 2 }] - */ - var unionBy = baseRest(function(arrays) { - var iteratee = last(arrays); - if (isArrayLikeObject(iteratee)) { - iteratee = undefined; - } - return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)); - }); - - /** - * This method is like `_.union` except that it accepts `comparator` which - * is invoked to compare elements of `arrays`. Result values are chosen from - * the first array in which the value occurs. The comparator is invoked - * with two arguments: (arrVal, othVal). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of combined values. - * @example - * - * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; - * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; - * - * _.unionWith(objects, others, _.isEqual); - * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] - */ - var unionWith = baseRest(function(arrays) { - var comparator = last(arrays); - comparator = typeof comparator == 'function' ? comparator : undefined; - return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator); - }); - - /** - * Creates a duplicate-free version of an array, using - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons, in which only the first occurrence of each element - * is kept. The order of result values is determined by the order they occur - * in the array. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to inspect. - * @returns {Array} Returns the new duplicate free array. - * @example - * - * _.uniq([2, 1, 2]); - * // => [2, 1] - */ - function uniq(array) { - return (array && array.length) ? baseUniq(array) : []; - } - - /** - * This method is like `_.uniq` except that it accepts `iteratee` which is - * invoked for each element in `array` to generate the criterion by which - * uniqueness is computed. The order of result values is determined by the - * order they occur in the array. The iteratee is invoked with one argument: - * (value). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {Array} Returns the new duplicate free array. - * @example - * - * _.uniqBy([2.1, 1.2, 2.3], Math.floor); - * // => [2.1, 1.2] - * - * // The `_.property` iteratee shorthand. - * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); - * // => [{ 'x': 1 }, { 'x': 2 }] - */ - function uniqBy(array, iteratee) { - return (array && array.length) ? baseUniq(array, getIteratee(iteratee, 2)) : []; - } - - /** - * This method is like `_.uniq` except that it accepts `comparator` which - * is invoked to compare elements of `array`. The order of result values is - * determined by the order they occur in the array.The comparator is invoked - * with two arguments: (arrVal, othVal). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new duplicate free array. - * @example - * - * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }]; - * - * _.uniqWith(objects, _.isEqual); - * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] - */ - function uniqWith(array, comparator) { - comparator = typeof comparator == 'function' ? comparator : undefined; - return (array && array.length) ? baseUniq(array, undefined, comparator) : []; - } - - /** - * This method is like `_.zip` except that it accepts an array of grouped - * elements and creates an array regrouping the elements to their pre-zip - * configuration. - * - * @static - * @memberOf _ - * @since 1.2.0 - * @category Array - * @param {Array} array The array of grouped elements to process. - * @returns {Array} Returns the new array of regrouped elements. - * @example - * - * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]); - * // => [['a', 1, true], ['b', 2, false]] - * - * _.unzip(zipped); - * // => [['a', 'b'], [1, 2], [true, false]] - */ - function unzip(array) { - if (!(array && array.length)) { - return []; - } - var length = 0; - array = arrayFilter(array, function(group) { - if (isArrayLikeObject(group)) { - length = nativeMax(group.length, length); - return true; - } - }); - return baseTimes(length, function(index) { - return arrayMap(array, baseProperty(index)); - }); - } - - /** - * This method is like `_.unzip` except that it accepts `iteratee` to specify - * how regrouped values should be combined. The iteratee is invoked with the - * elements of each group: (...group). - * - * @static - * @memberOf _ - * @since 3.8.0 - * @category Array - * @param {Array} array The array of grouped elements to process. - * @param {Function} [iteratee=_.identity] The function to combine - * regrouped values. - * @returns {Array} Returns the new array of regrouped elements. - * @example - * - * var zipped = _.zip([1, 2], [10, 20], [100, 200]); - * // => [[1, 10, 100], [2, 20, 200]] - * - * _.unzipWith(zipped, _.add); - * // => [3, 30, 300] - */ - function unzipWith(array, iteratee) { - if (!(array && array.length)) { - return []; - } - var result = unzip(array); - if (iteratee == null) { - return result; - } - return arrayMap(result, function(group) { - return apply(iteratee, undefined, group); - }); - } - - /** - * Creates an array excluding all given values using - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. - * - * **Note:** Unlike `_.pull`, this method returns a new array. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {...*} [values] The values to exclude. - * @returns {Array} Returns the new array of filtered values. - * @see _.difference, _.xor - * @example - * - * _.without([2, 1, 2, 3], 1, 2); - * // => [3] - */ - var without = baseRest(function(array, values) { - return isArrayLikeObject(array) - ? baseDifference(array, values) - : []; - }); - - /** - * Creates an array of unique values that is the - * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) - * of the given arrays. The order of result values is determined by the order - * they occur in the arrays. - * - * @static - * @memberOf _ - * @since 2.4.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of filtered values. - * @see _.difference, _.without - * @example - * - * _.xor([2, 1], [2, 3]); - * // => [1, 3] - */ - var xor = baseRest(function(arrays) { - return baseXor(arrayFilter(arrays, isArrayLikeObject)); - }); - - /** - * This method is like `_.xor` except that it accepts `iteratee` which is - * invoked for each element of each `arrays` to generate the criterion by - * which by which they're compared. The order of result values is determined - * by the order they occur in the arrays. The iteratee is invoked with one - * argument: (value). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor); - * // => [1.2, 3.4] - * - * // The `_.property` iteratee shorthand. - * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); - * // => [{ 'x': 2 }] - */ - var xorBy = baseRest(function(arrays) { - var iteratee = last(arrays); - if (isArrayLikeObject(iteratee)) { - iteratee = undefined; - } - return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2)); - }); - - /** - * This method is like `_.xor` except that it accepts `comparator` which is - * invoked to compare elements of `arrays`. The order of result values is - * determined by the order they occur in the arrays. The comparator is invoked - * with two arguments: (arrVal, othVal). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; - * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; - * - * _.xorWith(objects, others, _.isEqual); - * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] - */ - var xorWith = baseRest(function(arrays) { - var comparator = last(arrays); - comparator = typeof comparator == 'function' ? comparator : undefined; - return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator); - }); - - /** - * Creates an array of grouped elements, the first of which contains the - * first elements of the given arrays, the second of which contains the - * second elements of the given arrays, and so on. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Array - * @param {...Array} [arrays] The arrays to process. - * @returns {Array} Returns the new array of grouped elements. - * @example - * - * _.zip(['a', 'b'], [1, 2], [true, false]); - * // => [['a', 1, true], ['b', 2, false]] - */ - var zip = baseRest(unzip); - - /** - * This method is like `_.fromPairs` except that it accepts two arrays, - * one of property identifiers and one of corresponding values. - * - * @static - * @memberOf _ - * @since 0.4.0 - * @category Array - * @param {Array} [props=[]] The property identifiers. - * @param {Array} [values=[]] The property values. - * @returns {Object} Returns the new object. - * @example - * - * _.zipObject(['a', 'b'], [1, 2]); - * // => { 'a': 1, 'b': 2 } - */ - function zipObject(props, values) { - return baseZipObject(props || [], values || [], assignValue); - } - - /** - * This method is like `_.zipObject` except that it supports property paths. - * - * @static - * @memberOf _ - * @since 4.1.0 - * @category Array - * @param {Array} [props=[]] The property identifiers. - * @param {Array} [values=[]] The property values. - * @returns {Object} Returns the new object. - * @example - * - * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]); - * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } } - */ - function zipObjectDeep(props, values) { - return baseZipObject(props || [], values || [], baseSet); - } - - /** - * This method is like `_.zip` except that it accepts `iteratee` to specify - * how grouped values should be combined. The iteratee is invoked with the - * elements of each group: (...group). - * - * @static - * @memberOf _ - * @since 3.8.0 - * @category Array - * @param {...Array} [arrays] The arrays to process. - * @param {Function} [iteratee=_.identity] The function to combine - * grouped values. - * @returns {Array} Returns the new array of grouped elements. - * @example - * - * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) { - * return a + b + c; - * }); - * // => [111, 222] - */ - var zipWith = baseRest(function(arrays) { - var length = arrays.length, - iteratee = length > 1 ? arrays[length - 1] : undefined; - - iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined; - return unzipWith(arrays, iteratee); - }); - - /*------------------------------------------------------------------------*/ - - /** - * Creates a `lodash` wrapper instance that wraps `value` with explicit method - * chain sequences enabled. The result of such sequences must be unwrapped - * with `_#value`. - * - * @static - * @memberOf _ - * @since 1.3.0 - * @category Seq - * @param {*} value The value to wrap. - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 40 }, - * { 'user': 'pebbles', 'age': 1 } - * ]; - * - * var youngest = _ - * .chain(users) - * .sortBy('age') - * .map(function(o) { - * return o.user + ' is ' + o.age; - * }) - * .head() - * .value(); - * // => 'pebbles is 1' - */ - function chain(value) { - var result = lodash(value); - result.__chain__ = true; - return result; - } - - /** - * This method invokes `interceptor` and returns `value`. The interceptor - * is invoked with one argument; (value). The purpose of this method is to - * "tap into" a method chain sequence in order to modify intermediate results. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Seq - * @param {*} value The value to provide to `interceptor`. - * @param {Function} interceptor The function to invoke. - * @returns {*} Returns `value`. - * @example - * - * _([1, 2, 3]) - * .tap(function(array) { - * // Mutate input array. - * array.pop(); - * }) - * .reverse() - * .value(); - * // => [2, 1] - */ - function tap(value, interceptor) { - interceptor(value); - return value; - } - - /** - * This method is like `_.tap` except that it returns the result of `interceptor`. - * The purpose of this method is to "pass thru" values replacing intermediate - * results in a method chain sequence. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Seq - * @param {*} value The value to provide to `interceptor`. - * @param {Function} interceptor The function to invoke. - * @returns {*} Returns the result of `interceptor`. - * @example - * - * _(' abc ') - * .chain() - * .trim() - * .thru(function(value) { - * return [value]; - * }) - * .value(); - * // => ['abc'] - */ - function thru(value, interceptor) { - return interceptor(value); - } - - /** - * This method is the wrapper version of `_.at`. - * - * @name at - * @memberOf _ - * @since 1.0.0 - * @category Seq - * @param {...(string|string[])} [paths] The property paths to pick. - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; - * - * _(object).at(['a[0].b.c', 'a[1]']).value(); - * // => [3, 4] - */ - var wrapperAt = flatRest(function(paths) { - var length = paths.length, - start = length ? paths[0] : 0, - value = this.__wrapped__, - interceptor = function(object) { return baseAt(object, paths); }; - - if (length > 1 || this.__actions__.length || - !(value instanceof LazyWrapper) || !isIndex(start)) { - return this.thru(interceptor); - } - value = value.slice(start, +start + (length ? 1 : 0)); - value.__actions__.push({ - 'func': thru, - 'args': [interceptor], - 'thisArg': undefined - }); - return new LodashWrapper(value, this.__chain__).thru(function(array) { - if (length && !array.length) { - array.push(undefined); - } - return array; - }); - }); - - /** - * Creates a `lodash` wrapper instance with explicit method chain sequences enabled. - * - * @name chain - * @memberOf _ - * @since 0.1.0 - * @category Seq - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 40 } - * ]; - * - * // A sequence without explicit chaining. - * _(users).head(); - * // => { 'user': 'barney', 'age': 36 } - * - * // A sequence with explicit chaining. - * _(users) - * .chain() - * .head() - * .pick('user') - * .value(); - * // => { 'user': 'barney' } - */ - function wrapperChain() { - return chain(this); - } - - /** - * Executes the chain sequence and returns the wrapped result. - * - * @name commit - * @memberOf _ - * @since 3.2.0 - * @category Seq - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var array = [1, 2]; - * var wrapped = _(array).push(3); - * - * console.log(array); - * // => [1, 2] - * - * wrapped = wrapped.commit(); - * console.log(array); - * // => [1, 2, 3] - * - * wrapped.last(); - * // => 3 - * - * console.log(array); - * // => [1, 2, 3] - */ - function wrapperCommit() { - return new LodashWrapper(this.value(), this.__chain__); - } - - /** - * Gets the next value on a wrapped object following the - * [iterator protocol](https://mdn.io/iteration_protocols#iterator). - * - * @name next - * @memberOf _ - * @since 4.0.0 - * @category Seq - * @returns {Object} Returns the next iterator value. - * @example - * - * var wrapped = _([1, 2]); - * - * wrapped.next(); - * // => { 'done': false, 'value': 1 } - * - * wrapped.next(); - * // => { 'done': false, 'value': 2 } - * - * wrapped.next(); - * // => { 'done': true, 'value': undefined } - */ - function wrapperNext() { - if (this.__values__ === undefined) { - this.__values__ = toArray(this.value()); - } - var done = this.__index__ >= this.__values__.length, - value = done ? undefined : this.__values__[this.__index__++]; - - return { 'done': done, 'value': value }; - } - - /** - * Enables the wrapper to be iterable. - * - * @name Symbol.iterator - * @memberOf _ - * @since 4.0.0 - * @category Seq - * @returns {Object} Returns the wrapper object. - * @example - * - * var wrapped = _([1, 2]); - * - * wrapped[Symbol.iterator]() === wrapped; - * // => true - * - * Array.from(wrapped); - * // => [1, 2] - */ - function wrapperToIterator() { - return this; - } - - /** - * Creates a clone of the chain sequence planting `value` as the wrapped value. - * - * @name plant - * @memberOf _ - * @since 3.2.0 - * @category Seq - * @param {*} value The value to plant. - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * function square(n) { - * return n * n; - * } - * - * var wrapped = _([1, 2]).map(square); - * var other = wrapped.plant([3, 4]); - * - * other.value(); - * // => [9, 16] - * - * wrapped.value(); - * // => [1, 4] - */ - function wrapperPlant(value) { - var result, - parent = this; - - while (parent instanceof baseLodash) { - var clone = wrapperClone(parent); - clone.__index__ = 0; - clone.__values__ = undefined; - if (result) { - previous.__wrapped__ = clone; - } else { - result = clone; - } - var previous = clone; - parent = parent.__wrapped__; - } - previous.__wrapped__ = value; - return result; - } - - /** - * This method is the wrapper version of `_.reverse`. - * - * **Note:** This method mutates the wrapped array. - * - * @name reverse - * @memberOf _ - * @since 0.1.0 - * @category Seq - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var array = [1, 2, 3]; - * - * _(array).reverse().value() - * // => [3, 2, 1] - * - * console.log(array); - * // => [3, 2, 1] - */ - function wrapperReverse() { - var value = this.__wrapped__; - if (value instanceof LazyWrapper) { - var wrapped = value; - if (this.__actions__.length) { - wrapped = new LazyWrapper(this); - } - wrapped = wrapped.reverse(); - wrapped.__actions__.push({ - 'func': thru, - 'args': [reverse], - 'thisArg': undefined - }); - return new LodashWrapper(wrapped, this.__chain__); - } - return this.thru(reverse); - } - - /** - * Executes the chain sequence to resolve the unwrapped value. - * - * @name value - * @memberOf _ - * @since 0.1.0 - * @alias toJSON, valueOf - * @category Seq - * @returns {*} Returns the resolved unwrapped value. - * @example - * - * _([1, 2, 3]).value(); - * // => [1, 2, 3] - */ - function wrapperValue() { - return baseWrapperValue(this.__wrapped__, this.__actions__); - } - - /*------------------------------------------------------------------------*/ - - /** - * Creates an object composed of keys generated from the results of running - * each element of `collection` thru `iteratee`. The corresponding value of - * each key is the number of times the key was returned by `iteratee`. The - * iteratee is invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 0.5.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The iteratee to transform keys. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * _.countBy([6.1, 4.2, 6.3], Math.floor); - * // => { '4': 1, '6': 2 } - * - * // The `_.property` iteratee shorthand. - * _.countBy(['one', 'two', 'three'], 'length'); - * // => { '3': 2, '5': 1 } - */ - var countBy = createAggregator(function(result, value, key) { - if (hasOwnProperty.call(result, key)) { - ++result[key]; - } else { - baseAssignValue(result, key, 1); - } - }); - - /** - * Checks if `predicate` returns truthy for **all** elements of `collection`. - * Iteration is stopped once `predicate` returns falsey. The predicate is - * invoked with three arguments: (value, index|key, collection). - * - * **Note:** This method returns `true` for - * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because - * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of - * elements of empty collections. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {boolean} Returns `true` if all elements pass the predicate check, - * else `false`. - * @example - * - * _.every([true, 1, null, 'yes'], Boolean); - * // => false - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': false }, - * { 'user': 'fred', 'age': 40, 'active': false } - * ]; - * - * // The `_.matches` iteratee shorthand. - * _.every(users, { 'user': 'barney', 'active': false }); - * // => false - * - * // The `_.matchesProperty` iteratee shorthand. - * _.every(users, ['active', false]); - * // => true - * - * // The `_.property` iteratee shorthand. - * _.every(users, 'active'); - * // => false - */ - function every(collection, predicate, guard) { - var func = isArray(collection) ? arrayEvery : baseEvery; - if (guard && isIterateeCall(collection, predicate, guard)) { - predicate = undefined; - } - return func(collection, getIteratee(predicate, 3)); - } - - /** - * Iterates over elements of `collection`, returning an array of all elements - * `predicate` returns truthy for. The predicate is invoked with three - * arguments: (value, index|key, collection). - * - * **Note:** Unlike `_.remove`, this method returns a new array. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - * @see _.reject - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': true }, - * { 'user': 'fred', 'age': 40, 'active': false } - * ]; - * - * _.filter(users, function(o) { return !o.active; }); - * // => objects for ['fred'] - * - * // The `_.matches` iteratee shorthand. - * _.filter(users, { 'age': 36, 'active': true }); - * // => objects for ['barney'] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.filter(users, ['active', false]); - * // => objects for ['fred'] - * - * // The `_.property` iteratee shorthand. - * _.filter(users, 'active'); - * // => objects for ['barney'] - */ - function filter(collection, predicate) { - var func = isArray(collection) ? arrayFilter : baseFilter; - return func(collection, getIteratee(predicate, 3)); - } - - /** - * Iterates over elements of `collection`, returning the first element - * `predicate` returns truthy for. The predicate is invoked with three - * arguments: (value, index|key, collection). - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to inspect. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @param {number} [fromIndex=0] The index to search from. - * @returns {*} Returns the matched element, else `undefined`. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': true }, - * { 'user': 'fred', 'age': 40, 'active': false }, - * { 'user': 'pebbles', 'age': 1, 'active': true } - * ]; - * - * _.find(users, function(o) { return o.age < 40; }); - * // => object for 'barney' - * - * // The `_.matches` iteratee shorthand. - * _.find(users, { 'age': 1, 'active': true }); - * // => object for 'pebbles' - * - * // The `_.matchesProperty` iteratee shorthand. - * _.find(users, ['active', false]); - * // => object for 'fred' - * - * // The `_.property` iteratee shorthand. - * _.find(users, 'active'); - * // => object for 'barney' - */ - var find = createFind(findIndex); - - /** - * This method is like `_.find` except that it iterates over elements of - * `collection` from right to left. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Collection - * @param {Array|Object} collection The collection to inspect. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @param {number} [fromIndex=collection.length-1] The index to search from. - * @returns {*} Returns the matched element, else `undefined`. - * @example - * - * _.findLast([1, 2, 3, 4], function(n) { - * return n % 2 == 1; - * }); - * // => 3 - */ - var findLast = createFind(findLastIndex); - - /** - * Creates a flattened array of values by running each element in `collection` - * thru `iteratee` and flattening the mapped results. The iteratee is invoked - * with three arguments: (value, index|key, collection). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Array} Returns the new flattened array. - * @example - * - * function duplicate(n) { - * return [n, n]; - * } - * - * _.flatMap([1, 2], duplicate); - * // => [1, 1, 2, 2] - */ - function flatMap(collection, iteratee) { - return baseFlatten(map(collection, iteratee), 1); - } - - /** - * This method is like `_.flatMap` except that it recursively flattens the - * mapped results. - * - * @static - * @memberOf _ - * @since 4.7.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Array} Returns the new flattened array. - * @example - * - * function duplicate(n) { - * return [[[n, n]]]; - * } - * - * _.flatMapDeep([1, 2], duplicate); - * // => [1, 1, 2, 2] - */ - function flatMapDeep(collection, iteratee) { - return baseFlatten(map(collection, iteratee), INFINITY); - } - - /** - * This method is like `_.flatMap` except that it recursively flattens the - * mapped results up to `depth` times. - * - * @static - * @memberOf _ - * @since 4.7.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {number} [depth=1] The maximum recursion depth. - * @returns {Array} Returns the new flattened array. - * @example - * - * function duplicate(n) { - * return [[[n, n]]]; - * } - * - * _.flatMapDepth([1, 2], duplicate, 2); - * // => [[1, 1], [2, 2]] - */ - function flatMapDepth(collection, iteratee, depth) { - depth = depth === undefined ? 1 : toInteger(depth); - return baseFlatten(map(collection, iteratee), depth); - } - - /** - * Iterates over elements of `collection` and invokes `iteratee` for each element. - * The iteratee is invoked with three arguments: (value, index|key, collection). - * Iteratee functions may exit iteration early by explicitly returning `false`. - * - * **Note:** As with other "Collections" methods, objects with a "length" - * property are iterated like arrays. To avoid this behavior use `_.forIn` - * or `_.forOwn` for object iteration. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @alias each - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Array|Object} Returns `collection`. - * @see _.forEachRight - * @example - * - * _.forEach([1, 2], function(value) { - * console.log(value); - * }); - * // => Logs `1` then `2`. - * - * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) { - * console.log(key); - * }); - * // => Logs 'a' then 'b' (iteration order is not guaranteed). - */ - function forEach(collection, iteratee) { - var func = isArray(collection) ? arrayEach : baseEach; - return func(collection, getIteratee(iteratee, 3)); - } - - /** - * This method is like `_.forEach` except that it iterates over elements of - * `collection` from right to left. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @alias eachRight - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Array|Object} Returns `collection`. - * @see _.forEach - * @example - * - * _.forEachRight([1, 2], function(value) { - * console.log(value); - * }); - * // => Logs `2` then `1`. - */ - function forEachRight(collection, iteratee) { - var func = isArray(collection) ? arrayEachRight : baseEachRight; - return func(collection, getIteratee(iteratee, 3)); - } - - /** - * Creates an object composed of keys generated from the results of running - * each element of `collection` thru `iteratee`. The order of grouped values - * is determined by the order they occur in `collection`. The corresponding - * value of each key is an array of elements responsible for generating the - * key. The iteratee is invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The iteratee to transform keys. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * _.groupBy([6.1, 4.2, 6.3], Math.floor); - * // => { '4': [4.2], '6': [6.1, 6.3] } - * - * // The `_.property` iteratee shorthand. - * _.groupBy(['one', 'two', 'three'], 'length'); - * // => { '3': ['one', 'two'], '5': ['three'] } - */ - var groupBy = createAggregator(function(result, value, key) { - if (hasOwnProperty.call(result, key)) { - result[key].push(value); - } else { - baseAssignValue(result, key, [value]); - } - }); - - /** - * Checks if `value` is in `collection`. If `collection` is a string, it's - * checked for a substring of `value`, otherwise - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * is used for equality comparisons. If `fromIndex` is negative, it's used as - * the offset from the end of `collection`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object|string} collection The collection to inspect. - * @param {*} value The value to search for. - * @param {number} [fromIndex=0] The index to search from. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. - * @returns {boolean} Returns `true` if `value` is found, else `false`. - * @example - * - * _.includes([1, 2, 3], 1); - * // => true - * - * _.includes([1, 2, 3], 1, 2); - * // => false - * - * _.includes({ 'a': 1, 'b': 2 }, 1); - * // => true - * - * _.includes('abcd', 'bc'); - * // => true - */ - function includes(collection, value, fromIndex, guard) { - collection = isArrayLike(collection) ? collection : values(collection); - fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0; - - var length = collection.length; - if (fromIndex < 0) { - fromIndex = nativeMax(length + fromIndex, 0); - } - return isString(collection) - ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1) - : (!!length && baseIndexOf(collection, value, fromIndex) > -1); - } - - /** - * Invokes the method at `path` of each element in `collection`, returning - * an array of the results of each invoked method. Any additional arguments - * are provided to each invoked method. If `path` is a function, it's invoked - * for, and `this` bound to, each element in `collection`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Array|Function|string} path The path of the method to invoke or - * the function invoked per iteration. - * @param {...*} [args] The arguments to invoke each method with. - * @returns {Array} Returns the array of results. - * @example - * - * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort'); - * // => [[1, 5, 7], [1, 2, 3]] - * - * _.invokeMap([123, 456], String.prototype.split, ''); - * // => [['1', '2', '3'], ['4', '5', '6']] - */ - var invokeMap = baseRest(function(collection, path, args) { - var index = -1, - isFunc = typeof path == 'function', - result = isArrayLike(collection) ? Array(collection.length) : []; - - baseEach(collection, function(value) { - result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args); - }); - return result; - }); - - /** - * Creates an object composed of keys generated from the results of running - * each element of `collection` thru `iteratee`. The corresponding value of - * each key is the last element responsible for generating the key. The - * iteratee is invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The iteratee to transform keys. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * var array = [ - * { 'dir': 'left', 'code': 97 }, - * { 'dir': 'right', 'code': 100 } - * ]; - * - * _.keyBy(array, function(o) { - * return String.fromCharCode(o.code); - * }); - * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } - * - * _.keyBy(array, 'dir'); - * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } - */ - var keyBy = createAggregator(function(result, value, key) { - baseAssignValue(result, key, value); - }); - - /** - * Creates an array of values by running each element in `collection` thru - * `iteratee`. The iteratee is invoked with three arguments: - * (value, index|key, collection). - * - * Many lodash methods are guarded to work as iteratees for methods like - * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`. - * - * The guarded methods are: - * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`, - * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`, - * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`, - * `template`, `trim`, `trimEnd`, `trimStart`, and `words` - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Array} Returns the new mapped array. - * @example - * - * function square(n) { - * return n * n; - * } - * - * _.map([4, 8], square); - * // => [16, 64] - * - * _.map({ 'a': 4, 'b': 8 }, square); - * // => [16, 64] (iteration order is not guaranteed) - * - * var users = [ - * { 'user': 'barney' }, - * { 'user': 'fred' } - * ]; - * - * // The `_.property` iteratee shorthand. - * _.map(users, 'user'); - * // => ['barney', 'fred'] - */ - function map(collection, iteratee) { - var func = isArray(collection) ? arrayMap : baseMap; - return func(collection, getIteratee(iteratee, 3)); - } - - /** - * This method is like `_.sortBy` except that it allows specifying the sort - * orders of the iteratees to sort by. If `orders` is unspecified, all values - * are sorted in ascending order. Otherwise, specify an order of "desc" for - * descending or "asc" for ascending sort order of corresponding values. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]] - * The iteratees to sort by. - * @param {string[]} [orders] The sort orders of `iteratees`. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. - * @returns {Array} Returns the new sorted array. - * @example - * - * var users = [ - * { 'user': 'fred', 'age': 48 }, - * { 'user': 'barney', 'age': 34 }, - * { 'user': 'fred', 'age': 40 }, - * { 'user': 'barney', 'age': 36 } - * ]; - * - * // Sort by `user` in ascending order and by `age` in descending order. - * _.orderBy(users, ['user', 'age'], ['asc', 'desc']); - * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] - */ - function orderBy(collection, iteratees, orders, guard) { - if (collection == null) { - return []; - } - if (!isArray(iteratees)) { - iteratees = iteratees == null ? [] : [iteratees]; - } - orders = guard ? undefined : orders; - if (!isArray(orders)) { - orders = orders == null ? [] : [orders]; - } - return baseOrderBy(collection, iteratees, orders); - } - - /** - * Creates an array of elements split into two groups, the first of which - * contains elements `predicate` returns truthy for, the second of which - * contains elements `predicate` returns falsey for. The predicate is - * invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the array of grouped elements. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': false }, - * { 'user': 'fred', 'age': 40, 'active': true }, - * { 'user': 'pebbles', 'age': 1, 'active': false } - * ]; - * - * _.partition(users, function(o) { return o.active; }); - * // => objects for [['fred'], ['barney', 'pebbles']] - * - * // The `_.matches` iteratee shorthand. - * _.partition(users, { 'age': 1, 'active': false }); - * // => objects for [['pebbles'], ['barney', 'fred']] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.partition(users, ['active', false]); - * // => objects for [['barney', 'pebbles'], ['fred']] - * - * // The `_.property` iteratee shorthand. - * _.partition(users, 'active'); - * // => objects for [['fred'], ['barney', 'pebbles']] - */ - var partition = createAggregator(function(result, value, key) { - result[key ? 0 : 1].push(value); - }, function() { return [[], []]; }); - - /** - * Reduces `collection` to a value which is the accumulated result of running - * each element in `collection` thru `iteratee`, where each successive - * invocation is supplied the return value of the previous. If `accumulator` - * is not given, the first element of `collection` is used as the initial - * value. The iteratee is invoked with four arguments: - * (accumulator, value, index|key, collection). - * - * Many lodash methods are guarded to work as iteratees for methods like - * `_.reduce`, `_.reduceRight`, and `_.transform`. - * - * The guarded methods are: - * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`, - * and `sortBy` - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @returns {*} Returns the accumulated value. - * @see _.reduceRight - * @example - * - * _.reduce([1, 2], function(sum, n) { - * return sum + n; - * }, 0); - * // => 3 - * - * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { - * (result[value] || (result[value] = [])).push(key); - * return result; - * }, {}); - * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed) - */ - function reduce(collection, iteratee, accumulator) { - var func = isArray(collection) ? arrayReduce : baseReduce, - initAccum = arguments.length < 3; - - return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach); - } - - /** - * This method is like `_.reduce` except that it iterates over elements of - * `collection` from right to left. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @returns {*} Returns the accumulated value. - * @see _.reduce - * @example - * - * var array = [[0, 1], [2, 3], [4, 5]]; - * - * _.reduceRight(array, function(flattened, other) { - * return flattened.concat(other); - * }, []); - * // => [4, 5, 2, 3, 0, 1] - */ - function reduceRight(collection, iteratee, accumulator) { - var func = isArray(collection) ? arrayReduceRight : baseReduce, - initAccum = arguments.length < 3; - - return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight); - } - - /** - * The opposite of `_.filter`; this method returns the elements of `collection` - * that `predicate` does **not** return truthy for. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - * @see _.filter - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': false }, - * { 'user': 'fred', 'age': 40, 'active': true } - * ]; - * - * _.reject(users, function(o) { return !o.active; }); - * // => objects for ['fred'] - * - * // The `_.matches` iteratee shorthand. - * _.reject(users, { 'age': 40, 'active': true }); - * // => objects for ['barney'] - * - * // The `_.matchesProperty` iteratee shorthand. - * _.reject(users, ['active', false]); - * // => objects for ['fred'] - * - * // The `_.property` iteratee shorthand. - * _.reject(users, 'active'); - * // => objects for ['barney'] - */ - function reject(collection, predicate) { - var func = isArray(collection) ? arrayFilter : baseFilter; - return func(collection, negate(getIteratee(predicate, 3))); - } - - /** - * Gets a random element from `collection`. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Collection - * @param {Array|Object} collection The collection to sample. - * @returns {*} Returns the random element. - * @example - * - * _.sample([1, 2, 3, 4]); - * // => 2 - */ - function sample(collection) { - var func = isArray(collection) ? arraySample : baseSample; - return func(collection); - } - - /** - * Gets `n` random elements at unique keys from `collection` up to the - * size of `collection`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Collection - * @param {Array|Object} collection The collection to sample. - * @param {number} [n=1] The number of elements to sample. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Array} Returns the random elements. - * @example - * - * _.sampleSize([1, 2, 3], 2); - * // => [3, 1] - * - * _.sampleSize([1, 2, 3], 4); - * // => [2, 3, 1] - */ - function sampleSize(collection, n, guard) { - if ((guard ? isIterateeCall(collection, n, guard) : n === undefined)) { - n = 1; - } else { - n = toInteger(n); - } - var func = isArray(collection) ? arraySampleSize : baseSampleSize; - return func(collection, n); - } - - /** - * Creates an array of shuffled values, using a version of the - * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to shuffle. - * @returns {Array} Returns the new shuffled array. - * @example - * - * _.shuffle([1, 2, 3, 4]); - * // => [4, 1, 3, 2] - */ - function shuffle(collection) { - var func = isArray(collection) ? arrayShuffle : baseShuffle; - return func(collection); - } - - /** - * Gets the size of `collection` by returning its length for array-like - * values or the number of own enumerable string keyed properties for objects. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object|string} collection The collection to inspect. - * @returns {number} Returns the collection size. - * @example - * - * _.size([1, 2, 3]); - * // => 3 - * - * _.size({ 'a': 1, 'b': 2 }); - * // => 2 - * - * _.size('pebbles'); - * // => 7 - */ - function size(collection) { - if (collection == null) { - return 0; - } - if (isArrayLike(collection)) { - return isString(collection) ? stringSize(collection) : collection.length; - } - var tag = getTag(collection); - if (tag == mapTag || tag == setTag) { - return collection.size; - } - return baseKeys(collection).length; - } - - /** - * Checks if `predicate` returns truthy for **any** element of `collection`. - * Iteration is stopped once `predicate` returns truthy. The predicate is - * invoked with three arguments: (value, index|key, collection). - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - * @example - * - * _.some([null, 0, 'yes', false], Boolean); - * // => true - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false } - * ]; - * - * // The `_.matches` iteratee shorthand. - * _.some(users, { 'user': 'barney', 'active': false }); - * // => false - * - * // The `_.matchesProperty` iteratee shorthand. - * _.some(users, ['active', false]); - * // => true - * - * // The `_.property` iteratee shorthand. - * _.some(users, 'active'); - * // => true - */ - function some(collection, predicate, guard) { - var func = isArray(collection) ? arraySome : baseSome; - if (guard && isIterateeCall(collection, predicate, guard)) { - predicate = undefined; - } - return func(collection, getIteratee(predicate, 3)); - } - - /** - * Creates an array of elements, sorted in ascending order by the results of - * running each element in a collection thru each iteratee. This method - * performs a stable sort, that is, it preserves the original sort order of - * equal elements. The iteratees are invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {...(Function|Function[])} [iteratees=[_.identity]] - * The iteratees to sort by. - * @returns {Array} Returns the new sorted array. - * @example - * - * var users = [ - * { 'user': 'fred', 'age': 48 }, - * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 40 }, - * { 'user': 'barney', 'age': 34 } - * ]; - * - * _.sortBy(users, [function(o) { return o.user; }]); - * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] - * - * _.sortBy(users, ['user', 'age']); - * // => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]] - */ - var sortBy = baseRest(function(collection, iteratees) { - if (collection == null) { - return []; - } - var length = iteratees.length; - if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) { - iteratees = []; - } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) { - iteratees = [iteratees[0]]; - } - return baseOrderBy(collection, baseFlatten(iteratees, 1), []); - }); - - /*------------------------------------------------------------------------*/ - - /** - * Gets the timestamp of the number of milliseconds that have elapsed since - * the Unix epoch (1 January 1970 00:00:00 UTC). - * - * @static - * @memberOf _ - * @since 2.4.0 - * @category Date - * @returns {number} Returns the timestamp. - * @example - * - * _.defer(function(stamp) { - * console.log(_.now() - stamp); - * }, _.now()); - * // => Logs the number of milliseconds it took for the deferred invocation. - */ - var now = ctxNow || function() { - return root.Date.now(); - }; - - /*------------------------------------------------------------------------*/ - - /** - * The opposite of `_.before`; this method creates a function that invokes - * `func` once it's called `n` or more times. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {number} n The number of calls before `func` is invoked. - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * var saves = ['profile', 'settings']; - * - * var done = _.after(saves.length, function() { - * console.log('done saving!'); - * }); - * - * _.forEach(saves, function(type) { - * asyncSave({ 'type': type, 'complete': done }); - * }); - * // => Logs 'done saving!' after the two async saves have completed. - */ - function after(n, func) { - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - n = toInteger(n); - return function() { - if (--n < 1) { - return func.apply(this, arguments); - } - }; - } - - /** - * Creates a function that invokes `func`, with up to `n` arguments, - * ignoring any additional arguments. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Function - * @param {Function} func The function to cap arguments for. - * @param {number} [n=func.length] The arity cap. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Function} Returns the new capped function. - * @example - * - * _.map(['6', '8', '10'], _.ary(parseInt, 1)); - * // => [6, 8, 10] - */ - function ary(func, n, guard) { - n = guard ? undefined : n; - n = (func && n == null) ? func.length : n; - return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n); - } - - /** - * Creates a function that invokes `func`, with the `this` binding and arguments - * of the created function, while it's called less than `n` times. Subsequent - * calls to the created function return the result of the last `func` invocation. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Function - * @param {number} n The number of calls at which `func` is no longer invoked. - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * jQuery(element).on('click', _.before(5, addContactToList)); - * // => Allows adding up to 4 contacts to the list. - */ - function before(n, func) { - var result; - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - n = toInteger(n); - return function() { - if (--n > 0) { - result = func.apply(this, arguments); - } - if (n <= 1) { - func = undefined; - } - return result; - }; - } - - /** - * Creates a function that invokes `func` with the `this` binding of `thisArg` - * and `partials` prepended to the arguments it receives. - * - * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, - * may be used as a placeholder for partially applied arguments. - * - * **Note:** Unlike native `Function#bind`, this method doesn't set the "length" - * property of bound functions. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to bind. - * @param {*} thisArg The `this` binding of `func`. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new bound function. - * @example - * - * function greet(greeting, punctuation) { - * return greeting + ' ' + this.user + punctuation; - * } - * - * var object = { 'user': 'fred' }; - * - * var bound = _.bind(greet, object, 'hi'); - * bound('!'); - * // => 'hi fred!' - * - * // Bound with placeholders. - * var bound = _.bind(greet, object, _, '!'); - * bound('hi'); - * // => 'hi fred!' - */ - var bind = baseRest(function(func, thisArg, partials) { - var bitmask = WRAP_BIND_FLAG; - if (partials.length) { - var holders = replaceHolders(partials, getHolder(bind)); - bitmask |= WRAP_PARTIAL_FLAG; - } - return createWrap(func, bitmask, thisArg, partials, holders); - }); - - /** - * Creates a function that invokes the method at `object[key]` with `partials` - * prepended to the arguments it receives. - * - * This method differs from `_.bind` by allowing bound functions to reference - * methods that may be redefined or don't yet exist. See - * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern) - * for more details. - * - * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for partially applied arguments. - * - * @static - * @memberOf _ - * @since 0.10.0 - * @category Function - * @param {Object} object The object to invoke the method on. - * @param {string} key The key of the method. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new bound function. - * @example - * - * var object = { - * 'user': 'fred', - * 'greet': function(greeting, punctuation) { - * return greeting + ' ' + this.user + punctuation; - * } - * }; - * - * var bound = _.bindKey(object, 'greet', 'hi'); - * bound('!'); - * // => 'hi fred!' - * - * object.greet = function(greeting, punctuation) { - * return greeting + 'ya ' + this.user + punctuation; - * }; - * - * bound('!'); - * // => 'hiya fred!' - * - * // Bound with placeholders. - * var bound = _.bindKey(object, 'greet', _, '!'); - * bound('hi'); - * // => 'hiya fred!' - */ - var bindKey = baseRest(function(object, key, partials) { - var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG; - if (partials.length) { - var holders = replaceHolders(partials, getHolder(bindKey)); - bitmask |= WRAP_PARTIAL_FLAG; - } - return createWrap(key, bitmask, object, partials, holders); - }); - - /** - * Creates a function that accepts arguments of `func` and either invokes - * `func` returning its result, if at least `arity` number of arguments have - * been provided, or returns a function that accepts the remaining `func` - * arguments, and so on. The arity of `func` may be specified if `func.length` - * is not sufficient. - * - * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, - * may be used as a placeholder for provided arguments. - * - * **Note:** This method doesn't set the "length" property of curried functions. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Function - * @param {Function} func The function to curry. - * @param {number} [arity=func.length] The arity of `func`. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Function} Returns the new curried function. - * @example - * - * var abc = function(a, b, c) { - * return [a, b, c]; - * }; - * - * var curried = _.curry(abc); - * - * curried(1)(2)(3); - * // => [1, 2, 3] - * - * curried(1, 2)(3); - * // => [1, 2, 3] - * - * curried(1, 2, 3); - * // => [1, 2, 3] - * - * // Curried with placeholders. - * curried(1)(_, 3)(2); - * // => [1, 2, 3] - */ - function curry(func, arity, guard) { - arity = guard ? undefined : arity; - var result = createWrap(func, WRAP_CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity); - result.placeholder = curry.placeholder; - return result; - } - - /** - * This method is like `_.curry` except that arguments are applied to `func` - * in the manner of `_.partialRight` instead of `_.partial`. - * - * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for provided arguments. - * - * **Note:** This method doesn't set the "length" property of curried functions. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Function - * @param {Function} func The function to curry. - * @param {number} [arity=func.length] The arity of `func`. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Function} Returns the new curried function. - * @example - * - * var abc = function(a, b, c) { - * return [a, b, c]; - * }; - * - * var curried = _.curryRight(abc); - * - * curried(3)(2)(1); - * // => [1, 2, 3] - * - * curried(2, 3)(1); - * // => [1, 2, 3] - * - * curried(1, 2, 3); - * // => [1, 2, 3] - * - * // Curried with placeholders. - * curried(3)(1, _)(2); - * // => [1, 2, 3] - */ - function curryRight(func, arity, guard) { - arity = guard ? undefined : arity; - var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity); - result.placeholder = curryRight.placeholder; - return result; - } - - /** - * Creates a debounced function that delays invoking `func` until after `wait` - * milliseconds have elapsed since the last time the debounced function was - * invoked. The debounced function comes with a `cancel` method to cancel - * delayed `func` invocations and a `flush` method to immediately invoke them. - * Provide `options` to indicate whether `func` should be invoked on the - * leading and/or trailing edge of the `wait` timeout. The `func` is invoked - * with the last arguments provided to the debounced function. Subsequent - * calls to the debounced function return the result of the last `func` - * invocation. - * - * **Note:** If `leading` and `trailing` options are `true`, `func` is - * invoked on the trailing edge of the timeout only if the debounced function - * is invoked more than once during the `wait` timeout. - * - * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred - * until to the next tick, similar to `setTimeout` with a timeout of `0`. - * - * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) - * for details over the differences between `_.debounce` and `_.throttle`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to debounce. - * @param {number} [wait=0] The number of milliseconds to delay. - * @param {Object} [options={}] The options object. - * @param {boolean} [options.leading=false] - * Specify invoking on the leading edge of the timeout. - * @param {number} [options.maxWait] - * The maximum time `func` is allowed to be delayed before it's invoked. - * @param {boolean} [options.trailing=true] - * Specify invoking on the trailing edge of the timeout. - * @returns {Function} Returns the new debounced function. - * @example - * - * // Avoid costly calculations while the window size is in flux. - * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); - * - * // Invoke `sendMail` when clicked, debouncing subsequent calls. - * jQuery(element).on('click', _.debounce(sendMail, 300, { - * 'leading': true, - * 'trailing': false - * })); - * - * // Ensure `batchLog` is invoked once after 1 second of debounced calls. - * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); - * var source = new EventSource('/stream'); - * jQuery(source).on('message', debounced); - * - * // Cancel the trailing debounced invocation. - * jQuery(window).on('popstate', debounced.cancel); - */ - function debounce(func, wait, options) { - var lastArgs, - lastThis, - maxWait, - result, - timerId, - lastCallTime, - lastInvokeTime = 0, - leading = false, - maxing = false, - trailing = true; - - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - wait = toNumber(wait) || 0; - if (isObject(options)) { - leading = !!options.leading; - maxing = 'maxWait' in options; - maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; - trailing = 'trailing' in options ? !!options.trailing : trailing; - } - - function invokeFunc(time) { - var args = lastArgs, - thisArg = lastThis; - - lastArgs = lastThis = undefined; - lastInvokeTime = time; - result = func.apply(thisArg, args); - return result; - } - - function leadingEdge(time) { - // Reset any `maxWait` timer. - lastInvokeTime = time; - // Start the timer for the trailing edge. - timerId = setTimeout(timerExpired, wait); - // Invoke the leading edge. - return leading ? invokeFunc(time) : result; - } - - function remainingWait(time) { - var timeSinceLastCall = time - lastCallTime, - timeSinceLastInvoke = time - lastInvokeTime, - timeWaiting = wait - timeSinceLastCall; - - return maxing - ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) - : timeWaiting; - } - - function shouldInvoke(time) { - var timeSinceLastCall = time - lastCallTime, - timeSinceLastInvoke = time - lastInvokeTime; - - // Either this is the first call, activity has stopped and we're at the - // trailing edge, the system time has gone backwards and we're treating - // it as the trailing edge, or we've hit the `maxWait` limit. - return (lastCallTime === undefined || (timeSinceLastCall >= wait) || - (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); - } - - function timerExpired() { - var time = now(); - if (shouldInvoke(time)) { - return trailingEdge(time); - } - // Restart the timer. - timerId = setTimeout(timerExpired, remainingWait(time)); - } - - function trailingEdge(time) { - timerId = undefined; - - // Only invoke if we have `lastArgs` which means `func` has been - // debounced at least once. - if (trailing && lastArgs) { - return invokeFunc(time); - } - lastArgs = lastThis = undefined; - return result; - } - - function cancel() { - if (timerId !== undefined) { - clearTimeout(timerId); - } - lastInvokeTime = 0; - lastArgs = lastCallTime = lastThis = timerId = undefined; - } - - function flush() { - return timerId === undefined ? result : trailingEdge(now()); - } - - function debounced() { - var time = now(), - isInvoking = shouldInvoke(time); - - lastArgs = arguments; - lastThis = this; - lastCallTime = time; - - if (isInvoking) { - if (timerId === undefined) { - return leadingEdge(lastCallTime); - } - if (maxing) { - // Handle invocations in a tight loop. - timerId = setTimeout(timerExpired, wait); - return invokeFunc(lastCallTime); - } - } - if (timerId === undefined) { - timerId = setTimeout(timerExpired, wait); - } - return result; - } - debounced.cancel = cancel; - debounced.flush = flush; - return debounced; - } - - /** - * Defers invoking the `func` until the current call stack has cleared. Any - * additional arguments are provided to `func` when it's invoked. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to defer. - * @param {...*} [args] The arguments to invoke `func` with. - * @returns {number} Returns the timer id. - * @example - * - * _.defer(function(text) { - * console.log(text); - * }, 'deferred'); - * // => Logs 'deferred' after one millisecond. - */ - var defer = baseRest(function(func, args) { - return baseDelay(func, 1, args); - }); - - /** - * Invokes `func` after `wait` milliseconds. Any additional arguments are - * provided to `func` when it's invoked. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to delay. - * @param {number} wait The number of milliseconds to delay invocation. - * @param {...*} [args] The arguments to invoke `func` with. - * @returns {number} Returns the timer id. - * @example - * - * _.delay(function(text) { - * console.log(text); - * }, 1000, 'later'); - * // => Logs 'later' after one second. - */ - var delay = baseRest(function(func, wait, args) { - return baseDelay(func, toNumber(wait) || 0, args); - }); - - /** - * Creates a function that invokes `func` with arguments reversed. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Function - * @param {Function} func The function to flip arguments for. - * @returns {Function} Returns the new flipped function. - * @example - * - * var flipped = _.flip(function() { - * return _.toArray(arguments); - * }); - * - * flipped('a', 'b', 'c', 'd'); - * // => ['d', 'c', 'b', 'a'] - */ - function flip(func) { - return createWrap(func, WRAP_FLIP_FLAG); - } - - /** - * Creates a function that memoizes the result of `func`. If `resolver` is - * provided, it determines the cache key for storing the result based on the - * arguments provided to the memoized function. By default, the first argument - * provided to the memoized function is used as the map cache key. The `func` - * is invoked with the `this` binding of the memoized function. - * - * **Note:** The cache is exposed as the `cache` property on the memoized - * function. Its creation may be customized by replacing the `_.memoize.Cache` - * constructor with one whose instances implement the - * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) - * method interface of `clear`, `delete`, `get`, `has`, and `set`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to have its output memoized. - * @param {Function} [resolver] The function to resolve the cache key. - * @returns {Function} Returns the new memoized function. - * @example - * - * var object = { 'a': 1, 'b': 2 }; - * var other = { 'c': 3, 'd': 4 }; - * - * var values = _.memoize(_.values); - * values(object); - * // => [1, 2] - * - * values(other); - * // => [3, 4] - * - * object.a = 2; - * values(object); - * // => [1, 2] - * - * // Modify the result cache. - * values.cache.set(object, ['a', 'b']); - * values(object); - * // => ['a', 'b'] - * - * // Replace `_.memoize.Cache`. - * _.memoize.Cache = WeakMap; - */ - function memoize(func, resolver) { - if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) { - throw new TypeError(FUNC_ERROR_TEXT); - } - var memoized = function() { - var args = arguments, - key = resolver ? resolver.apply(this, args) : args[0], - cache = memoized.cache; - - if (cache.has(key)) { - return cache.get(key); - } - var result = func.apply(this, args); - memoized.cache = cache.set(key, result) || cache; - return result; - }; - memoized.cache = new (memoize.Cache || MapCache); - return memoized; - } - - // Expose `MapCache`. - memoize.Cache = MapCache; - - /** - * Creates a function that negates the result of the predicate `func`. The - * `func` predicate is invoked with the `this` binding and arguments of the - * created function. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Function - * @param {Function} predicate The predicate to negate. - * @returns {Function} Returns the new negated function. - * @example - * - * function isEven(n) { - * return n % 2 == 0; - * } - * - * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); - * // => [1, 3, 5] - */ - function negate(predicate) { - if (typeof predicate != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - return function() { - var args = arguments; - switch (args.length) { - case 0: return !predicate.call(this); - case 1: return !predicate.call(this, args[0]); - case 2: return !predicate.call(this, args[0], args[1]); - case 3: return !predicate.call(this, args[0], args[1], args[2]); - } - return !predicate.apply(this, args); - }; - } - - /** - * Creates a function that is restricted to invoking `func` once. Repeat calls - * to the function return the value of the first invocation. The `func` is - * invoked with the `this` binding and arguments of the created function. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * var initialize = _.once(createApplication); - * initialize(); - * initialize(); - * // => `createApplication` is invoked once - */ - function once(func) { - return before(2, func); - } - - /** - * Creates a function that invokes `func` with its arguments transformed. - * - * @static - * @since 4.0.0 - * @memberOf _ - * @category Function - * @param {Function} func The function to wrap. - * @param {...(Function|Function[])} [transforms=[_.identity]] - * The argument transforms. - * @returns {Function} Returns the new function. - * @example - * - * function doubled(n) { - * return n * 2; - * } - * - * function square(n) { - * return n * n; - * } - * - * var func = _.overArgs(function(x, y) { - * return [x, y]; - * }, [square, doubled]); - * - * func(9, 3); - * // => [81, 6] - * - * func(10, 5); - * // => [100, 10] - */ - var overArgs = castRest(function(func, transforms) { - transforms = (transforms.length == 1 && isArray(transforms[0])) - ? arrayMap(transforms[0], baseUnary(getIteratee())) - : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee())); - - var funcsLength = transforms.length; - return baseRest(function(args) { - var index = -1, - length = nativeMin(args.length, funcsLength); - - while (++index < length) { - args[index] = transforms[index].call(this, args[index]); - } - return apply(func, this, args); - }); - }); - - /** - * Creates a function that invokes `func` with `partials` prepended to the - * arguments it receives. This method is like `_.bind` except it does **not** - * alter the `this` binding. - * - * The `_.partial.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for partially applied arguments. - * - * **Note:** This method doesn't set the "length" property of partially - * applied functions. - * - * @static - * @memberOf _ - * @since 0.2.0 - * @category Function - * @param {Function} func The function to partially apply arguments to. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new partially applied function. - * @example - * - * function greet(greeting, name) { - * return greeting + ' ' + name; - * } - * - * var sayHelloTo = _.partial(greet, 'hello'); - * sayHelloTo('fred'); - * // => 'hello fred' - * - * // Partially applied with placeholders. - * var greetFred = _.partial(greet, _, 'fred'); - * greetFred('hi'); - * // => 'hi fred' - */ - var partial = baseRest(function(func, partials) { - var holders = replaceHolders(partials, getHolder(partial)); - return createWrap(func, WRAP_PARTIAL_FLAG, undefined, partials, holders); - }); - - /** - * This method is like `_.partial` except that partially applied arguments - * are appended to the arguments it receives. - * - * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for partially applied arguments. - * - * **Note:** This method doesn't set the "length" property of partially - * applied functions. - * - * @static - * @memberOf _ - * @since 1.0.0 - * @category Function - * @param {Function} func The function to partially apply arguments to. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new partially applied function. - * @example - * - * function greet(greeting, name) { - * return greeting + ' ' + name; - * } - * - * var greetFred = _.partialRight(greet, 'fred'); - * greetFred('hi'); - * // => 'hi fred' - * - * // Partially applied with placeholders. - * var sayHelloTo = _.partialRight(greet, 'hello', _); - * sayHelloTo('fred'); - * // => 'hello fred' - */ - var partialRight = baseRest(function(func, partials) { - var holders = replaceHolders(partials, getHolder(partialRight)); - return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined, partials, holders); - }); - - /** - * Creates a function that invokes `func` with arguments arranged according - * to the specified `indexes` where the argument value at the first index is - * provided as the first argument, the argument value at the second index is - * provided as the second argument, and so on. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Function - * @param {Function} func The function to rearrange arguments for. - * @param {...(number|number[])} indexes The arranged argument indexes. - * @returns {Function} Returns the new function. - * @example - * - * var rearged = _.rearg(function(a, b, c) { - * return [a, b, c]; - * }, [2, 0, 1]); - * - * rearged('b', 'c', 'a') - * // => ['a', 'b', 'c'] - */ - var rearg = flatRest(function(func, indexes) { - return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes); - }); - - /** - * Creates a function that invokes `func` with the `this` binding of the - * created function and arguments from `start` and beyond provided as - * an array. - * - * **Note:** This method is based on the - * [rest parameter](https://mdn.io/rest_parameters). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Function - * @param {Function} func The function to apply a rest parameter to. - * @param {number} [start=func.length-1] The start position of the rest parameter. - * @returns {Function} Returns the new function. - * @example - * - * var say = _.rest(function(what, names) { - * return what + ' ' + _.initial(names).join(', ') + - * (_.size(names) > 1 ? ', & ' : '') + _.last(names); - * }); - * - * say('hello', 'fred', 'barney', 'pebbles'); - * // => 'hello fred, barney, & pebbles' - */ - function rest(func, start) { - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - start = start === undefined ? start : toInteger(start); - return baseRest(func, start); - } - - /** - * Creates a function that invokes `func` with the `this` binding of the - * create function and an array of arguments much like - * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply). - * - * **Note:** This method is based on the - * [spread operator](https://mdn.io/spread_operator). - * - * @static - * @memberOf _ - * @since 3.2.0 - * @category Function - * @param {Function} func The function to spread arguments over. - * @param {number} [start=0] The start position of the spread. - * @returns {Function} Returns the new function. - * @example - * - * var say = _.spread(function(who, what) { - * return who + ' says ' + what; - * }); - * - * say(['fred', 'hello']); - * // => 'fred says hello' - * - * var numbers = Promise.all([ - * Promise.resolve(40), - * Promise.resolve(36) - * ]); - * - * numbers.then(_.spread(function(x, y) { - * return x + y; - * })); - * // => a Promise of 76 - */ - function spread(func, start) { - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - start = start == null ? 0 : nativeMax(toInteger(start), 0); - return baseRest(function(args) { - var array = args[start], - otherArgs = castSlice(args, 0, start); - - if (array) { - arrayPush(otherArgs, array); - } - return apply(func, this, otherArgs); - }); - } - - /** - * Creates a throttled function that only invokes `func` at most once per - * every `wait` milliseconds. The throttled function comes with a `cancel` - * method to cancel delayed `func` invocations and a `flush` method to - * immediately invoke them. Provide `options` to indicate whether `func` - * should be invoked on the leading and/or trailing edge of the `wait` - * timeout. The `func` is invoked with the last arguments provided to the - * throttled function. Subsequent calls to the throttled function return the - * result of the last `func` invocation. - * - * **Note:** If `leading` and `trailing` options are `true`, `func` is - * invoked on the trailing edge of the timeout only if the throttled function - * is invoked more than once during the `wait` timeout. - * - * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred - * until to the next tick, similar to `setTimeout` with a timeout of `0`. - * - * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) - * for details over the differences between `_.throttle` and `_.debounce`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to throttle. - * @param {number} [wait=0] The number of milliseconds to throttle invocations to. - * @param {Object} [options={}] The options object. - * @param {boolean} [options.leading=true] - * Specify invoking on the leading edge of the timeout. - * @param {boolean} [options.trailing=true] - * Specify invoking on the trailing edge of the timeout. - * @returns {Function} Returns the new throttled function. - * @example - * - * // Avoid excessively updating the position while scrolling. - * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); - * - * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. - * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); - * jQuery(element).on('click', throttled); - * - * // Cancel the trailing throttled invocation. - * jQuery(window).on('popstate', throttled.cancel); - */ - function throttle(func, wait, options) { - var leading = true, - trailing = true; - - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - if (isObject(options)) { - leading = 'leading' in options ? !!options.leading : leading; - trailing = 'trailing' in options ? !!options.trailing : trailing; - } - return debounce(func, wait, { - 'leading': leading, - 'maxWait': wait, - 'trailing': trailing - }); - } - - /** - * Creates a function that accepts up to one argument, ignoring any - * additional arguments. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Function - * @param {Function} func The function to cap arguments for. - * @returns {Function} Returns the new capped function. - * @example - * - * _.map(['6', '8', '10'], _.unary(parseInt)); - * // => [6, 8, 10] - */ - function unary(func) { - return ary(func, 1); - } - - /** - * Creates a function that provides `value` to `wrapper` as its first - * argument. Any additional arguments provided to the function are appended - * to those provided to the `wrapper`. The wrapper is invoked with the `this` - * binding of the created function. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {*} value The value to wrap. - * @param {Function} [wrapper=identity] The wrapper function. - * @returns {Function} Returns the new function. - * @example - * - * var p = _.wrap(_.escape, function(func, text) { - * return '

' + func(text) + '

'; - * }); - * - * p('fred, barney, & pebbles'); - * // => '

fred, barney, & pebbles

' - */ - function wrap(value, wrapper) { - return partial(castFunction(wrapper), value); - } - - /*------------------------------------------------------------------------*/ - - /** - * Casts `value` as an array if it's not one. - * - * @static - * @memberOf _ - * @since 4.4.0 - * @category Lang - * @param {*} value The value to inspect. - * @returns {Array} Returns the cast array. - * @example - * - * _.castArray(1); - * // => [1] - * - * _.castArray({ 'a': 1 }); - * // => [{ 'a': 1 }] - * - * _.castArray('abc'); - * // => ['abc'] - * - * _.castArray(null); - * // => [null] - * - * _.castArray(undefined); - * // => [undefined] - * - * _.castArray(); - * // => [] - * - * var array = [1, 2, 3]; - * console.log(_.castArray(array) === array); - * // => true - */ - function castArray() { - if (!arguments.length) { - return []; - } - var value = arguments[0]; - return isArray(value) ? value : [value]; - } - - /** - * Creates a shallow clone of `value`. - * - * **Note:** This method is loosely based on the - * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm) - * and supports cloning arrays, array buffers, booleans, date objects, maps, - * numbers, `Object` objects, regexes, sets, strings, symbols, and typed - * arrays. The own enumerable properties of `arguments` objects are cloned - * as plain objects. An empty object is returned for uncloneable values such - * as error objects, functions, DOM nodes, and WeakMaps. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to clone. - * @returns {*} Returns the cloned value. - * @see _.cloneDeep - * @example - * - * var objects = [{ 'a': 1 }, { 'b': 2 }]; - * - * var shallow = _.clone(objects); - * console.log(shallow[0] === objects[0]); - * // => true - */ - function clone(value) { - return baseClone(value, CLONE_SYMBOLS_FLAG); - } - - /** - * This method is like `_.clone` except that it accepts `customizer` which - * is invoked to produce the cloned value. If `customizer` returns `undefined`, - * cloning is handled by the method instead. The `customizer` is invoked with - * up to four arguments; (value [, index|key, object, stack]). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to clone. - * @param {Function} [customizer] The function to customize cloning. - * @returns {*} Returns the cloned value. - * @see _.cloneDeepWith - * @example - * - * function customizer(value) { - * if (_.isElement(value)) { - * return value.cloneNode(false); - * } - * } - * - * var el = _.cloneWith(document.body, customizer); - * - * console.log(el === document.body); - * // => false - * console.log(el.nodeName); - * // => 'BODY' - * console.log(el.childNodes.length); - * // => 0 - */ - function cloneWith(value, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - return baseClone(value, CLONE_SYMBOLS_FLAG, customizer); - } - - /** - * This method is like `_.clone` except that it recursively clones `value`. - * - * @static - * @memberOf _ - * @since 1.0.0 - * @category Lang - * @param {*} value The value to recursively clone. - * @returns {*} Returns the deep cloned value. - * @see _.clone - * @example - * - * var objects = [{ 'a': 1 }, { 'b': 2 }]; - * - * var deep = _.cloneDeep(objects); - * console.log(deep[0] === objects[0]); - * // => false - */ - function cloneDeep(value) { - return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG); - } - - /** - * This method is like `_.cloneWith` except that it recursively clones `value`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to recursively clone. - * @param {Function} [customizer] The function to customize cloning. - * @returns {*} Returns the deep cloned value. - * @see _.cloneWith - * @example - * - * function customizer(value) { - * if (_.isElement(value)) { - * return value.cloneNode(true); - * } - * } - * - * var el = _.cloneDeepWith(document.body, customizer); - * - * console.log(el === document.body); - * // => false - * console.log(el.nodeName); - * // => 'BODY' - * console.log(el.childNodes.length); - * // => 20 - */ - function cloneDeepWith(value, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer); - } - - /** - * Checks if `object` conforms to `source` by invoking the predicate - * properties of `source` with the corresponding property values of `object`. - * - * **Note:** This method is equivalent to `_.conforms` when `source` is - * partially applied. - * - * @static - * @memberOf _ - * @since 4.14.0 - * @category Lang - * @param {Object} object The object to inspect. - * @param {Object} source The object of property predicates to conform to. - * @returns {boolean} Returns `true` if `object` conforms, else `false`. - * @example - * - * var object = { 'a': 1, 'b': 2 }; - * - * _.conformsTo(object, { 'b': function(n) { return n > 1; } }); - * // => true - * - * _.conformsTo(object, { 'b': function(n) { return n > 2; } }); - * // => false - */ - function conformsTo(object, source) { - return source == null || baseConformsTo(object, source, keys(source)); - } - - /** - * Performs a - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * comparison between two values to determine if they are equivalent. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * var object = { 'a': 1 }; - * var other = { 'a': 1 }; - * - * _.eq(object, object); - * // => true - * - * _.eq(object, other); - * // => false - * - * _.eq('a', 'a'); - * // => true - * - * _.eq('a', Object('a')); - * // => false - * - * _.eq(NaN, NaN); - * // => true - */ - function eq(value, other) { - return value === other || (value !== value && other !== other); - } - - /** - * Checks if `value` is greater than `other`. - * - * @static - * @memberOf _ - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is greater than `other`, - * else `false`. - * @see _.lt - * @example - * - * _.gt(3, 1); - * // => true - * - * _.gt(3, 3); - * // => false - * - * _.gt(1, 3); - * // => false - */ - var gt = createRelationalOperation(baseGt); - - /** - * Checks if `value` is greater than or equal to `other`. - * - * @static - * @memberOf _ - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is greater than or equal to - * `other`, else `false`. - * @see _.lte - * @example - * - * _.gte(3, 1); - * // => true - * - * _.gte(3, 3); - * // => true - * - * _.gte(1, 3); - * // => false - */ - var gte = createRelationalOperation(function(value, other) { - return value >= other; - }); - - /** - * Checks if `value` is likely an `arguments` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an `arguments` object, - * else `false`. - * @example - * - * _.isArguments(function() { return arguments; }()); - * // => true - * - * _.isArguments([1, 2, 3]); - * // => false - */ - var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) { - return isObjectLike(value) && hasOwnProperty.call(value, 'callee') && - !propertyIsEnumerable.call(value, 'callee'); - }; - - /** - * Checks if `value` is classified as an `Array` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array, else `false`. - * @example - * - * _.isArray([1, 2, 3]); - * // => true - * - * _.isArray(document.body.children); - * // => false - * - * _.isArray('abc'); - * // => false - * - * _.isArray(_.noop); - * // => false - */ - var isArray = Array.isArray; - - /** - * Checks if `value` is classified as an `ArrayBuffer` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. - * @example - * - * _.isArrayBuffer(new ArrayBuffer(2)); - * // => true - * - * _.isArrayBuffer(new Array(2)); - * // => false - */ - var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer; - - /** - * Checks if `value` is array-like. A value is considered array-like if it's - * not a function and has a `value.length` that's an integer greater than or - * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is array-like, else `false`. - * @example - * - * _.isArrayLike([1, 2, 3]); - * // => true - * - * _.isArrayLike(document.body.children); - * // => true - * - * _.isArrayLike('abc'); - * // => true - * - * _.isArrayLike(_.noop); - * // => false - */ - function isArrayLike(value) { - return value != null && isLength(value.length) && !isFunction(value); - } - - /** - * This method is like `_.isArrayLike` except that it also checks if `value` - * is an object. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array-like object, - * else `false`. - * @example - * - * _.isArrayLikeObject([1, 2, 3]); - * // => true - * - * _.isArrayLikeObject(document.body.children); - * // => true - * - * _.isArrayLikeObject('abc'); - * // => false - * - * _.isArrayLikeObject(_.noop); - * // => false - */ - function isArrayLikeObject(value) { - return isObjectLike(value) && isArrayLike(value); - } - - /** - * Checks if `value` is classified as a boolean primitive or object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a boolean, else `false`. - * @example - * - * _.isBoolean(false); - * // => true - * - * _.isBoolean(null); - * // => false - */ - function isBoolean(value) { - return value === true || value === false || - (isObjectLike(value) && baseGetTag(value) == boolTag); - } - - /** - * Checks if `value` is a buffer. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. - * @example - * - * _.isBuffer(new Buffer(2)); - * // => true - * - * _.isBuffer(new Uint8Array(2)); - * // => false - */ - var isBuffer = nativeIsBuffer || stubFalse; - - /** - * Checks if `value` is classified as a `Date` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a date object, else `false`. - * @example - * - * _.isDate(new Date); - * // => true - * - * _.isDate('Mon April 23 2012'); - * // => false - */ - var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate; - - /** - * Checks if `value` is likely a DOM element. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. - * @example - * - * _.isElement(document.body); - * // => true - * - * _.isElement(''); - * // => false - */ - function isElement(value) { - return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value); - } - - /** - * Checks if `value` is an empty object, collection, map, or set. - * - * Objects are considered empty if they have no own enumerable string keyed - * properties. - * - * Array-like values such as `arguments` objects, arrays, buffers, strings, or - * jQuery-like collections are considered empty if they have a `length` of `0`. - * Similarly, maps and sets are considered empty if they have a `size` of `0`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is empty, else `false`. - * @example - * - * _.isEmpty(null); - * // => true - * - * _.isEmpty(true); - * // => true - * - * _.isEmpty(1); - * // => true - * - * _.isEmpty([1, 2, 3]); - * // => false - * - * _.isEmpty({ 'a': 1 }); - * // => false - */ - function isEmpty(value) { - if (value == null) { - return true; - } - if (isArrayLike(value) && - (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' || - isBuffer(value) || isTypedArray(value) || isArguments(value))) { - return !value.length; - } - var tag = getTag(value); - if (tag == mapTag || tag == setTag) { - return !value.size; - } - if (isPrototype(value)) { - return !baseKeys(value).length; - } - for (var key in value) { - if (hasOwnProperty.call(value, key)) { - return false; - } - } - return true; - } - - /** - * Performs a deep comparison between two values to determine if they are - * equivalent. - * - * **Note:** This method supports comparing arrays, array buffers, booleans, - * date objects, error objects, maps, numbers, `Object` objects, regexes, - * sets, strings, symbols, and typed arrays. `Object` objects are compared - * by their own, not inherited, enumerable properties. Functions and DOM - * nodes are compared by strict equality, i.e. `===`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * var object = { 'a': 1 }; - * var other = { 'a': 1 }; - * - * _.isEqual(object, other); - * // => true - * - * object === other; - * // => false - */ - function isEqual(value, other) { - return baseIsEqual(value, other); - } - - /** - * This method is like `_.isEqual` except that it accepts `customizer` which - * is invoked to compare values. If `customizer` returns `undefined`, comparisons - * are handled by the method instead. The `customizer` is invoked with up to - * six arguments: (objValue, othValue [, index|key, object, other, stack]). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @param {Function} [customizer] The function to customize comparisons. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * function isGreeting(value) { - * return /^h(?:i|ello)$/.test(value); - * } - * - * function customizer(objValue, othValue) { - * if (isGreeting(objValue) && isGreeting(othValue)) { - * return true; - * } - * } - * - * var array = ['hello', 'goodbye']; - * var other = ['hi', 'goodbye']; - * - * _.isEqualWith(array, other, customizer); - * // => true - */ - function isEqualWith(value, other, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - var result = customizer ? customizer(value, other) : undefined; - return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result; - } - - /** - * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, - * `SyntaxError`, `TypeError`, or `URIError` object. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an error object, else `false`. - * @example - * - * _.isError(new Error); - * // => true - * - * _.isError(Error); - * // => false - */ - function isError(value) { - if (!isObjectLike(value)) { - return false; - } - var tag = baseGetTag(value); - return tag == errorTag || tag == domExcTag || - (typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value)); - } - - /** - * Checks if `value` is a finite primitive number. - * - * **Note:** This method is based on - * [`Number.isFinite`](https://mdn.io/Number/isFinite). - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. - * @example - * - * _.isFinite(3); - * // => true - * - * _.isFinite(Number.MIN_VALUE); - * // => true - * - * _.isFinite(Infinity); - * // => false - * - * _.isFinite('3'); - * // => false - */ - function isFinite(value) { - return typeof value == 'number' && nativeIsFinite(value); - } - - /** - * Checks if `value` is classified as a `Function` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a function, else `false`. - * @example - * - * _.isFunction(_); - * // => true - * - * _.isFunction(/abc/); - * // => false - */ - function isFunction(value) { - if (!isObject(value)) { - return false; - } - // The use of `Object#toString` avoids issues with the `typeof` operator - // in Safari 9 which returns 'object' for typed arrays and other constructors. - var tag = baseGetTag(value); - return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; - } - - /** - * Checks if `value` is an integer. - * - * **Note:** This method is based on - * [`Number.isInteger`](https://mdn.io/Number/isInteger). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an integer, else `false`. - * @example - * - * _.isInteger(3); - * // => true - * - * _.isInteger(Number.MIN_VALUE); - * // => false - * - * _.isInteger(Infinity); - * // => false - * - * _.isInteger('3'); - * // => false - */ - function isInteger(value) { - return typeof value == 'number' && value == toInteger(value); - } - - /** - * Checks if `value` is a valid array-like length. - * - * **Note:** This method is loosely based on - * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. - * @example - * - * _.isLength(3); - * // => true - * - * _.isLength(Number.MIN_VALUE); - * // => false - * - * _.isLength(Infinity); - * // => false - * - * _.isLength('3'); - * // => false - */ - function isLength(value) { - return typeof value == 'number' && - value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; - } - - /** - * Checks if `value` is the - * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) - * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an object, else `false`. - * @example - * - * _.isObject({}); - * // => true - * - * _.isObject([1, 2, 3]); - * // => true - * - * _.isObject(_.noop); - * // => true - * - * _.isObject(null); - * // => false - */ - function isObject(value) { - var type = typeof value; - return value != null && (type == 'object' || type == 'function'); - } - - /** - * Checks if `value` is object-like. A value is object-like if it's not `null` - * and has a `typeof` result of "object". - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is object-like, else `false`. - * @example - * - * _.isObjectLike({}); - * // => true - * - * _.isObjectLike([1, 2, 3]); - * // => true - * - * _.isObjectLike(_.noop); - * // => false - * - * _.isObjectLike(null); - * // => false - */ - function isObjectLike(value) { - return value != null && typeof value == 'object'; - } - - /** - * Checks if `value` is classified as a `Map` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a map, else `false`. - * @example - * - * _.isMap(new Map); - * // => true - * - * _.isMap(new WeakMap); - * // => false - */ - var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap; - - /** - * Performs a partial deep comparison between `object` and `source` to - * determine if `object` contains equivalent property values. - * - * **Note:** This method is equivalent to `_.matches` when `source` is - * partially applied. - * - * Partial comparisons will match empty array and empty object `source` - * values against any array or object value, respectively. See `_.isEqual` - * for a list of supported value comparisons. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {Object} object The object to inspect. - * @param {Object} source The object of property values to match. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - * @example - * - * var object = { 'a': 1, 'b': 2 }; - * - * _.isMatch(object, { 'b': 2 }); - * // => true - * - * _.isMatch(object, { 'b': 1 }); - * // => false - */ - function isMatch(object, source) { - return object === source || baseIsMatch(object, source, getMatchData(source)); - } - - /** - * This method is like `_.isMatch` except that it accepts `customizer` which - * is invoked to compare values. If `customizer` returns `undefined`, comparisons - * are handled by the method instead. The `customizer` is invoked with five - * arguments: (objValue, srcValue, index|key, object, source). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {Object} object The object to inspect. - * @param {Object} source The object of property values to match. - * @param {Function} [customizer] The function to customize comparisons. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - * @example - * - * function isGreeting(value) { - * return /^h(?:i|ello)$/.test(value); - * } - * - * function customizer(objValue, srcValue) { - * if (isGreeting(objValue) && isGreeting(srcValue)) { - * return true; - * } - * } - * - * var object = { 'greeting': 'hello' }; - * var source = { 'greeting': 'hi' }; - * - * _.isMatchWith(object, source, customizer); - * // => true - */ - function isMatchWith(object, source, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - return baseIsMatch(object, source, getMatchData(source), customizer); - } - - /** - * Checks if `value` is `NaN`. - * - * **Note:** This method is based on - * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as - * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for - * `undefined` and other non-number values. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. - * @example - * - * _.isNaN(NaN); - * // => true - * - * _.isNaN(new Number(NaN)); - * // => true - * - * isNaN(undefined); - * // => true - * - * _.isNaN(undefined); - * // => false - */ - function isNaN(value) { - // An `NaN` primitive is the only value that is not equal to itself. - // Perform the `toStringTag` check first to avoid errors with some - // ActiveX objects in IE. - return isNumber(value) && value != +value; - } - - /** - * Checks if `value` is a pristine native function. - * - * **Note:** This method can't reliably detect native functions in the presence - * of the core-js package because core-js circumvents this kind of detection. - * Despite multiple requests, the core-js maintainer has made it clear: any - * attempt to fix the detection will be obstructed. As a result, we're left - * with little choice but to throw an error. Unfortunately, this also affects - * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill), - * which rely on core-js. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a native function, - * else `false`. - * @example - * - * _.isNative(Array.prototype.push); - * // => true - * - * _.isNative(_); - * // => false - */ - function isNative(value) { - if (isMaskable(value)) { - throw new Error(CORE_ERROR_TEXT); - } - return baseIsNative(value); - } - - /** - * Checks if `value` is `null`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `null`, else `false`. - * @example - * - * _.isNull(null); - * // => true - * - * _.isNull(void 0); - * // => false - */ - function isNull(value) { - return value === null; - } - - /** - * Checks if `value` is `null` or `undefined`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is nullish, else `false`. - * @example - * - * _.isNil(null); - * // => true - * - * _.isNil(void 0); - * // => true - * - * _.isNil(NaN); - * // => false - */ - function isNil(value) { - return value == null; - } - - /** - * Checks if `value` is classified as a `Number` primitive or object. - * - * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are - * classified as numbers, use the `_.isFinite` method. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a number, else `false`. - * @example - * - * _.isNumber(3); - * // => true - * - * _.isNumber(Number.MIN_VALUE); - * // => true - * - * _.isNumber(Infinity); - * // => true - * - * _.isNumber('3'); - * // => false - */ - function isNumber(value) { - return typeof value == 'number' || - (isObjectLike(value) && baseGetTag(value) == numberTag); - } - - /** - * Checks if `value` is a plain object, that is, an object created by the - * `Object` constructor or one with a `[[Prototype]]` of `null`. - * - * @static - * @memberOf _ - * @since 0.8.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. - * @example - * - * function Foo() { - * this.a = 1; - * } - * - * _.isPlainObject(new Foo); - * // => false - * - * _.isPlainObject([1, 2, 3]); - * // => false - * - * _.isPlainObject({ 'x': 0, 'y': 0 }); - * // => true - * - * _.isPlainObject(Object.create(null)); - * // => true - */ - function isPlainObject(value) { - if (!isObjectLike(value) || baseGetTag(value) != objectTag) { - return false; - } - var proto = getPrototype(value); - if (proto === null) { - return true; - } - var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; - return typeof Ctor == 'function' && Ctor instanceof Ctor && - funcToString.call(Ctor) == objectCtorString; - } - - /** - * Checks if `value` is classified as a `RegExp` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. - * @example - * - * _.isRegExp(/abc/); - * // => true - * - * _.isRegExp('/abc/'); - * // => false - */ - var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp; - - /** - * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754 - * double precision number which isn't the result of a rounded unsafe integer. - * - * **Note:** This method is based on - * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`. - * @example - * - * _.isSafeInteger(3); - * // => true - * - * _.isSafeInteger(Number.MIN_VALUE); - * // => false - * - * _.isSafeInteger(Infinity); - * // => false - * - * _.isSafeInteger('3'); - * // => false - */ - function isSafeInteger(value) { - return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER; - } - - /** - * Checks if `value` is classified as a `Set` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a set, else `false`. - * @example - * - * _.isSet(new Set); - * // => true - * - * _.isSet(new WeakSet); - * // => false - */ - var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet; - - /** - * Checks if `value` is classified as a `String` primitive or object. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a string, else `false`. - * @example - * - * _.isString('abc'); - * // => true - * - * _.isString(1); - * // => false - */ - function isString(value) { - return typeof value == 'string' || - (!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag); - } - - /** - * Checks if `value` is classified as a `Symbol` primitive or object. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. - * @example - * - * _.isSymbol(Symbol.iterator); - * // => true - * - * _.isSymbol('abc'); - * // => false - */ - function isSymbol(value) { - return typeof value == 'symbol' || - (isObjectLike(value) && baseGetTag(value) == symbolTag); - } - - /** - * Checks if `value` is classified as a typed array. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. - * @example - * - * _.isTypedArray(new Uint8Array); - * // => true - * - * _.isTypedArray([]); - * // => false - */ - var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; - - /** - * Checks if `value` is `undefined`. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. - * @example - * - * _.isUndefined(void 0); - * // => true - * - * _.isUndefined(null); - * // => false - */ - function isUndefined(value) { - return value === undefined; - } - - /** - * Checks if `value` is classified as a `WeakMap` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a weak map, else `false`. - * @example - * - * _.isWeakMap(new WeakMap); - * // => true - * - * _.isWeakMap(new Map); - * // => false - */ - function isWeakMap(value) { - return isObjectLike(value) && getTag(value) == weakMapTag; - } - - /** - * Checks if `value` is classified as a `WeakSet` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a weak set, else `false`. - * @example - * - * _.isWeakSet(new WeakSet); - * // => true - * - * _.isWeakSet(new Set); - * // => false - */ - function isWeakSet(value) { - return isObjectLike(value) && baseGetTag(value) == weakSetTag; - } - - /** - * Checks if `value` is less than `other`. - * - * @static - * @memberOf _ - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is less than `other`, - * else `false`. - * @see _.gt - * @example - * - * _.lt(1, 3); - * // => true - * - * _.lt(3, 3); - * // => false - * - * _.lt(3, 1); - * // => false - */ - var lt = createRelationalOperation(baseLt); - - /** - * Checks if `value` is less than or equal to `other`. - * - * @static - * @memberOf _ - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is less than or equal to - * `other`, else `false`. - * @see _.gte - * @example - * - * _.lte(1, 3); - * // => true - * - * _.lte(3, 3); - * // => true - * - * _.lte(3, 1); - * // => false - */ - var lte = createRelationalOperation(function(value, other) { - return value <= other; - }); - - /** - * Converts `value` to an array. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Lang - * @param {*} value The value to convert. - * @returns {Array} Returns the converted array. - * @example - * - * _.toArray({ 'a': 1, 'b': 2 }); - * // => [1, 2] - * - * _.toArray('abc'); - * // => ['a', 'b', 'c'] - * - * _.toArray(1); - * // => [] - * - * _.toArray(null); - * // => [] - */ - function toArray(value) { - if (!value) { - return []; - } - if (isArrayLike(value)) { - return isString(value) ? stringToArray(value) : copyArray(value); - } - if (symIterator && value[symIterator]) { - return iteratorToArray(value[symIterator]()); - } - var tag = getTag(value), - func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values); - - return func(value); - } - - /** - * Converts `value` to a finite number. - * - * @static - * @memberOf _ - * @since 4.12.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {number} Returns the converted number. - * @example - * - * _.toFinite(3.2); - * // => 3.2 - * - * _.toFinite(Number.MIN_VALUE); - * // => 5e-324 - * - * _.toFinite(Infinity); - * // => 1.7976931348623157e+308 - * - * _.toFinite('3.2'); - * // => 3.2 - */ - function toFinite(value) { - if (!value) { - return value === 0 ? value : 0; - } - value = toNumber(value); - if (value === INFINITY || value === -INFINITY) { - var sign = (value < 0 ? -1 : 1); - return sign * MAX_INTEGER; - } - return value === value ? value : 0; - } - - /** - * Converts `value` to an integer. - * - * **Note:** This method is loosely based on - * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {number} Returns the converted integer. - * @example - * - * _.toInteger(3.2); - * // => 3 - * - * _.toInteger(Number.MIN_VALUE); - * // => 0 - * - * _.toInteger(Infinity); - * // => 1.7976931348623157e+308 - * - * _.toInteger('3.2'); - * // => 3 - */ - function toInteger(value) { - var result = toFinite(value), - remainder = result % 1; - - return result === result ? (remainder ? result - remainder : result) : 0; - } - - /** - * Converts `value` to an integer suitable for use as the length of an - * array-like object. - * - * **Note:** This method is based on - * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {number} Returns the converted integer. - * @example - * - * _.toLength(3.2); - * // => 3 - * - * _.toLength(Number.MIN_VALUE); - * // => 0 - * - * _.toLength(Infinity); - * // => 4294967295 - * - * _.toLength('3.2'); - * // => 3 - */ - function toLength(value) { - return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0; - } - - /** - * Converts `value` to a number. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to process. - * @returns {number} Returns the number. - * @example - * - * _.toNumber(3.2); - * // => 3.2 - * - * _.toNumber(Number.MIN_VALUE); - * // => 5e-324 - * - * _.toNumber(Infinity); - * // => Infinity - * - * _.toNumber('3.2'); - * // => 3.2 - */ - function toNumber(value) { - if (typeof value == 'number') { - return value; - } - if (isSymbol(value)) { - return NAN; - } - if (isObject(value)) { - var other = typeof value.valueOf == 'function' ? value.valueOf() : value; - value = isObject(other) ? (other + '') : other; - } - if (typeof value != 'string') { - return value === 0 ? value : +value; - } - value = value.replace(reTrim, ''); - var isBinary = reIsBinary.test(value); - return (isBinary || reIsOctal.test(value)) - ? freeParseInt(value.slice(2), isBinary ? 2 : 8) - : (reIsBadHex.test(value) ? NAN : +value); - } - - /** - * Converts `value` to a plain object flattening inherited enumerable string - * keyed properties of `value` to own properties of the plain object. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {Object} Returns the converted plain object. - * @example - * - * function Foo() { - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.assign({ 'a': 1 }, new Foo); - * // => { 'a': 1, 'b': 2 } - * - * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); - * // => { 'a': 1, 'b': 2, 'c': 3 } - */ - function toPlainObject(value) { - return copyObject(value, keysIn(value)); - } - - /** - * Converts `value` to a safe integer. A safe integer can be compared and - * represented correctly. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {number} Returns the converted integer. - * @example - * - * _.toSafeInteger(3.2); - * // => 3 - * - * _.toSafeInteger(Number.MIN_VALUE); - * // => 0 - * - * _.toSafeInteger(Infinity); - * // => 9007199254740991 - * - * _.toSafeInteger('3.2'); - * // => 3 - */ - function toSafeInteger(value) { - return value - ? baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER) - : (value === 0 ? value : 0); - } - - /** - * Converts `value` to a string. An empty string is returned for `null` - * and `undefined` values. The sign of `-0` is preserved. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {string} Returns the converted string. - * @example - * - * _.toString(null); - * // => '' - * - * _.toString(-0); - * // => '-0' - * - * _.toString([1, 2, 3]); - * // => '1,2,3' - */ - function toString(value) { - return value == null ? '' : baseToString(value); - } - - /*------------------------------------------------------------------------*/ - - /** - * Assigns own enumerable string keyed properties of source objects to the - * destination object. Source objects are applied from left to right. - * Subsequent sources overwrite property assignments of previous sources. - * - * **Note:** This method mutates `object` and is loosely based on - * [`Object.assign`](https://mdn.io/Object/assign). - * - * @static - * @memberOf _ - * @since 0.10.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @see _.assignIn - * @example - * - * function Foo() { - * this.a = 1; - * } - * - * function Bar() { - * this.c = 3; - * } - * - * Foo.prototype.b = 2; - * Bar.prototype.d = 4; - * - * _.assign({ 'a': 0 }, new Foo, new Bar); - * // => { 'a': 1, 'c': 3 } - */ - var assign = createAssigner(function(object, source) { - if (isPrototype(source) || isArrayLike(source)) { - copyObject(source, keys(source), object); - return; - } - for (var key in source) { - if (hasOwnProperty.call(source, key)) { - assignValue(object, key, source[key]); - } - } - }); - - /** - * This method is like `_.assign` except that it iterates over own and - * inherited source properties. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @alias extend - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @see _.assign - * @example - * - * function Foo() { - * this.a = 1; - * } - * - * function Bar() { - * this.c = 3; - * } - * - * Foo.prototype.b = 2; - * Bar.prototype.d = 4; - * - * _.assignIn({ 'a': 0 }, new Foo, new Bar); - * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 } - */ - var assignIn = createAssigner(function(object, source) { - copyObject(source, keysIn(source), object); - }); - - /** - * This method is like `_.assignIn` except that it accepts `customizer` - * which is invoked to produce the assigned values. If `customizer` returns - * `undefined`, assignment is handled by the method instead. The `customizer` - * is invoked with five arguments: (objValue, srcValue, key, object, source). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @alias extendWith - * @category Object - * @param {Object} object The destination object. - * @param {...Object} sources The source objects. - * @param {Function} [customizer] The function to customize assigned values. - * @returns {Object} Returns `object`. - * @see _.assignWith - * @example - * - * function customizer(objValue, srcValue) { - * return _.isUndefined(objValue) ? srcValue : objValue; - * } - * - * var defaults = _.partialRight(_.assignInWith, customizer); - * - * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); - * // => { 'a': 1, 'b': 2 } - */ - var assignInWith = createAssigner(function(object, source, srcIndex, customizer) { - copyObject(source, keysIn(source), object, customizer); - }); - - /** - * This method is like `_.assign` except that it accepts `customizer` - * which is invoked to produce the assigned values. If `customizer` returns - * `undefined`, assignment is handled by the method instead. The `customizer` - * is invoked with five arguments: (objValue, srcValue, key, object, source). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} sources The source objects. - * @param {Function} [customizer] The function to customize assigned values. - * @returns {Object} Returns `object`. - * @see _.assignInWith - * @example - * - * function customizer(objValue, srcValue) { - * return _.isUndefined(objValue) ? srcValue : objValue; - * } - * - * var defaults = _.partialRight(_.assignWith, customizer); - * - * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); - * // => { 'a': 1, 'b': 2 } - */ - var assignWith = createAssigner(function(object, source, srcIndex, customizer) { - copyObject(source, keys(source), object, customizer); - }); - - /** - * Creates an array of values corresponding to `paths` of `object`. - * - * @static - * @memberOf _ - * @since 1.0.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {...(string|string[])} [paths] The property paths to pick. - * @returns {Array} Returns the picked values. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; - * - * _.at(object, ['a[0].b.c', 'a[1]']); - * // => [3, 4] - */ - var at = flatRest(baseAt); - - /** - * Creates an object that inherits from the `prototype` object. If a - * `properties` object is given, its own enumerable string keyed properties - * are assigned to the created object. - * - * @static - * @memberOf _ - * @since 2.3.0 - * @category Object - * @param {Object} prototype The object to inherit from. - * @param {Object} [properties] The properties to assign to the object. - * @returns {Object} Returns the new object. - * @example - * - * function Shape() { - * this.x = 0; - * this.y = 0; - * } - * - * function Circle() { - * Shape.call(this); - * } - * - * Circle.prototype = _.create(Shape.prototype, { - * 'constructor': Circle - * }); - * - * var circle = new Circle; - * circle instanceof Circle; - * // => true - * - * circle instanceof Shape; - * // => true - */ - function create(prototype, properties) { - var result = baseCreate(prototype); - return properties == null ? result : baseAssign(result, properties); - } - - /** - * Assigns own and inherited enumerable string keyed properties of source - * objects to the destination object for all destination properties that - * resolve to `undefined`. Source objects are applied from left to right. - * Once a property is set, additional values of the same property are ignored. - * - * **Note:** This method mutates `object`. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @see _.defaultsDeep - * @example - * - * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); - * // => { 'a': 1, 'b': 2 } - */ - var defaults = baseRest(function(object, sources) { - object = Object(object); - - var index = -1; - var length = sources.length; - var guard = length > 2 ? sources[2] : undefined; - - if (guard && isIterateeCall(sources[0], sources[1], guard)) { - length = 1; - } - - while (++index < length) { - var source = sources[index]; - var props = keysIn(source); - var propsIndex = -1; - var propsLength = props.length; - - while (++propsIndex < propsLength) { - var key = props[propsIndex]; - var value = object[key]; - - if (value === undefined || - (eq(value, objectProto[key]) && !hasOwnProperty.call(object, key))) { - object[key] = source[key]; - } - } - } - - return object; - }); - - /** - * This method is like `_.defaults` except that it recursively assigns - * default properties. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 3.10.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @see _.defaults - * @example - * - * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } }); - * // => { 'a': { 'b': 2, 'c': 3 } } - */ - var defaultsDeep = baseRest(function(args) { - args.push(undefined, customDefaultsMerge); - return apply(mergeWith, undefined, args); - }); - - /** - * This method is like `_.find` except that it returns the key of the first - * element `predicate` returns truthy for instead of the element itself. - * - * @static - * @memberOf _ - * @since 1.1.0 - * @category Object - * @param {Object} object The object to inspect. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {string|undefined} Returns the key of the matched element, - * else `undefined`. - * @example - * - * var users = { - * 'barney': { 'age': 36, 'active': true }, - * 'fred': { 'age': 40, 'active': false }, - * 'pebbles': { 'age': 1, 'active': true } - * }; - * - * _.findKey(users, function(o) { return o.age < 40; }); - * // => 'barney' (iteration order is not guaranteed) - * - * // The `_.matches` iteratee shorthand. - * _.findKey(users, { 'age': 1, 'active': true }); - * // => 'pebbles' - * - * // The `_.matchesProperty` iteratee shorthand. - * _.findKey(users, ['active', false]); - * // => 'fred' - * - * // The `_.property` iteratee shorthand. - * _.findKey(users, 'active'); - * // => 'barney' - */ - function findKey(object, predicate) { - return baseFindKey(object, getIteratee(predicate, 3), baseForOwn); - } - - /** - * This method is like `_.findKey` except that it iterates over elements of - * a collection in the opposite order. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Object - * @param {Object} object The object to inspect. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {string|undefined} Returns the key of the matched element, - * else `undefined`. - * @example - * - * var users = { - * 'barney': { 'age': 36, 'active': true }, - * 'fred': { 'age': 40, 'active': false }, - * 'pebbles': { 'age': 1, 'active': true } - * }; - * - * _.findLastKey(users, function(o) { return o.age < 40; }); - * // => returns 'pebbles' assuming `_.findKey` returns 'barney' - * - * // The `_.matches` iteratee shorthand. - * _.findLastKey(users, { 'age': 36, 'active': true }); - * // => 'barney' - * - * // The `_.matchesProperty` iteratee shorthand. - * _.findLastKey(users, ['active', false]); - * // => 'fred' - * - * // The `_.property` iteratee shorthand. - * _.findLastKey(users, 'active'); - * // => 'pebbles' - */ - function findLastKey(object, predicate) { - return baseFindKey(object, getIteratee(predicate, 3), baseForOwnRight); - } - - /** - * Iterates over own and inherited enumerable string keyed properties of an - * object and invokes `iteratee` for each property. The iteratee is invoked - * with three arguments: (value, key, object). Iteratee functions may exit - * iteration early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @since 0.3.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns `object`. - * @see _.forInRight - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forIn(new Foo, function(value, key) { - * console.log(key); - * }); - * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed). - */ - function forIn(object, iteratee) { - return object == null - ? object - : baseFor(object, getIteratee(iteratee, 3), keysIn); - } - - /** - * This method is like `_.forIn` except that it iterates over properties of - * `object` in the opposite order. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns `object`. - * @see _.forIn - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forInRight(new Foo, function(value, key) { - * console.log(key); - * }); - * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'. - */ - function forInRight(object, iteratee) { - return object == null - ? object - : baseForRight(object, getIteratee(iteratee, 3), keysIn); - } - - /** - * Iterates over own enumerable string keyed properties of an object and - * invokes `iteratee` for each property. The iteratee is invoked with three - * arguments: (value, key, object). Iteratee functions may exit iteration - * early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @since 0.3.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns `object`. - * @see _.forOwnRight - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forOwn(new Foo, function(value, key) { - * console.log(key); - * }); - * // => Logs 'a' then 'b' (iteration order is not guaranteed). - */ - function forOwn(object, iteratee) { - return object && baseForOwn(object, getIteratee(iteratee, 3)); - } - - /** - * This method is like `_.forOwn` except that it iterates over properties of - * `object` in the opposite order. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns `object`. - * @see _.forOwn - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forOwnRight(new Foo, function(value, key) { - * console.log(key); - * }); - * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'. - */ - function forOwnRight(object, iteratee) { - return object && baseForOwnRight(object, getIteratee(iteratee, 3)); - } - - /** - * Creates an array of function property names from own enumerable properties - * of `object`. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to inspect. - * @returns {Array} Returns the function names. - * @see _.functionsIn - * @example - * - * function Foo() { - * this.a = _.constant('a'); - * this.b = _.constant('b'); - * } - * - * Foo.prototype.c = _.constant('c'); - * - * _.functions(new Foo); - * // => ['a', 'b'] - */ - function functions(object) { - return object == null ? [] : baseFunctions(object, keys(object)); - } - - /** - * Creates an array of function property names from own and inherited - * enumerable properties of `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to inspect. - * @returns {Array} Returns the function names. - * @see _.functions - * @example - * - * function Foo() { - * this.a = _.constant('a'); - * this.b = _.constant('b'); - * } - * - * Foo.prototype.c = _.constant('c'); - * - * _.functionsIn(new Foo); - * // => ['a', 'b', 'c'] - */ - function functionsIn(object) { - return object == null ? [] : baseFunctions(object, keysIn(object)); - } - - /** - * Gets the value at `path` of `object`. If the resolved value is - * `undefined`, the `defaultValue` is returned in its place. - * - * @static - * @memberOf _ - * @since 3.7.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to get. - * @param {*} [defaultValue] The value returned for `undefined` resolved values. - * @returns {*} Returns the resolved value. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }] }; - * - * _.get(object, 'a[0].b.c'); - * // => 3 - * - * _.get(object, ['a', '0', 'b', 'c']); - * // => 3 - * - * _.get(object, 'a.b.c', 'default'); - * // => 'default' - */ - function get(object, path, defaultValue) { - var result = object == null ? undefined : baseGet(object, path); - return result === undefined ? defaultValue : result; - } - - /** - * Checks if `path` is a direct property of `object`. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @returns {boolean} Returns `true` if `path` exists, else `false`. - * @example - * - * var object = { 'a': { 'b': 2 } }; - * var other = _.create({ 'a': _.create({ 'b': 2 }) }); - * - * _.has(object, 'a'); - * // => true - * - * _.has(object, 'a.b'); - * // => true - * - * _.has(object, ['a', 'b']); - * // => true - * - * _.has(other, 'a'); - * // => false - */ - function has(object, path) { - return object != null && hasPath(object, path, baseHas); - } - - /** - * Checks if `path` is a direct or inherited property of `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @returns {boolean} Returns `true` if `path` exists, else `false`. - * @example - * - * var object = _.create({ 'a': _.create({ 'b': 2 }) }); - * - * _.hasIn(object, 'a'); - * // => true - * - * _.hasIn(object, 'a.b'); - * // => true - * - * _.hasIn(object, ['a', 'b']); - * // => true - * - * _.hasIn(object, 'b'); - * // => false - */ - function hasIn(object, path) { - return object != null && hasPath(object, path, baseHasIn); - } - - /** - * Creates an object composed of the inverted keys and values of `object`. - * If `object` contains duplicate values, subsequent values overwrite - * property assignments of previous values. - * - * @static - * @memberOf _ - * @since 0.7.0 - * @category Object - * @param {Object} object The object to invert. - * @returns {Object} Returns the new inverted object. - * @example - * - * var object = { 'a': 1, 'b': 2, 'c': 1 }; - * - * _.invert(object); - * // => { '1': 'c', '2': 'b' } - */ - var invert = createInverter(function(result, value, key) { - if (value != null && - typeof value.toString != 'function') { - value = nativeObjectToString.call(value); - } - - result[value] = key; - }, constant(identity)); - - /** - * This method is like `_.invert` except that the inverted object is generated - * from the results of running each element of `object` thru `iteratee`. The - * corresponding inverted value of each inverted key is an array of keys - * responsible for generating the inverted value. The iteratee is invoked - * with one argument: (value). - * - * @static - * @memberOf _ - * @since 4.1.0 - * @category Object - * @param {Object} object The object to invert. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {Object} Returns the new inverted object. - * @example - * - * var object = { 'a': 1, 'b': 2, 'c': 1 }; - * - * _.invertBy(object); - * // => { '1': ['a', 'c'], '2': ['b'] } - * - * _.invertBy(object, function(value) { - * return 'group' + value; - * }); - * // => { 'group1': ['a', 'c'], 'group2': ['b'] } - */ - var invertBy = createInverter(function(result, value, key) { - if (value != null && - typeof value.toString != 'function') { - value = nativeObjectToString.call(value); - } - - if (hasOwnProperty.call(result, value)) { - result[value].push(key); - } else { - result[value] = [key]; - } - }, getIteratee); - - /** - * Invokes the method at `path` of `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the method to invoke. - * @param {...*} [args] The arguments to invoke the method with. - * @returns {*} Returns the result of the invoked method. - * @example - * - * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] }; - * - * _.invoke(object, 'a[0].b.c.slice', 1, 3); - * // => [2, 3] - */ - var invoke = baseRest(baseInvoke); - - /** - * Creates an array of the own enumerable property names of `object`. - * - * **Note:** Non-object values are coerced to objects. See the - * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) - * for more details. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.keys(new Foo); - * // => ['a', 'b'] (iteration order is not guaranteed) - * - * _.keys('hi'); - * // => ['0', '1'] - */ - function keys(object) { - return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); - } - - /** - * Creates an array of the own and inherited enumerable property names of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.keysIn(new Foo); - * // => ['a', 'b', 'c'] (iteration order is not guaranteed) - */ - function keysIn(object) { - return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); - } - - /** - * The opposite of `_.mapValues`; this method creates an object with the - * same values as `object` and keys generated by running each own enumerable - * string keyed property of `object` thru `iteratee`. The iteratee is invoked - * with three arguments: (value, key, object). - * - * @static - * @memberOf _ - * @since 3.8.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns the new mapped object. - * @see _.mapValues - * @example - * - * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { - * return key + value; - * }); - * // => { 'a1': 1, 'b2': 2 } - */ - function mapKeys(object, iteratee) { - var result = {}; - iteratee = getIteratee(iteratee, 3); - - baseForOwn(object, function(value, key, object) { - baseAssignValue(result, iteratee(value, key, object), value); - }); - return result; - } - - /** - * Creates an object with the same keys as `object` and values generated - * by running each own enumerable string keyed property of `object` thru - * `iteratee`. The iteratee is invoked with three arguments: - * (value, key, object). - * - * @static - * @memberOf _ - * @since 2.4.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns the new mapped object. - * @see _.mapKeys - * @example - * - * var users = { - * 'fred': { 'user': 'fred', 'age': 40 }, - * 'pebbles': { 'user': 'pebbles', 'age': 1 } - * }; - * - * _.mapValues(users, function(o) { return o.age; }); - * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) - * - * // The `_.property` iteratee shorthand. - * _.mapValues(users, 'age'); - * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) - */ - function mapValues(object, iteratee) { - var result = {}; - iteratee = getIteratee(iteratee, 3); - - baseForOwn(object, function(value, key, object) { - baseAssignValue(result, key, iteratee(value, key, object)); - }); - return result; - } - - /** - * This method is like `_.assign` except that it recursively merges own and - * inherited enumerable string keyed properties of source objects into the - * destination object. Source properties that resolve to `undefined` are - * skipped if a destination value exists. Array and plain object properties - * are merged recursively. Other objects and value types are overridden by - * assignment. Source objects are applied from left to right. Subsequent - * sources overwrite property assignments of previous sources. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 0.5.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @example - * - * var object = { - * 'a': [{ 'b': 2 }, { 'd': 4 }] - * }; - * - * var other = { - * 'a': [{ 'c': 3 }, { 'e': 5 }] - * }; - * - * _.merge(object, other); - * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } - */ - var merge = createAssigner(function(object, source, srcIndex) { - baseMerge(object, source, srcIndex); - }); - - /** - * This method is like `_.merge` except that it accepts `customizer` which - * is invoked to produce the merged values of the destination and source - * properties. If `customizer` returns `undefined`, merging is handled by the - * method instead. The `customizer` is invoked with six arguments: - * (objValue, srcValue, key, object, source, stack). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} sources The source objects. - * @param {Function} customizer The function to customize assigned values. - * @returns {Object} Returns `object`. - * @example - * - * function customizer(objValue, srcValue) { - * if (_.isArray(objValue)) { - * return objValue.concat(srcValue); - * } - * } - * - * var object = { 'a': [1], 'b': [2] }; - * var other = { 'a': [3], 'b': [4] }; - * - * _.mergeWith(object, other, customizer); - * // => { 'a': [1, 3], 'b': [2, 4] } - */ - var mergeWith = createAssigner(function(object, source, srcIndex, customizer) { - baseMerge(object, source, srcIndex, customizer); - }); - - /** - * The opposite of `_.pick`; this method creates an object composed of the - * own and inherited enumerable property paths of `object` that are not omitted. - * - * **Note:** This method is considerably slower than `_.pick`. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The source object. - * @param {...(string|string[])} [paths] The property paths to omit. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'a': 1, 'b': '2', 'c': 3 }; - * - * _.omit(object, ['a', 'c']); - * // => { 'b': '2' } - */ - var omit = flatRest(function(object, paths) { - var result = {}; - if (object == null) { - return result; - } - var isDeep = false; - paths = arrayMap(paths, function(path) { - path = castPath(path, object); - isDeep || (isDeep = path.length > 1); - return path; - }); - copyObject(object, getAllKeysIn(object), result); - if (isDeep) { - result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone); - } - var length = paths.length; - while (length--) { - baseUnset(result, paths[length]); - } - return result; - }); - - /** - * The opposite of `_.pickBy`; this method creates an object composed of - * the own and inherited enumerable string keyed properties of `object` that - * `predicate` doesn't return truthy for. The predicate is invoked with two - * arguments: (value, key). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The source object. - * @param {Function} [predicate=_.identity] The function invoked per property. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'a': 1, 'b': '2', 'c': 3 }; - * - * _.omitBy(object, _.isNumber); - * // => { 'b': '2' } - */ - function omitBy(object, predicate) { - return pickBy(object, negate(getIteratee(predicate))); - } - - /** - * Creates an object composed of the picked `object` properties. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The source object. - * @param {...(string|string[])} [paths] The property paths to pick. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'a': 1, 'b': '2', 'c': 3 }; - * - * _.pick(object, ['a', 'c']); - * // => { 'a': 1, 'c': 3 } - */ - var pick = flatRest(function(object, paths) { - return object == null ? {} : basePick(object, paths); - }); - - /** - * Creates an object composed of the `object` properties `predicate` returns - * truthy for. The predicate is invoked with two arguments: (value, key). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The source object. - * @param {Function} [predicate=_.identity] The function invoked per property. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'a': 1, 'b': '2', 'c': 3 }; - * - * _.pickBy(object, _.isNumber); - * // => { 'a': 1, 'c': 3 } - */ - function pickBy(object, predicate) { - if (object == null) { - return {}; - } - var props = arrayMap(getAllKeysIn(object), function(prop) { - return [prop]; - }); - predicate = getIteratee(predicate); - return basePickBy(object, props, function(value, path) { - return predicate(value, path[0]); - }); - } - - /** - * This method is like `_.get` except that if the resolved value is a - * function it's invoked with the `this` binding of its parent object and - * its result is returned. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to resolve. - * @param {*} [defaultValue] The value returned for `undefined` resolved values. - * @returns {*} Returns the resolved value. - * @example - * - * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; - * - * _.result(object, 'a[0].b.c1'); - * // => 3 - * - * _.result(object, 'a[0].b.c2'); - * // => 4 - * - * _.result(object, 'a[0].b.c3', 'default'); - * // => 'default' - * - * _.result(object, 'a[0].b.c3', _.constant('default')); - * // => 'default' - */ - function result(object, path, defaultValue) { - path = castPath(path, object); - - var index = -1, - length = path.length; - - // Ensure the loop is entered when path is empty. - if (!length) { - length = 1; - object = undefined; - } - while (++index < length) { - var value = object == null ? undefined : object[toKey(path[index])]; - if (value === undefined) { - index = length; - value = defaultValue; - } - object = isFunction(value) ? value.call(object) : value; - } - return object; - } - - /** - * Sets the value at `path` of `object`. If a portion of `path` doesn't exist, - * it's created. Arrays are created for missing index properties while objects - * are created for all other missing properties. Use `_.setWith` to customize - * `path` creation. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 3.7.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {*} value The value to set. - * @returns {Object} Returns `object`. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }] }; - * - * _.set(object, 'a[0].b.c', 4); - * console.log(object.a[0].b.c); - * // => 4 - * - * _.set(object, ['x', '0', 'y', 'z'], 5); - * console.log(object.x[0].y.z); - * // => 5 - */ - function set(object, path, value) { - return object == null ? object : baseSet(object, path, value); - } - - /** - * This method is like `_.set` except that it accepts `customizer` which is - * invoked to produce the objects of `path`. If `customizer` returns `undefined` - * path creation is handled by the method instead. The `customizer` is invoked - * with three arguments: (nsValue, key, nsObject). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {*} value The value to set. - * @param {Function} [customizer] The function to customize assigned values. - * @returns {Object} Returns `object`. - * @example - * - * var object = {}; - * - * _.setWith(object, '[0][1]', 'a', Object); - * // => { '0': { '1': 'a' } } - */ - function setWith(object, path, value, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - return object == null ? object : baseSet(object, path, value, customizer); - } - - /** - * Creates an array of own enumerable string keyed-value pairs for `object` - * which can be consumed by `_.fromPairs`. If `object` is a map or set, its - * entries are returned. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @alias entries - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the key-value pairs. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.toPairs(new Foo); - * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed) - */ - var toPairs = createToPairs(keys); - - /** - * Creates an array of own and inherited enumerable string keyed-value pairs - * for `object` which can be consumed by `_.fromPairs`. If `object` is a map - * or set, its entries are returned. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @alias entriesIn - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the key-value pairs. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.toPairsIn(new Foo); - * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed) - */ - var toPairsIn = createToPairs(keysIn); - - /** - * An alternative to `_.reduce`; this method transforms `object` to a new - * `accumulator` object which is the result of running each of its own - * enumerable string keyed properties thru `iteratee`, with each invocation - * potentially mutating the `accumulator` object. If `accumulator` is not - * provided, a new object with the same `[[Prototype]]` will be used. The - * iteratee is invoked with four arguments: (accumulator, value, key, object). - * Iteratee functions may exit iteration early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @since 1.3.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [accumulator] The custom accumulator value. - * @returns {*} Returns the accumulated value. - * @example - * - * _.transform([2, 3, 4], function(result, n) { - * result.push(n *= n); - * return n % 2 == 0; - * }, []); - * // => [4, 9] - * - * _.transform({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { - * (result[value] || (result[value] = [])).push(key); - * }, {}); - * // => { '1': ['a', 'c'], '2': ['b'] } - */ - function transform(object, iteratee, accumulator) { - var isArr = isArray(object), - isArrLike = isArr || isBuffer(object) || isTypedArray(object); - - iteratee = getIteratee(iteratee, 4); - if (accumulator == null) { - var Ctor = object && object.constructor; - if (isArrLike) { - accumulator = isArr ? new Ctor : []; - } - else if (isObject(object)) { - accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {}; - } - else { - accumulator = {}; - } - } - (isArrLike ? arrayEach : baseForOwn)(object, function(value, index, object) { - return iteratee(accumulator, value, index, object); - }); - return accumulator; - } - - /** - * Removes the property at `path` of `object`. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to unset. - * @returns {boolean} Returns `true` if the property is deleted, else `false`. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 7 } }] }; - * _.unset(object, 'a[0].b.c'); - * // => true - * - * console.log(object); - * // => { 'a': [{ 'b': {} }] }; - * - * _.unset(object, ['a', '0', 'b', 'c']); - * // => true - * - * console.log(object); - * // => { 'a': [{ 'b': {} }] }; - */ - function unset(object, path) { - return object == null ? true : baseUnset(object, path); - } - - /** - * This method is like `_.set` except that accepts `updater` to produce the - * value to set. Use `_.updateWith` to customize `path` creation. The `updater` - * is invoked with one argument: (value). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.6.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {Function} updater The function to produce the updated value. - * @returns {Object} Returns `object`. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }] }; - * - * _.update(object, 'a[0].b.c', function(n) { return n * n; }); - * console.log(object.a[0].b.c); - * // => 9 - * - * _.update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; }); - * console.log(object.x[0].y.z); - * // => 0 - */ - function update(object, path, updater) { - return object == null ? object : baseUpdate(object, path, castFunction(updater)); - } - - /** - * This method is like `_.update` except that it accepts `customizer` which is - * invoked to produce the objects of `path`. If `customizer` returns `undefined` - * path creation is handled by the method instead. The `customizer` is invoked - * with three arguments: (nsValue, key, nsObject). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.6.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {Function} updater The function to produce the updated value. - * @param {Function} [customizer] The function to customize assigned values. - * @returns {Object} Returns `object`. - * @example - * - * var object = {}; - * - * _.updateWith(object, '[0][1]', _.constant('a'), Object); - * // => { '0': { '1': 'a' } } - */ - function updateWith(object, path, updater, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - return object == null ? object : baseUpdate(object, path, castFunction(updater), customizer); - } - - /** - * Creates an array of the own enumerable string keyed property values of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property values. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.values(new Foo); - * // => [1, 2] (iteration order is not guaranteed) - * - * _.values('hi'); - * // => ['h', 'i'] - */ - function values(object) { - return object == null ? [] : baseValues(object, keys(object)); - } - - /** - * Creates an array of the own and inherited enumerable string keyed property - * values of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property values. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.valuesIn(new Foo); - * // => [1, 2, 3] (iteration order is not guaranteed) - */ - function valuesIn(object) { - return object == null ? [] : baseValues(object, keysIn(object)); - } - - /*------------------------------------------------------------------------*/ - - /** - * Clamps `number` within the inclusive `lower` and `upper` bounds. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Number - * @param {number} number The number to clamp. - * @param {number} [lower] The lower bound. - * @param {number} upper The upper bound. - * @returns {number} Returns the clamped number. - * @example - * - * _.clamp(-10, -5, 5); - * // => -5 - * - * _.clamp(10, -5, 5); - * // => 5 - */ - function clamp(number, lower, upper) { - if (upper === undefined) { - upper = lower; - lower = undefined; - } - if (upper !== undefined) { - upper = toNumber(upper); - upper = upper === upper ? upper : 0; - } - if (lower !== undefined) { - lower = toNumber(lower); - lower = lower === lower ? lower : 0; - } - return baseClamp(toNumber(number), lower, upper); - } - - /** - * Checks if `n` is between `start` and up to, but not including, `end`. If - * `end` is not specified, it's set to `start` with `start` then set to `0`. - * If `start` is greater than `end` the params are swapped to support - * negative ranges. - * - * @static - * @memberOf _ - * @since 3.3.0 - * @category Number - * @param {number} number The number to check. - * @param {number} [start=0] The start of the range. - * @param {number} end The end of the range. - * @returns {boolean} Returns `true` if `number` is in the range, else `false`. - * @see _.range, _.rangeRight - * @example - * - * _.inRange(3, 2, 4); - * // => true - * - * _.inRange(4, 8); - * // => true - * - * _.inRange(4, 2); - * // => false - * - * _.inRange(2, 2); - * // => false - * - * _.inRange(1.2, 2); - * // => true - * - * _.inRange(5.2, 4); - * // => false - * - * _.inRange(-3, -2, -6); - * // => true - */ - function inRange(number, start, end) { - start = toFinite(start); - if (end === undefined) { - end = start; - start = 0; - } else { - end = toFinite(end); - } - number = toNumber(number); - return baseInRange(number, start, end); - } - - /** - * Produces a random number between the inclusive `lower` and `upper` bounds. - * If only one argument is provided a number between `0` and the given number - * is returned. If `floating` is `true`, or either `lower` or `upper` are - * floats, a floating-point number is returned instead of an integer. - * - * **Note:** JavaScript follows the IEEE-754 standard for resolving - * floating-point values which can produce unexpected results. - * - * @static - * @memberOf _ - * @since 0.7.0 - * @category Number - * @param {number} [lower=0] The lower bound. - * @param {number} [upper=1] The upper bound. - * @param {boolean} [floating] Specify returning a floating-point number. - * @returns {number} Returns the random number. - * @example - * - * _.random(0, 5); - * // => an integer between 0 and 5 - * - * _.random(5); - * // => also an integer between 0 and 5 - * - * _.random(5, true); - * // => a floating-point number between 0 and 5 - * - * _.random(1.2, 5.2); - * // => a floating-point number between 1.2 and 5.2 - */ - function random(lower, upper, floating) { - if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) { - upper = floating = undefined; - } - if (floating === undefined) { - if (typeof upper == 'boolean') { - floating = upper; - upper = undefined; - } - else if (typeof lower == 'boolean') { - floating = lower; - lower = undefined; - } - } - if (lower === undefined && upper === undefined) { - lower = 0; - upper = 1; - } - else { - lower = toFinite(lower); - if (upper === undefined) { - upper = lower; - lower = 0; - } else { - upper = toFinite(upper); - } - } - if (lower > upper) { - var temp = lower; - lower = upper; - upper = temp; - } - if (floating || lower % 1 || upper % 1) { - var rand = nativeRandom(); - return nativeMin(lower + (rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1)))), upper); - } - return baseRandom(lower, upper); - } - - /*------------------------------------------------------------------------*/ - - /** - * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the camel cased string. - * @example - * - * _.camelCase('Foo Bar'); - * // => 'fooBar' - * - * _.camelCase('--foo-bar--'); - * // => 'fooBar' - * - * _.camelCase('__FOO_BAR__'); - * // => 'fooBar' - */ - var camelCase = createCompounder(function(result, word, index) { - word = word.toLowerCase(); - return result + (index ? capitalize(word) : word); - }); - - /** - * Converts the first character of `string` to upper case and the remaining - * to lower case. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to capitalize. - * @returns {string} Returns the capitalized string. - * @example - * - * _.capitalize('FRED'); - * // => 'Fred' - */ - function capitalize(string) { - return upperFirst(toString(string).toLowerCase()); - } - - /** - * Deburrs `string` by converting - * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) - * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A) - * letters to basic Latin letters and removing - * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to deburr. - * @returns {string} Returns the deburred string. - * @example - * - * _.deburr('déjà vu'); - * // => 'deja vu' - */ - function deburr(string) { - string = toString(string); - return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); - } - - /** - * Checks if `string` ends with the given target string. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to inspect. - * @param {string} [target] The string to search for. - * @param {number} [position=string.length] The position to search up to. - * @returns {boolean} Returns `true` if `string` ends with `target`, - * else `false`. - * @example - * - * _.endsWith('abc', 'c'); - * // => true - * - * _.endsWith('abc', 'b'); - * // => false - * - * _.endsWith('abc', 'b', 2); - * // => true - */ - function endsWith(string, target, position) { - string = toString(string); - target = baseToString(target); - - var length = string.length; - position = position === undefined - ? length - : baseClamp(toInteger(position), 0, length); - - var end = position; - position -= target.length; - return position >= 0 && string.slice(position, end) == target; - } - - /** - * Converts the characters "&", "<", ">", '"', and "'" in `string` to their - * corresponding HTML entities. - * - * **Note:** No other characters are escaped. To escape additional - * characters use a third-party library like [_he_](https://mths.be/he). - * - * Though the ">" character is escaped for symmetry, characters like - * ">" and "/" don't need escaping in HTML and have no special meaning - * unless they're part of a tag or unquoted attribute value. See - * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) - * (under "semi-related fun fact") for more details. - * - * When working with HTML you should always - * [quote attribute values](http://wonko.com/post/html-escaping) to reduce - * XSS vectors. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @example - * - * _.escape('fred, barney, & pebbles'); - * // => 'fred, barney, & pebbles' - */ - function escape(string) { - string = toString(string); - return (string && reHasUnescapedHtml.test(string)) - ? string.replace(reUnescapedHtml, escapeHtmlChar) - : string; - } - - /** - * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+", - * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @example - * - * _.escapeRegExp('[lodash](https://lodash.com/)'); - * // => '\[lodash\]\(https://lodash\.com/\)' - */ - function escapeRegExp(string) { - string = toString(string); - return (string && reHasRegExpChar.test(string)) - ? string.replace(reRegExpChar, '\\$&') - : string; - } - - /** - * Converts `string` to - * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the kebab cased string. - * @example - * - * _.kebabCase('Foo Bar'); - * // => 'foo-bar' - * - * _.kebabCase('fooBar'); - * // => 'foo-bar' - * - * _.kebabCase('__FOO_BAR__'); - * // => 'foo-bar' - */ - var kebabCase = createCompounder(function(result, word, index) { - return result + (index ? '-' : '') + word.toLowerCase(); - }); - - /** - * Converts `string`, as space separated words, to lower case. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the lower cased string. - * @example - * - * _.lowerCase('--Foo-Bar--'); - * // => 'foo bar' - * - * _.lowerCase('fooBar'); - * // => 'foo bar' - * - * _.lowerCase('__FOO_BAR__'); - * // => 'foo bar' - */ - var lowerCase = createCompounder(function(result, word, index) { - return result + (index ? ' ' : '') + word.toLowerCase(); - }); - - /** - * Converts the first character of `string` to lower case. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the converted string. - * @example - * - * _.lowerFirst('Fred'); - * // => 'fred' - * - * _.lowerFirst('FRED'); - * // => 'fRED' - */ - var lowerFirst = createCaseFirst('toLowerCase'); - - /** - * Pads `string` on the left and right sides if it's shorter than `length`. - * Padding characters are truncated if they can't be evenly divided by `length`. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.pad('abc', 8); - * // => ' abc ' - * - * _.pad('abc', 8, '_-'); - * // => '_-abc_-_' - * - * _.pad('abc', 3); - * // => 'abc' - */ - function pad(string, length, chars) { - string = toString(string); - length = toInteger(length); - - var strLength = length ? stringSize(string) : 0; - if (!length || strLength >= length) { - return string; - } - var mid = (length - strLength) / 2; - return ( - createPadding(nativeFloor(mid), chars) + - string + - createPadding(nativeCeil(mid), chars) - ); - } - - /** - * Pads `string` on the right side if it's shorter than `length`. Padding - * characters are truncated if they exceed `length`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.padEnd('abc', 6); - * // => 'abc ' - * - * _.padEnd('abc', 6, '_-'); - * // => 'abc_-_' - * - * _.padEnd('abc', 3); - * // => 'abc' - */ - function padEnd(string, length, chars) { - string = toString(string); - length = toInteger(length); - - var strLength = length ? stringSize(string) : 0; - return (length && strLength < length) - ? (string + createPadding(length - strLength, chars)) - : string; - } - - /** - * Pads `string` on the left side if it's shorter than `length`. Padding - * characters are truncated if they exceed `length`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.padStart('abc', 6); - * // => ' abc' - * - * _.padStart('abc', 6, '_-'); - * // => '_-_abc' - * - * _.padStart('abc', 3); - * // => 'abc' - */ - function padStart(string, length, chars) { - string = toString(string); - length = toInteger(length); - - var strLength = length ? stringSize(string) : 0; - return (length && strLength < length) - ? (createPadding(length - strLength, chars) + string) - : string; - } - - /** - * Converts `string` to an integer of the specified radix. If `radix` is - * `undefined` or `0`, a `radix` of `10` is used unless `value` is a - * hexadecimal, in which case a `radix` of `16` is used. - * - * **Note:** This method aligns with the - * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`. - * - * @static - * @memberOf _ - * @since 1.1.0 - * @category String - * @param {string} string The string to convert. - * @param {number} [radix=10] The radix to interpret `value` by. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {number} Returns the converted integer. - * @example - * - * _.parseInt('08'); - * // => 8 - * - * _.map(['6', '08', '10'], _.parseInt); - * // => [6, 8, 10] - */ - function parseInt(string, radix, guard) { - if (guard || radix == null) { - radix = 0; - } else if (radix) { - radix = +radix; - } - return nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0); - } - - /** - * Repeats the given string `n` times. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to repeat. - * @param {number} [n=1] The number of times to repeat the string. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {string} Returns the repeated string. - * @example - * - * _.repeat('*', 3); - * // => '***' - * - * _.repeat('abc', 2); - * // => 'abcabc' - * - * _.repeat('abc', 0); - * // => '' - */ - function repeat(string, n, guard) { - if ((guard ? isIterateeCall(string, n, guard) : n === undefined)) { - n = 1; - } else { - n = toInteger(n); - } - return baseRepeat(toString(string), n); - } - - /** - * Replaces matches for `pattern` in `string` with `replacement`. - * - * **Note:** This method is based on - * [`String#replace`](https://mdn.io/String/replace). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to modify. - * @param {RegExp|string} pattern The pattern to replace. - * @param {Function|string} replacement The match replacement. - * @returns {string} Returns the modified string. - * @example - * - * _.replace('Hi Fred', 'Fred', 'Barney'); - * // => 'Hi Barney' - */ - function replace() { - var args = arguments, - string = toString(args[0]); - - return args.length < 3 ? string : string.replace(args[1], args[2]); - } - - /** - * Converts `string` to - * [snake case](https://en.wikipedia.org/wiki/Snake_case). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the snake cased string. - * @example - * - * _.snakeCase('Foo Bar'); - * // => 'foo_bar' - * - * _.snakeCase('fooBar'); - * // => 'foo_bar' - * - * _.snakeCase('--FOO-BAR--'); - * // => 'foo_bar' - */ - var snakeCase = createCompounder(function(result, word, index) { - return result + (index ? '_' : '') + word.toLowerCase(); - }); - - /** - * Splits `string` by `separator`. - * - * **Note:** This method is based on - * [`String#split`](https://mdn.io/String/split). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to split. - * @param {RegExp|string} separator The separator pattern to split by. - * @param {number} [limit] The length to truncate results to. - * @returns {Array} Returns the string segments. - * @example - * - * _.split('a-b-c', '-', 2); - * // => ['a', 'b'] - */ - function split(string, separator, limit) { - if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) { - separator = limit = undefined; - } - limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0; - if (!limit) { - return []; - } - string = toString(string); - if (string && ( - typeof separator == 'string' || - (separator != null && !isRegExp(separator)) - )) { - separator = baseToString(separator); - if (!separator && hasUnicode(string)) { - return castSlice(stringToArray(string), 0, limit); - } - } - return string.split(separator, limit); - } - - /** - * Converts `string` to - * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). - * - * @static - * @memberOf _ - * @since 3.1.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the start cased string. - * @example - * - * _.startCase('--foo-bar--'); - * // => 'Foo Bar' - * - * _.startCase('fooBar'); - * // => 'Foo Bar' - * - * _.startCase('__FOO_BAR__'); - * // => 'FOO BAR' - */ - var startCase = createCompounder(function(result, word, index) { - return result + (index ? ' ' : '') + upperFirst(word); - }); - - /** - * Checks if `string` starts with the given target string. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to inspect. - * @param {string} [target] The string to search for. - * @param {number} [position=0] The position to search from. - * @returns {boolean} Returns `true` if `string` starts with `target`, - * else `false`. - * @example - * - * _.startsWith('abc', 'a'); - * // => true - * - * _.startsWith('abc', 'b'); - * // => false - * - * _.startsWith('abc', 'b', 1); - * // => true - */ - function startsWith(string, target, position) { - string = toString(string); - position = position == null - ? 0 - : baseClamp(toInteger(position), 0, string.length); - - target = baseToString(target); - return string.slice(position, position + target.length) == target; - } - - /** - * Creates a compiled template function that can interpolate data properties - * in "interpolate" delimiters, HTML-escape interpolated data properties in - * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data - * properties may be accessed as free variables in the template. If a setting - * object is given, it takes precedence over `_.templateSettings` values. - * - * **Note:** In the development build `_.template` utilizes - * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) - * for easier debugging. - * - * For more information on precompiling templates see - * [lodash's custom builds documentation](https://lodash.com/custom-builds). - * - * For more information on Chrome extension sandboxes see - * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category String - * @param {string} [string=''] The template string. - * @param {Object} [options={}] The options object. - * @param {RegExp} [options.escape=_.templateSettings.escape] - * The HTML "escape" delimiter. - * @param {RegExp} [options.evaluate=_.templateSettings.evaluate] - * The "evaluate" delimiter. - * @param {Object} [options.imports=_.templateSettings.imports] - * An object to import into the template as free variables. - * @param {RegExp} [options.interpolate=_.templateSettings.interpolate] - * The "interpolate" delimiter. - * @param {string} [options.sourceURL='lodash.templateSources[n]'] - * The sourceURL of the compiled template. - * @param {string} [options.variable='obj'] - * The data object variable name. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Function} Returns the compiled template function. - * @example - * - * // Use the "interpolate" delimiter to create a compiled template. - * var compiled = _.template('hello <%= user %>!'); - * compiled({ 'user': 'fred' }); - * // => 'hello fred!' - * - * // Use the HTML "escape" delimiter to escape data property values. - * var compiled = _.template('<%- value %>'); - * compiled({ 'value': ' + @@ -95,13 +101,26 @@ Angular
-
一套框架,多种平台
移动 & 桌面
+
The modern web
developer's platform

- 该网站需要浏览器支持 JavaScript + This website requires JavaScript.

+ + + + diff --git a/aio/src/main.ts b/aio/src/main.ts index 00ca40d010..dc96f6ad56 100644 --- a/aio/src/main.ts +++ b/aio/src/main.ts @@ -8,7 +8,5 @@ if (environment.production) { enableProdMode(); } -document.addEventListener('DOMContentLoaded', () => { - platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.error(err)); -}); +platformBrowserDynamic().bootstrapModule(AppModule); + diff --git a/aio/src/polyfills.ts b/aio/src/polyfills.ts index 5508a41d96..ced2732a9c 100644 --- a/aio/src/polyfills.ts +++ b/aio/src/polyfills.ts @@ -18,7 +18,9 @@ * BROWSER POLYFILLS */ -/** IE11 requires the following for NgClass support on SVG elements */ +/** + * IE11 requires the following for NgClass support on SVG elements + */ // import 'classlist.js'; // Run `npm install --save classlist.js`. /** @@ -55,7 +57,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** @@ -63,11 +65,3 @@ import 'zone.js/dist/zone'; // Included with Angular CLI. */ // Custom Elements polyfill. Required for browsers that do not natively support Custom Elements. import '@webcomponents/custom-elements'; -// Custom Element ES5 shim. Required for browsers that natively support Custom Elements, but do not -// support ES2015 modules. -// NOTE: Chrome, Firefox and Safari should not need this, because they added support for ES2015 -// modules before Custom Elements. It is still required for some other (less common) browsers: -// - UC browser for android 11.8 (~3.5% global usage) -// - Samsung browser 5.0-8.1 (~0.43% global usage) -// - Opera 41-47 (~0.02% global usage) -import '@webcomponents/custom-elements/src/native-shim'; diff --git a/aio/src/pwa-manifest.json b/aio/src/pwa-manifest.json index 4a106d0345..12f667f483 100644 --- a/aio/src/pwa-manifest.json +++ b/aio/src/pwa-manifest.json @@ -1,6 +1,9 @@ { - "short_name": "angular.io", "name": "Angular Documentation", + "short_name": "angular.io", + "background_color": "#1976d2", + "theme_color": "#1976d2", + "display": "standalone", "icons": [ { "src": "assets/images/favicons/favicon-194x194.png", @@ -11,10 +14,39 @@ "src": "assets/images/favicons/favicon-144x144.png", "sizes": "144x144", "type": "image/png" + }, + { + "src": "assets/images/favicons/favicon-144x144-maskable.png", + "sizes": "144x144", + "type": "image/png", + "purpose": "maskable" } ], "start_url": "/?utm_source=homescreen", - "background_color": "#1976d2", - "theme_color": "#1976d2", - "display": "standalone" + "shortcuts": [ + { + "name": "Go to API Reference", + "short_name": "API", + "description": "Go to the Angular API reference page.", + "url": "/api?utm_source=homescreen" + }, + { + "name": "Go to Glossary", + "short_name": "Glossary", + "description": "Go to the glossary page: A list of common Angular terms and their explanation.", + "url": "/guide/glossary?utm_source=homescreen" + }, + { + "name": "Go to Resources", + "short_name": "Resources", + "description": "Go to the resources page: A list of Angular resouces, such as development tooling, UI libraries, books, courses, community publications, podcasts, etc.", + "url": "/resources?utm_source=homescreen" + }, + { + "name": "Go to Tutorial: Tour of Heroes", + "short_name": "Tutorial", + "description": "Go to the \"Tour of Heroes\" tutorial page: Learn how to create your first Angular application.", + "url": "/tutorial?utm_source=homescreen" + } + ] } diff --git a/aio/src/styles/0-base/_typography.scss b/aio/src/styles/0-base/_typography.scss index 20619f775e..200da433c5 100755 --- a/aio/src/styles/0-base/_typography.scss +++ b/aio/src/styles/0-base/_typography.scss @@ -6,20 +6,21 @@ body { font-family: $main-font; margin: 0; color: $darkgray; - @include font-size(14); + @include font-size(16); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } h1, h2, h3, h4, h5, h6 { color: $deepgray; + margin-bottom: 0; + font-weight: 500; } h1 { - @include font-size(24); - font-weight: 500; + @include font-size(40); display: inline-block; - margin: 8px 0px; + margin: 1.6rem 0; @media screen and (max-width: 600px) { margin-top: 0; @@ -27,38 +28,41 @@ h1 { } h2 { - @include font-size(22); - font-weight: 500; - margin: 32px 0px 24px; + border-top: 1px solid $lightgray; clear: both; + @include font-size(32); + margin-top: 4rem; + padding-top: 4rem; + + @include marketing-pages($extraSelectors: ('.page-api'), $nestParentSelector: true) { + border-top: 0; + margin-top: 2rem; + padding-top: 0; + } } h3 { - @include font-size(20); - font-weight: 400; - margin: 24px 0px 12px; + @include font-size(24); + margin-top: 3rem; clear: both; } h4 { - @include font-size(18); - font-weight: 400; - margin: 8px 0px; + @include font-size(20); + margin-top: 3rem; clear: both; } h5 { - @include font-size(16); - font-weight: 500; - margin: 8px 0px; + @include font-size(18); + margin-top: 2rem; clear: both; } h6 { @include font-size(16); - font-weight: 500; color: $mediumgray; - margin: 8px 0px; + margin-top: 2rem; clear: both; } @@ -87,18 +91,19 @@ ol, li, input, a { - @include font-size(14); - @include line-height(24); - @include letter-spacing(0.3); + @include font-size(16); + @include line-height(32); + font-family: inherit; font-weight: 400; color: $darkgray; + & > em { @include letter-spacing(0.3); } } p { - margin: 14px 0 0; + margin: 1em 0; } p + ul { @@ -122,6 +127,7 @@ ol ol { li { padding-bottom: 8px; + @include line-height(24); p { margin: 0; @@ -160,7 +166,6 @@ table tbody th { td { font-weight: 400; padding: 8px 30px; - @include letter-spacing(0.3); > p, ul { @@ -188,43 +193,40 @@ code { .sidenav-content a { color: $blue; + &:hover { - color: $mediumgray; + text-decoration: underline; } } -// The following css rule adds an icon to external links in the docs area. -// The following `folder-*` classes are applied to the `doc-viewer`component when it is displaying docs for these areas of the documentation. -// We add the icon to all external links which are identified as absolute links (those that start with `http` or https`). -// For more info see PR #36601 +// The following css rule adds an icon to external links in the docs area, based on the classes that are applied to the +// `doc-viewer` component when it is displaying docs for the different areas of the documentation. +// We add the icon to all external links which are identified as absolute links (those that start with `http:` or https:`). +// For more info see PR #36601. +@include docs-pages { + aio-doc-viewer { + // The docs-viewer also contain links to GitHub (e.g. the "edit this page" icon) identified with + // the `.github-links` class. We don't want to add the external link icon to these links. + :not(.github-links) { + > a { + &[href^="http:"], + &[href^="https:"] { + display: inline-flex; + padding-right: calc(1em + 0.25rem); + position: relative; -.folder-api, -.folder-cli, -.folder-docs, -.folder-guide, -.folder-start, -.folder-tutorial { - - aio-doc-viewer{ - a { - &[href^="http:"]::after, - &[href^="https:"]::after { - font-family: "Material Icons"; - content: "open_in_new"; - margin-left: 2px; - position: relative; - @include line-height(24); - vertical-align: middle; - } - } - - // The docs-viewer also contain links to GitHub (e.g. the edit this page icon) identified with `.github-links` class. - // We don't want to add the external link icon to these links, so we hide them. - .github-links a { - &[href^="http:"]::after, - &[href^="https:"]::after { - display: none; + &::after { + content: "open_in_new"; + font-family: "Material Icons"; + position: absolute; + right: 0; + } + } } } } } + +.error-text { + color: $brightred; +} diff --git a/aio/src/styles/1-layouts/_content-layout.scss b/aio/src/styles/1-layouts/_content-layout.scss index 8455d1a172..b64103fa32 100644 --- a/aio/src/styles/1-layouts/_content-layout.scss +++ b/aio/src/styles/1-layouts/_content-layout.scss @@ -4,8 +4,12 @@ } .sidenav-content { + display: block; // This is required for browsers that do not recognize `
` as a block + // element (such as IE11). min-height: 100vh; padding: 80px 3rem 2rem; + max-width: 50em; + margin: 0 auto; @media screen and (max-width: 600px) { min-height: 450px; @@ -16,9 +20,10 @@ padding: 80px 1rem 1rem; } - aio-shell.page-docs & { - // padding: 6rem 3rem 3rem 3rem; // THIS CAUSES THE TOP NAV TOOLBAR TO JUMP BETWEEN DOCS AND OTHER PAGES - margin: auto; + @include marketing-pages( + $extraSelectors: ('.page-api', '.page-file-not-found', '.page-guide-cheatsheet'), + $nestParentSelector: true) { + max-width: none; } button { @@ -31,11 +36,3 @@ aio-menu { display: none; } } - -#guide-change-log h2::before { - content: ""; - display: block; - height: 1px; - margin: 24px 0px; - background: $lightgray; -} diff --git a/aio/src/styles/1-layouts/_footer.scss b/aio/src/styles/1-layouts/_footer.scss index b986e31dac..642ef54e65 100644 --- a/aio/src/styles/1-layouts/_footer.scss +++ b/aio/src/styles/1-layouts/_footer.scss @@ -13,104 +13,99 @@ footer { & > * { color: $white; - } - } - - .footer-block { - margin: 0 24px; - vertical-align: top; - } - - a { - color: $white; - text-decoration: none; - z-index: 20; - position: relative; - - &:hover { - text-decoration: underline; + max-width: 50em; } - &:visited { + .footer-block { + margin: 0 24px; + vertical-align: top; + } + + a { + color: $white; text-decoration: none; + z-index: 20; + position: relative; + + &:hover { + text-decoration: underline; + } + + &:visited { + text-decoration: none; + } + + &:focus { + // `outline-offset` is not applied on Chrome on Windows, if `outline-style` is `auto. + outline: 1px solid rgba($white, 0.8); + outline-offset: 2px; + } } - &:focus { - // `outline-offset` is not applied on Chrome on Windows, if `outline-style` is `auto. - outline: 1px solid rgba($white, 0.8); - outline-offset: 2px; + h3 { + @include font-size(16); + text-transform: uppercase; + font-weight: 400; + margin: 8px 0 12px; + color: $white; + + @media (max-width: 600px) { + @include font-size(14); + } } - } - a.action { - cursor: pointer; - } + p { + text-align: center; + margin: 10px auto 5px; - h3 { - @include font-size(16); - text-transform: uppercase; - font-weight: 400; - margin: 8px 0 12px; - color: $white; - } - - p { - text-align: center; - margin: 10px 0px 5px; - - @media (max-width: 480px) { - text-align: left; - } - } - - div.grid-fluid { - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - justify-content: center; - text-align: left; - margin: 0 0 40px; - - ul { - list-style-position: inside; - padding: 0px; - margin: 0px; - - li { - list-style-type: none; - padding: 4px 0; + @media (max-width: 480px) { text-align: left; } } - @media (max-width: 480px) { - flex-direction: column; + div.grid-fluid { + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + justify-content: center; + text-align: left; + margin: 0 auto 40px; - .footer-block { - margin: 8px 24px; + ul { + list-style-position: inside; + padding: 0px; + margin: 0px; + + li { + list-style-type: none; + padding: 4px 0; + text-align: left; + } + } + + @media (max-width: 480px) { + flex-direction: column; + + .footer-block { + margin: 8px 24px; + } } } } - @media (max-width: 600px) { - h3 { - @include font-size(14); - } + &::after { + content: ""; + position: absolute; + z-index: -1; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: url("/assets/images/logos/angular/angular_whiteTransparent_withMargin.png") + top 0 left 0 repeat, + url("/assets/images/logos/angular/angular_whiteTransparent_withMargin.png") + top 80px left 160px repeat; + opacity: 0.05; + background-size: 320px auto; } } - -footer::after { - content: ""; - position: absolute; - z-index: -1; - top: 0; - bottom: 0; - left: 0; - right: 0; - background: url("/assets/images/logos/angular/angular_whiteTransparent_withMargin.png") - top 0 left 0 repeat, - url("/assets/images/logos/angular/angular_whiteTransparent_withMargin.png") - top 80px left 160px repeat; - opacity: 0.05; - background-size: 320px auto; -} diff --git a/aio/src/styles/1-layouts/_layout-global.scss b/aio/src/styles/1-layouts/_layout-global.scss index 2dc2565ef4..13735e0c85 100644 --- a/aio/src/styles/1-layouts/_layout-global.scss +++ b/aio/src/styles/1-layouts/_layout-global.scss @@ -1,34 +1,28 @@ html, body { - height: 100%; + height: 100%; } body, .content { - background-color: $white; + background-color: $white; +} + +.center-layout { + margin: 0 auto; + max-width: 62.5em; +} + +.center-layout-wide { + margin: 0 auto; + max-width: 84em; } .github-links + .content h1 { max-width: 90%; } -.clearfix { - content: ""; - display: table; - clear: both; -} - .clear { - clear: both; -} - -.l-clearfix:after, .clearfix:after { - content: ""; - display: table; - clear: both; -} - -.is-visible { - display: block!important; + clear: both; } .l-flex-wrap { @@ -37,20 +31,16 @@ body, } .flex-center { - display: flex; - justify-content: center; + display: flex; + justify-content: center; } .center { - text-align: center; + text-align: center; } .visually-hidden { - position: absolute !important; - top: -9999px !important; - left: -9999px !important; -} - -.text-uppercase { - text-transform: uppercase; + position: absolute !important; + top: -9999px !important; + left: -9999px !important; } diff --git a/aio/src/styles/1-layouts/_marketing-layout.scss b/aio/src/styles/1-layouts/_marketing-layout.scss index fd20bcfb21..e1d99ef5fe 100644 --- a/aio/src/styles/1-layouts/_marketing-layout.scss +++ b/aio/src/styles/1-layouts/_marketing-layout.scss @@ -106,6 +106,7 @@ section#intro { width: 400px; height: 400px; margin-bottom: 8px; + max-width: none; padding: 0; filter: drop-shadow(0 2px 2px rgba($black, 0.24)); @@ -260,62 +261,35 @@ section#intro { } } -aio-shell { - &.page-resources, &.page-events, &.page-features, &.page-presskit, &.page-contribute { - main { - padding: 0rem 0rem 3rem; - } +@include marketing-pages { + .sidenav-content { + padding: 0 0 3rem; } - &.page-home { - main { - padding: 0; + article { + padding: 3rem; + + @media (max-width: 800px) { + padding: 2.2rem; } } - - &.page-home, &.page-resources, &.page-events, &.page-contribute { - article { - padding: 32px; - - @media (max-width: 800px) { - padding: 24px; - } - } - } - - &.page-features { - article { - padding: 0 3rem; - } - } - - &.page-home, &.page-resources, &.page-events, &.page-features { - - .content img { - @media (max-width: 1300px) { - max-width: none; - } - } - - .feature-section img { - max-width: 70px; - } - - @media (max-width: 600px) { - mat-sidenav-container.sidenav-container { - padding-top: 0; - } - } - } - - .cta-bar .hero-cta { - color: $blue; - } } -.cta-bar.announcement-bar { - background: none; - box-shadow: none; +.page-home { + .sidenav-content { + padding-bottom: 0; + } +} + +.cta-bar { + &.announcement-bar { + background: none; + box-shadow: none; + } + + .hero-cta { + color: $blue; + } } .text-headline { @@ -385,23 +359,12 @@ div[layout=row]{ } .promo-img-container { - - img { - max-width: 90% !important; - } - p { margin: 0 20px; } img { - max-width: 90%; - - - @media (max-width: 599px) { - max-width: 100%; - float: initial !important; - } + max-width: 90% !important; } } } @@ -434,11 +397,8 @@ div[layout=row]{ display: none; } } -} -.page-features .marketing-banner { - margin-bottom: 20px; -} -.events-container{ - overflow-x: auto; + .page-features & { + margin-bottom: 20px; + } } diff --git a/aio/src/styles/1-layouts/_sidenav.scss b/aio/src/styles/1-layouts/_sidenav.scss index 73e101cf8b..1ecf57666a 100644 --- a/aio/src/styles/1-layouts/_sidenav.scss +++ b/aio/src/styles/1-layouts/_sidenav.scss @@ -3,41 +3,8 @@ transition: none; } -aio-nav-menu { - display: block; - margin: 0 auto; - max-width: 260px; - @include font-size(13); - - ul, a { - margin: 0; - } - - &:first-child { - margin-top: 16px; - } - - aio-nav-item div a { - padding-left: 6px; - } -} - -mat-sidenav.mat-sidenav.sidenav { - position: fixed; - top: 64px; - bottom: 0; - left: 0; - padding: 0; - min-width: 260px; - background-color: $white; - box-shadow: 6px 0 6px rgba(0,0,0,0.10); - - &.collapsed { - top: 56px; - } -} - mat-sidenav-container.sidenav-container { + background-color: $white; min-height: 100%; height: auto !important; max-width: 100%; @@ -47,194 +14,161 @@ mat-sidenav-container.sidenav-container { &.has-floating-toc { max-width: 82%; } -} -mat-sidenav-container.sidenav-container.mat-drawer-container.mat-sidenav-container, -mat-sidenav-container .sidenav-content { - background-color: $white; -} - -mat-sidenav-container div.mat-sidenav-content { - height: auto; -} - -.vertical-menu-item { - box-sizing: border-box; - color: $darkgray; - cursor: pointer; - display: flex; - align-items: center; - justify-content: space-between; - overflow-wrap: break-word; - padding: 8px; - text-decoration: none; - text-align: left; - width: 100%; - word-wrap: break-word; - - &:hover { - background-color: $lightgray; - color: $blue; - text-shadow: 0 0 5px #ffffff; + .sidenav-content { + height: auto; } - &:focus { - outline: $focus-outline-onlight auto 2px; - } + mat-sidenav.sidenav { + position: fixed; + top: 64px; + bottom: 0; + left: 0; + min-width: 260px; + background-color: $superlightgray; + border-right: 1px solid $lightgray; - &.selected { - color: $blue; - } + @media (max-width: 599px) { + top: 56px; + } - &.level-2 { - padding-top: 4px; - padding-bottom: 4px; - } - - span { - padding-right: 32px; - } - - //icons _within_ nav - .mat-icon { - height: 24px; - width: 24px; - } -} - -button.vertical-menu-item { - border: none; - background-color: transparent; - padding-top: 10px; - padding-bottom: 10px; - margin: 0; - width: 100%; -} - -.heading { - color: $darkgray; - cursor: pointer; - position: relative; - text-transform: uppercase; -} - -.heading-children { - &.expanded { - visibility: visible; - opacity: 1; - padding-left: 0; - max-height: 4000px; // Arbitrary max-height. Can increase if needed. Must have measurement to transition height. - transition: visibility 500ms, opacity 500ms, max-height 500ms; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - } - - &.collapsed { - overflow: hidden; // Needed to prevent unnecessary sidenav scrollbar. - visibility: hidden; - padding-left: 0; - opacity: 0; - max-height: 1px; // Must have measurement to transition height. - transition: visibility 275ms, opacity 275ms, max-height 280ms; - -webkit-transition-timing-function: ease-out; - transition-timing-function: ease-out; - } -} - -.no-animations { - .heading-children.expanded, - .heading-children.collapsed { - transition: none! important; - } -} - -.level-1 { - font-family: $main-font; - @include font-size(14); - font-weight: 400; - padding-left: 20px; - margin: 0; - transition: background-color 0.2s; - text-transform: uppercase; - - &.expanded .mat-icon, - .level-2.expanded .mat-icon { - @include rotate(90deg); - } - - &:not(.expanded) .mat-icon, - .level-2:not(.expanded) .mat-icon { - @include rotate(0deg); - } - -} - -.level-2 { - color: $mediumgray; - font-family: $main-font; - @include font-size(14); - font-weight: 400; - margin: 0; - padding-left: 32px; - text-transform: none; - - &.expanded .mat-icon, - .level-3.expanded .mat-icon { - @include rotate(90deg); - } - - &:not(.expanded) .mat-icon, - .level-3:not(.expanded) .mat-icon { - @include rotate(0deg); - } -} - -.level-3 { - color: $mediumgray; - font-family: $main-font; - @include font-size(14); - margin: 0; - padding-left: 40px; - text-transform: none; -} - -.level-4 { - color: $mediumgray; - font-family: $main-font; - @include font-size(14); - margin: 0; - padding-left: 48px; - text-transform: none; -} - -aio-nav-menu.top-menu { - padding: 24px 0 0; - - aio-nav-item:last-child div { - border-bottom: 1px solid rgba($mediumgray, 0.5); - } - - aio-nav-item:first-child div { - border-top: 1px solid rgba($mediumgray, 0.5); - } -} - -// Angular Version Selector -mat-sidenav .doc-version { - padding: 8px; - border-top: 1px solid $lightgray; - - select { - outline: none; - width: 100%; - background: rgba($lightgray, 0.5); - height: 32px; - border: 1px solid $lightgray; - color: $darkgray; - - option { - font-family: $main-font; - @include font-size(14); + // Angular Version Selector + .doc-version { + padding: 8px; + border-top: 1px solid $lightgray; + } + } +} + +aio-nav-menu { + display: block; + margin: 0 auto; + max-width: 268px; + + &:first-of-type { + margin-top: 16px; + } + + &:last-of-type { + margin-bottom: 16px; + } + + ul, a { + margin: 0; + } + + aio-nav-item { + .vertical-menu-item { + box-sizing: border-box; + color: $darkgray; + cursor: pointer; + display: flex; + align-items: center; + justify-content: space-between; + overflow-wrap: break-word; + padding: 8px; + text-decoration: none; + text-align: left; + width: 100%; + word-wrap: break-word; + + &:hover { + background-color: $lightgray; + color: $blue; + text-shadow: 0 0 5px $white; + } + + &:focus { + outline: $focus-outline-onlight auto 2px; + } + + &.selected { + color: $darkblue; + } + + span { + padding-right: 32px; + } + + //icons _within_ nav + .mat-icon { + height: 24px; + width: 24px; + } + } + + button.vertical-menu-item { + border: none; + background-color: transparent; + margin: 0; + width: 100%; + } + + .heading-children { + &.expanded { + visibility: visible; + opacity: 1; + padding-left: 0; + max-height: 4000px; // Arbitrary max-height. Can increase if needed. Must have measurement to transition height. + transition: visibility 500ms, opacity 500ms, max-height 500ms; + transition-timing-function: ease-in-out; + } + + &.collapsed { + overflow: hidden; // Needed to prevent unnecessary sidenav scrollbar. + visibility: hidden; + padding-left: 0; + opacity: 0; + max-height: 1px; // Must have measurement to transition height. + transition: visibility 275ms, opacity 275ms, max-height 280ms; + transition-timing-function: ease-out; + } + + .no-animations &.expanded, + .no-animations &.collapsed { + transition: none !important; + } + } + + .level-1 { + @include font-size(16); + @include line-height(28); + font-weight: 400; + padding-left: 20px; + margin: 0; + transition: background-color 0.2s; + } + + .level-2 { + @include font-size(14); + @include line-height(24); + font-weight: 400; + margin: 0; + padding-left: 36px; + } + + .level-3 { + @include font-size(14); + @include line-height(24); + margin: 0; + padding-left: 44px; + } + + .level-4 { + @include font-size(14); + @include line-height(24); + margin: 0; + padding-left: 52px; + } + + .level-1, .level-2, .level-3 { + &.collapsed > .mat-icon { + @include rotate(0deg); + } + + &.expanded > .mat-icon { + @include rotate(90deg); + } } } } diff --git a/aio/src/styles/1-layouts/_top-menu.scss b/aio/src/styles/1-layouts/_top-menu.scss index b6ad206fd5..d6393b528a 100644 --- a/aio/src/styles/1-layouts/_top-menu.scss +++ b/aio/src/styles/1-layouts/_top-menu.scss @@ -5,7 +5,7 @@ $hamburgerShownMargin: 0 8px 0 0; $hamburgerHiddenMargin: 0 16px 0 -64px; // DOCS PAGE / STANDARD: TOPNAV TOOLBAR FIXED -mat-toolbar.mat-toolbar { +mat-toolbar.app-toolbar { position: fixed; top: 0; right: 0; @@ -13,6 +13,30 @@ mat-toolbar.mat-toolbar { z-index: 10; box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.3); + // HOME PAGE OVERRIDE: TOPNAV TOOLBAR + .page-home & { + background-color: $blue; + + @media (min-width: 481px) { + &:not(.transitioning) { + background-color: transparent; + box-shadow: none; + position: absolute; + transition: background-color 0.2s linear; + } + } + } + + // DOCS PAGES OVERRIDE: HAMBURGER + @include docs-pages($nestParentSelector: true) { + @media (min-width: $showTopMenuWidth) { + .hamburger { + // Hamburger shown on non-marketing pages even on large screens. + margin: $hamburgerShownMargin; + } + } + } + mat-toolbar-row { padding: 0 16px 0 0; } @@ -20,227 +44,185 @@ mat-toolbar.mat-toolbar { mat-icon { color: $white; } -} -// HOME PAGE OVERRIDE: TOPNAV TOOLBAR -aio-shell.page-home mat-toolbar.mat-toolbar { - background-color: $blue; + // HAMBURGER BUTTON + .hamburger { + height: 100%; + margin: $hamburgerShownMargin; + padding: 0; - @media (min-width: 481px) { - &:not(.transitioning) { - background-color: transparent; - transition: background-color 0.2s linear; + @media (min-width: $showTopMenuWidth) { + // Hamburger hidden by default on large screens. + // (Will be shown per doc.) + margin: $hamburgerHiddenMargin; + } + + @media (max-width: 480px) { + min-width: 15%; + } + + &:not(.starting) { + transition-duration: 0.4s; + transition-property: color, margin; + transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1); + } + + &:hover { + color: $offwhite; + } + + & .mat-icon { + color: $white; + position: inherit; } } -} -// MARKETING PAGES OVERRIDE: TOPNAV TOOLBAR AND HAMBURGER -aio-shell.page-home mat-toolbar.mat-toolbar, -aio-shell.page-features mat-toolbar.mat-toolbar, -aio-shell.page-events mat-toolbar.mat-toolbar, -aio-shell.page-resources mat-toolbar.mat-toolbar { - box-shadow: none; + // HOME NAV-LINK + .nav-link.home { + cursor: pointer; + margin: 0 16px 0 0; + padding: 8px 0; - // FIXED TOPNAV TOOLBAR FOR SMALL MOBILE - @media (min-width: 481px) { - position: absolute; - } -} - -// DOCS PAGES OVERRIDE: HAMBURGER -aio-shell.folder-api mat-toolbar.mat-toolbar, -aio-shell.folder-cli mat-toolbar.mat-toolbar, -aio-shell.folder-docs mat-toolbar.mat-toolbar, -aio-shell.folder-guide mat-toolbar.mat-toolbar, -aio-shell.folder-start mat-toolbar.mat-toolbar, -aio-shell.folder-tutorial mat-toolbar.mat-toolbar { - @media (min-width: $showTopMenuWidth) { - .hamburger.mat-button { - // Hamburger shown on non-marketing pages even on large screens. - margin: $hamburgerShownMargin; + &:focus { + // `outline-offset` is not applied on Chrome on Windows, if `outline-style` is `auto. + outline: 1px solid $focus-outline-ondark; + outline-offset: 4px; } - } -} -// HAMBURGER BUTTON -.hamburger.mat-button { - height: 100%; - margin: $hamburgerShownMargin; - padding: 0; + @media screen and (max-width: $hideTopMenuWidth) { + padding: 4px 0; + } - @media (min-width: $showTopMenuWidth) { - // Hamburger hidden by default on large screens. - // (Will be shown per doc.) - margin: $hamburgerHiddenMargin; - } + @media screen and (max-width: 480px) { + margin-right: 8px; + } - @media (max-width: 480px) { - min-width: 15%; - } + img { + position: relative; + margin-top: -21px; + top: 12px; + height: 40px; - &:not(.starting) { - transition-duration: 0.4s; - transition-property: color, margin; - transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1); - } - - &:hover { - color: $offwhite; - } - - & .mat-icon { - color: $white; - position: inherit; - } -} - -// HOME NAV-LINK -.nav-link.home { - cursor: pointer; - margin: 0 16px 0 0; - padding: 8px 0; - - &:focus { - // `outline-offset` is not applied on Chrome on Windows, if `outline-style` is `auto. - outline: 1px solid $focus-outline-ondark; - outline-offset: 4px; - } - - @media screen and (max-width: $hideTopMenuWidth) { - padding: 4px 0; - } - - @media screen and (max-width: 480px) { - margin-right: 8px; - } - - img { - position: relative; - margin-top: -21px; - top: 12px; - height: 40px; - - @media (max-width: $hideTopMenuWidth) { - &:hover { - transform: scale(1.1); + @media (max-width: $hideTopMenuWidth) { + &:hover { + transform: scale(1.1); + } } } } -} -// TOP MENU -aio-top-menu { - display: flex; - flex-direction: row; - align-items: center; - width: 80%; - - ul { + // TOP MENU + aio-top-menu { display: flex; flex-direction: row; align-items: center; - list-style-position: inside; - padding: 0px; - margin: 0px; + width: 80%; - li { - padding-bottom: 2px; - list-style-type: none; - cursor: pointer; + ul { + display: flex; + flex-direction: row; + align-items: center; + list-style-position: inside; + padding: 0px; + margin: 0px; + + li { + padding-bottom: 2px; + list-style-type: none; + cursor: pointer; + + &:focus { + outline: none; + } + + a.nav-link { + margin: 0 4px; + padding: 0px; + cursor: pointer; + + .nav-link-inner { + border-radius: 4px; + padding: 8px 16px; + + &:hover { + background: rgba($white, 0.15); + } + } + + &:focus { + outline: none; + + .nav-link-inner { + background: rgba($white, 0.15); + border-radius: 1px; + box-shadow: 0 0 1px 2px $focus-outline-ondark; + } + } + + &:active { + .nav-link-inner { + background: rgba($white, 0.15); + } + } + } + + &.selected { + a.nav-link { + .nav-link-inner { + background: rgba($white, 0.15); + } + } + } + } + } + } + + // SEARCH BOX + aio-search-box.search-container { + display: flex; + justify-content: flex-end; + align-items: center; + width: 100%; + min-width: 150px; + height: 100%; + margin-right: 16px; + + input { + color: $darkgray; + border: none; + border-radius: 100px; + background-color: $white; + padding: 5px 16px; + margin-left: 8px; + width: 180px; + max-width: 240px; + height: 50%; + -webkit-appearance: none; &:focus { outline: none; } - a.nav-link { - margin: 0 4px; - padding: 0px; - cursor: pointer; + @include placeholder { + @include font-size(14); + color: $mediumgray; + } - .nav-link-inner { - border-radius: 4px; - padding: 8px 16px; - - &:hover { - background: rgba($white, 0.15); - } - } + @media (min-width: 1000px) { + transition: width 0.4s ease-in-out; &:focus { - outline: none; - - .nav-link-inner { - background: rgba($white, 0.15); - border-radius: 1px; - box-shadow: 0 0 1px 2px $focus-outline-ondark; - } - } - - &:active { - .nav-link-inner { - background: rgba($white, 0.15); - } + width: 80%; } } - &.selected { - a.nav-link { - .nav-link-inner { - background: rgba($white, 0.15); - } - } + @media (max-width: 480px) { + width: 150px; } } } -} -// SEARCH BOX -aio-search-box.search-container { - display: flex; - justify-content: flex-end; - align-items: center; - width: 100%; - min-width: 150px; - height: 100%; - margin-right: 16px; - - input { - color: $darkgray; - border: none; - border-radius: 100px; - background-color: $white; - padding: 5px 16px; - margin-left: 8px; - width: 180px; - max-width: 240px; - height: 50%; - -webkit-appearance: none; - - &:focus { - outline: none; - } - - @include placeholder { - @include font-size(14); - color: $mediumgray; - } - - @include bp(big) { - transition: width 0.4s ease-in-out; - - &:focus { - width: 80%; - } - } - - @media (max-width: 480px) { - width: 150px; - } - } -} - -// EXTERNAL LINK ICONS -.app-toolbar { + // EXTERNAL LINK ICONS .toolbar-external-icons-container { display: flex; flex-direction: row; diff --git a/aio/src/styles/2-modules/_api-list.scss b/aio/src/styles/2-modules/_api-list.scss index 21c48f8712..ae2162a7ab 100644 --- a/aio/src/styles/2-modules/_api-list.scss +++ b/aio/src/styles/2-modules/_api-list.scss @@ -1,329 +1,148 @@ -/* API EDIT ICON */ -#api { - .api-filter .material-icons { - right: 48px; - } -} - /* API LIST STYLES */ aio-api-list { - div.form-search i.material-icons { - width: 20px; - pointer-events: none; - } + .api-filter { + display: flex; + margin: 0 auto; - .form-search input { - width: 182px; + @media (max-width: 600px) { + flex-direction: column; + margin: 16px auto; + } - @media screen and (max-width: 600px) { - width: 100%; + /* API FILTER MENU */ + aio-select { + width: 200px; + + @media screen and (max-width: 600px) { + width: 100%; + } + } + + .form-select-menu, + .form-search { + margin: 8px; + + @media screen and (max-width: 600px) { + margin-left: 0; + } + } + + aio-select:first-child { + .form-select-menu { + margin-left: 0; + } + } + + .form-search { + position: relative; + float: left; + + input { + box-shadow: 0 2px 2px rgba($black, 0.24), 0 0 2px rgba($black, 0.12); + box-sizing: border-box; + border: 1px solid $white; + border-radius: 4px; + color: $darkgray; + @include font-size(14); + @include line-height(32); + outline: none; + padding: 4px 16px 4px 32px; + transition: all .2s; + width: 182px; + + @include placeholder { + @include font-size(14); + color: $mediumgray; + } + + &:focus { + border: 1px solid $blue-400; + box-shadow: 0 2px 2px rgba($blue-400, 0.24), 0 0 2px rgba($blue-400, 0.12); + } + + @media screen and (max-width: 600px) { + width: 100%; + } + } + + .material-icons { + color: $blue-grey-600; + @include font-size(20); + left: 8px; + pointer-events: none; + position: absolute; + top: 12px; + width: 20px; + z-index: $layer-1; + } + } + + /* API SEARCH ICON */ + .material-icons { + right: 48px; } } + /* LAYOUT */ + .api-list-container { display: flex; flex-direction: column; - margin: 0 auto; - padding-left: 0; + padding: 16px 0; + position: relative; + + @media handheld and (max-width: $phone-breakpoint), + screen and (max-device-width: $phone-breakpoint), + screen and (max-width: $tablet-breakpoint) { + padding: 24px 0 0; + } h2 { margin-top: 16px; - } - } -} - -.api-filter { - display: flex; - margin: 0 auto; - - @media (max-width: 600px) { - flex-direction: column; - margin: 16px auto; - } - - .form-select-menu, - .form-search { - margin: 8px; - - @media screen and (max-width: 600px) { - margin-left: 0; - } - } - - aio-select:first-child .form-select-menu { - margin-left: 0; - } -} - -/* LAYOUT */ - -.docs-content { - position: relative; -} - -.l-content-small { - padding: 16px; - max-width: 1100px; - margin: 0; - - @media handheld and (max-width: $phone-breakpoint), - screen and (max-device-width: $phone-breakpoint), - screen and (max-width: $tablet-breakpoint) { - padding: 24px 0 0; - } -} - -/* SEARCH BAR */ - -.form-search { - position: relative; - - input { - box-shadow: 0 2px 2px rgba($black, 0.24), 0 0 2px rgba($black, 0.12); - box-sizing: border-box; - border: 1px solid $white; - border-radius: 4px; - color: $darkgray; - @include font-size(14); - @include line-height(32); - outline: none; - padding: 4px 16px 4px 32px; - transition: all .2s; - - @include placeholder { - @include font-size(14); - color: $mediumgray; + margin-bottom: 16px; } - &:focus { - border: 1px solid $blue-400; - box-shadow: 0 2px 2px rgba($blue-400, 0.24), 0 0 2px rgba($blue-400, 0.12); - } - } - - .material-icons { - color: $blue-grey-600; - @include font-size(20); - left: 8px; - position: absolute; - top: 12px; - z-index: $layer-1; - } -} - -/* API SYMBOLS */ - -/* SYMBOL CLASS */ - -.symbol { - border-radius: 2px; - box-shadow: 0 1px 2px rgba($black, .24); - color: $white; - display: inline-block; - @include font-size(10); - font-weight: 600; - @include line-height(16); - text-align: center; - width: 16px; - - // SYMBOL TYPES - // Symbol mapping variables in *constants* - @each $name, $symbol in $api-symbols { - &.#{$name} { - background: map-get($symbol, background); - - &:before { - content: map-get($symbol, content); - } - } - } -} - -/* API HOMEE PAGE */ - -/* API FILTER MENU */ - -.api-filter { - aio-select { - width: 200px; - - @media screen and (max-width: 600px) { - width: 100%; - } - - .symbol { - margin-right: 8px; - } - } - - .form-search { - float: left; - } -} - -/* API CLASS LIST */ - -.docs-content .api-list { - list-style: none; - margin: 0 0 32px -8px; - padding: 0; - overflow: hidden; - - @media screen and (max-width: 600px) { - margin: 0 0 0 -8px; - } - - li { - @include font-size(14); - margin: 8px 0; - @include line-height(14); - padding: 0; - float: left; - width: 33%; - overflow: hidden; - min-width: 220px; - text-overflow: ellipsis; - white-space: nowrap; - - .symbol { - margin-right: 8px; - } - - a { - color: $blue-grey-600; - display: inline-block; - @include line-height(16); - padding: 0 16px 0; - text-decoration: none; - transition: all .3s; + /* API CLASS LIST */ + .api-list { + list-style: none; + margin: 0 0 32px -8px; + padding: 0; overflow: hidden; - text-overflow: ellipsis; - &:hover { - background: $blue-grey-50; - color: $blue-500; + @media screen and (max-width: 600px) { + margin: 0 0 0 -8px; } - &.deprecated-api-item { - text-decoration: line-through; + li { + @include font-size(14); + margin: 8px 0; + @include line-height(14); + padding: 0; + float: left; + width: 33%; + overflow: hidden; + min-width: 330px; + text-overflow: ellipsis; + white-space: nowrap; + + a { + color: $blue-grey-600; + @include line-height(16); + padding: 0 16px; + text-decoration: none; + transition: all .3s; + + &:hover { + background: $blue-grey-50; + color: $blue-500; + } + + &.deprecated-api-item { + text-decoration: line-through; + } + } } } } } - -.docs-content .h2-api-docs, -.docs-content .h2-api-docs:first-of-type { - @include font-size(18); - @include line-height(24); - margin-top: 0; -} - -.code-links { - a { - code, .api-doc-code { - color: #1E88E5 !important; - } - } -} - -.openParens { - margin-top: 15px; -} - -.endParens { - margin-bottom: 20px !important; -} - -p { - &.selector { - margin: 0; - } - - &.location-badge { - margin: 0 0 16px 16px !important; - } - - .api-doc-code { - border-bottom: 0; - - :hover { - border-bottom: none; - } - } -} - -.row-margin { - margin-bottom: 36px; - h2 { - @include line-height(28); - } -} - -.code-margin { - margin-bottom: 8px; -} - -.no-bg { - background: none; - padding: 0; -} - -.no-bg-with-indent { - padding-top: 0; - padding-bottom: 0; - padding-left: 16px; - margin-top: 6px; - margin-bottom: 0; - background: none; -} - -.code-background { - padding: 0 5px 0; - - span.pln { - color: #1E88E5 !important; - } -} - -.code-anchor { - cursor: pointer; - text-decoration: none; - font-size: inherit; - - &:hover { - text-decoration: underline; - } -} - -.api-doc-code { - @include font-size(14); - color: #1a2326; - - // the last .pln (white space) creates additional spacing between sections of the api doc. Remove it. - &.no-pln { - .pln:last-child { - display: none; - } - } -} - -@media screen and (max-width: 600px) { - .docs-content { - // Overrides display flex from angular material. - // This was added because Safari doesn't play nice with layout="column". - // Look of API doc in Chrome and Firefox remains the same, and is fixed for Safari. - .layout-xs-column { - display: block !important; - } - } - - .api-doc-code { - @include font-size(12); - } - - p.location-badge { - position: relative; - @include font-size(11); - } -} diff --git a/aio/src/styles/2-modules/_api-pages.scss b/aio/src/styles/2-modules/_api-pages.scss index f9ee828e3a..c4c5a5eb14 100644 --- a/aio/src/styles/2-modules/_api-pages.scss +++ b/aio/src/styles/2-modules/_api-pages.scss @@ -125,17 +125,17 @@ display: flex; justify-content: space-between; - .github-links { - a { - color: $mediumgray; + .github-links { + a { + color: $mediumgray; - .material-icons:hover { - background: none; - color: $blue; + .material-icons:hover { + background: none; + color: $blue; + } } } } - } h3 { margin: 6px 0; @@ -227,7 +227,6 @@ .inherited-members-list { ul { padding: 0; - li { list-style: none; margin-bottom: 12px; @@ -239,7 +238,6 @@ li, a { font-weight: bold; - i { font-weight: normal; } @@ -278,6 +276,7 @@ } h1 { + @include font-size(24); margin: 0; } } diff --git a/aio/src/styles/2-modules/_buttons.scss b/aio/src/styles/2-modules/_buttons.scss index 29702bd354..b91fb0d455 100644 --- a/aio/src/styles/2-modules/_buttons.scss +++ b/aio/src/styles/2-modules/_buttons.scss @@ -1,7 +1,14 @@ /* Button Styles */ -.button, -a.button.mat-button { +button { + font-family: inherit; +} + +// This rule overrides some Angular Material styles defined for `.mat-button` +// (hence we include `.mat-button` in the selector). +a.button.mat-button, +.button { + color: inherit; display: inline-block; @include line-height(32); padding: 0px 16px; @@ -83,18 +90,31 @@ a.button.mat-button { } } -a.filter-button { - width: 140px; - @include font-size(14); - padding: 0px 16px; - margin: 8px; - @include line-height(48); - border: 2px solid $blue; - border-radius: 4px; - &:hover { - background-color: $blue; - color: $white; +.group-buttons { + margin: 16px auto 32px; + + // This rule overrides some Angular Material styles defined for `.mat-button` + // (hence we include `.mat-button` in the selector). + a.button.mat-button.filter-button { + background-color: rgba($blue, 0.2); + border-radius: 4px; + @include font-size(16); + @include line-height(48); + margin: 8px; + padding: 0px 16px; + width: 168px; + + &.selected, + &:hover { + background-color: $blue; + color: $white; + } + + @media (max-width: 480px) { + @include font-size(14); + width: auto; + } } } diff --git a/aio/src/styles/2-modules/_card.scss b/aio/src/styles/2-modules/_card.scss index 15cbbf35fa..7b1ee812d2 100644 --- a/aio/src/styles/2-modules/_card.scss +++ b/aio/src/styles/2-modules/_card.scss @@ -1,83 +1,61 @@ .card-container { - display: flex; - flex-direction: row; - flex-wrap: wrap; - margin: 16px 0; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + margin: 16px 0; - .docs-card { - @include card(194px, 30%); - max-width: 340px; - min-width: 262px; - margin: 24px 8px; - padding-bottom: 48px; - position: relative; + .docs-card { + @include card(194px, 35%); + max-width: 340px; + min-width: 300px; + margin: 24px 16px; + padding-bottom: 48px; + position: relative; - @media screen and (max-width: 600px) { - width: 100%; - margin: 8px auto; - max-width: none; - } + @media screen and (max-width: 600px) { + width: 100%; + margin: 8px auto; + max-width: none; + } - &:hover { - text-decoration: none; + section { + color: $blue; + @include font-size(20); + @include line-height(24); + margin: 0; + padding: 32px 0 24px; + text-transform: none; + text-align: center; + } - section { - color: $blue; - } + p { + color: $darkgray; + @include font-size(13); + @include line-height(24); + padding: 0 16px; + margin: 0; + text-align: center; + } - p { - color: $darkgray; - padding: 0 16px; - } + .card-footer { + color: $darkblue; + background-color: rgba($blue, 0.1); + bottom: 0; + border-top: 0.5px solid $lightgray; + box-sizing: border-box; + @include line-height(48); + left: 0; + position: absolute; + right: 0; + text-align: right; - .card-footer { - @include line-height(32); - padding: 8px 16px; - background-color: rgba($blue, 0.1); - color: $blue; - } - } - - section { - color: $deepgray; - @include font-size(20); - @include line-height(24); - margin: 0; - padding: 32px 0 24px; - text-transform: none; - text-align: center; - } - - p { - color: $darkgray; - @include font-size(13); - @include line-height(24); - padding: 0 16px; - margin: 0; - text-align: center; - } - - .card-footer { - bottom: 0; - border-top: 0.5px solid $lightgray; - box-sizing: border-box; - @include line-height(48); - left: 0; - position: absolute; - right: 0; - text-align: right; + a { color: $mediumgray; - - a { - color: $mediumgray; - @include font-size(13); - } - } - - .card-footer.center { - text-align: center; + @include font-size(13); } } + } } .card-section { @@ -107,5 +85,4 @@ button { text-align: center; } - } diff --git a/aio/src/styles/2-modules/_code.scss b/aio/src/styles/2-modules/_code.scss index 5e87cec15a..f9d588f5dc 100644 --- a/aio/src/styles/2-modules/_code.scss +++ b/aio/src/styles/2-modules/_code.scss @@ -1,15 +1,23 @@ code-example, code-tabs { - clear: both; - display: block; + clear: both; + display: block; - ol { - list-style: decimal; - } + code { + overflow: auto; + } + + ol { + list-style: decimal; + } + + .mat-card { + padding: 0; + border-radius: 5px; + } } code-example { - &:not(.no-box) { background-color: rgba($backgroundgray, 0.2); border: 0.5px solid $lightgray; @@ -19,9 +27,10 @@ code-example { } &.no-box { - pre { + pre.prettyprint { margin: 0; } + code { background-color: transparent; } @@ -30,34 +39,44 @@ code-example { code { overflow: auto; } -} -code-example, code-tabs { - .mat-card { - padding: 0; - border-radius: 5px; + // TERMINAL / SHELL TEXT STYLES + &.code-shell, &[language=sh], &[language=bash] { + background-color: $darkgray; } - code { - overflow: auto; + + header { + background-color: $accentblue; + border-radius: 5px 5px 0 0; + color: $offwhite; + @include font-size(16); + padding: 8px 16px; } } code-tabs { margin: 16px 0; -} -// TERMINAL / SHELL TEXT STYLES + .code-tab-group { + .mat-tab-label { + &:hover { + background: rgba(black, 0.04); + } + } -code-example.code-shell, code-example[language=sh], code-example[language=bash] { - background-color: $darkgray; -} + .mat-tab-body { + overflow-y: hidden; -code-example header { - background-color: $accentblue; - border-radius: 5px 5px 0 0; - color: $offwhite; - @include font-size(16); - padding: 8px 16px; + .mat-tab-body-content { + height: auto; + transform: none; + + .fadeIn { + animation: opacity 2s ease-in; + } + } + } + } } code-example.avoid header, @@ -73,102 +92,114 @@ code-tabs.avoidFile mat-tab-body { border: 0.5px solid $anti-pattern; } -code-tabs div .mat-tab-body-content { - height: auto; -} - -code-tabs .mat-tab-body-wrapper mat-tab-body .mat-tab-body { - overflow-y: hidden; -} - -code-tabs mat-tab-body-content .fadeIn { - animation: opacity 2s ease-in; -} - -aio-code pre { +aio-code { + pre.prettyprint { + position: relative; display: flex; min-height: 32px; margin: 16px 24px; white-space: pre-wrap; align-items: center; - code span { - @include line-height(24); + code { + a { + color: inherit; + } + + span { + @include line-height(24); + } + + ol.linenums { + margin: 0; + color: lighten($mediumgray, 25%); + + li { + margin: 0; + font-family: $code-font; + font-size: 90%; + @include line-height(24); + + &::marker { + color: lighten($mediumgray, 25%); + } + } + } + + .code-missing { + color: $darkred; + } } -} + .copy-button { + position: absolute; + top: -7px; + right: -19px; + padding: 0; + overflow: visible; // This is required for the button to be displayed correctly in IE11. -.code-missing { - color: $darkred; -} + color: $blue-grey-200; + background-color: transparent; + border: none; + cursor: pointer; + &:hover { + color: $mediumgray; + } + } -.copy-button { - position: absolute; - top: -7px; - right: -19px; - padding: 0; - overflow: visible; // This is required for the button to be displayed correctly in IE11. + &.lang-sh, &.lang-bash { + .copy-button { + color: $mediumgray; - color: $blue-grey-200; - background-color: transparent; - border: none; - cursor: pointer; - &:hover { - color: $mediumgray; + &:hover { + color: $lightgray; + } + } + } } } -.lang-sh .copy-button, .lang-bash .copy-button { - color: $mediumgray; - &:hover { - color: $lightgray; +.sidenav-content { + code { + a { + color: $darkblue; + font-size: inherit; + font-weight: inherit; + } } -} -.code-tab-group .mat-tab-label { - &:hover { - background: rgba(black, 0.04); + :not(h1):not(h2):not(h3):not(h4):not(h5):not(h6):not(pre) > code { + background-color: rgba($lightgray, 0.5); + border-radius: 4px; + color: $deepgray; + padding: 4px; } - white-space: nowrap; -} -.code-tab-group .mat-tab-body-content { - height: auto; - transform: none; -} + .page-guide-cheatsheet & { + td:first-of-type, + th { + code { + background-color: inherit; + padding: 0; + white-space: pre-wrap; + } + } + } + .code-anchor { + cursor: pointer; + text-decoration: none; + font-size: inherit; -[role="tabpanel"] { - transition: none; -} - -.sidenav-content code a { - color: inherit; - font-size: inherit; - font-weight: inherit; + &:hover { + text-decoration: underline; + } + } } /* PRETTY PRINTING STYLES for prettify.js. */ -.prettyprint { - position: relative; -} - -/* Specify class=linenums on a pre to get line numbering */ -ol.linenums { - margin: 0; - font-family: $main-font; - color: #B3B6B7; - - li { - margin: 0; - font-family: $code-font; - font-size: 90%; - @include line-height(24); - } -} - /* The following class|color styles are derived from https://github.com/google/code-prettify/blob/master/src/prettify.css*/ /* SPAN elements with the classes below are added by prettyprint. */ @@ -204,8 +235,10 @@ ol.linenums { /* SHELL / TERMINAL CODE BLOCKS */ -code-example.code-shell, code-example[language=sh], code-example[language=bash] { - .pnk, .blk, .pln, .otl, .kwd, .typ, .tag, .str, .atv, .atn, .com, .lit, .pun, .dec { - color: $codegreen; +code-example { + &.code-shell, &[language=sh], &[language=bash] { + .pnk, .blk, .pln, .otl, .kwd, .typ, .tag, .str, .atv, .atn, .com, .lit, .pun, .dec { + color: $codegreen; + } } } diff --git a/aio/src/styles/2-modules/_contribute.scss b/aio/src/styles/2-modules/_contribute.scss index c6ab9bcc04..0b3b3b3937 100644 --- a/aio/src/styles/2-modules/_contribute.scss +++ b/aio/src/styles/2-modules/_contribute.scss @@ -5,7 +5,6 @@ .card-section { justify-content: space-between; - max-width: 880px; @media (max-width: 600px) { flex-direction: column; @@ -13,7 +12,8 @@ > :first-child { margin-right: 2rem; - width: 60%; + width: 67%; + @media (max-width: 600px) { width: 100%; } diff --git a/aio/src/styles/2-modules/_contributor.scss b/aio/src/styles/2-modules/_contributor.scss index 30570f55a8..cf367318a4 100644 --- a/aio/src/styles/2-modules/_contributor.scss +++ b/aio/src/styles/2-modules/_contributor.scss @@ -1,11 +1,12 @@ aio-contributor-list { - @media handheld and (max-width: 480px), screen and (max-width: 480px), screen and (max-width: 900px) { - .grid-fluid { - width: auto; - } + .contributor-group { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; } - @media handheld and (max-width: 480px), screen and (max-width: 480px), screen and (max-width: 900px) { + @media handheld and (max-width: 480px), screen and (max-width: 900px) { .grid-fluid { margin-left: 20px; margin-right: 20px; @@ -16,32 +17,6 @@ aio-contributor-list { } } -.group-buttons { - margin: 32px auto; - - @media (max-width: 480px) { - .filter-button.button { - font-size: 1.2rem; - padding: 0; - margin: 0; - } - } - - a { - &.selected { - background-color: $blue; - color: $white; - } - } -} - -.contributor-group { - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: center; -} - aio-contributor { background: $white; margin: 8px; @@ -82,6 +57,7 @@ aio-contributor { .info-item { color: $white; + display: flex; @include font-size(14); font-weight: 500; margin: 8px; @@ -89,6 +65,7 @@ aio-contributor { &:hover { color: $lightgray; + text-decoration: none; } &.icon { @@ -108,7 +85,7 @@ aio-contributor { } } - div.contributor-card { + .contributor-card { width: 250px; height: 270px; display: flex; @@ -156,11 +133,11 @@ aio-contributor { @include line-height(14); text-align: left; } - } &.flipped { transform:rotateY(180deg); + .card-front { display: none; } @@ -181,17 +158,6 @@ aio-contributor { transition: all .2s ease-in-out; } - section { - @include font-size(14); - font-weight: 500; - padding: 8px; - margin: 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - text-transform: uppercase; - } - p { cursor: pointer; @include font-size(14); diff --git a/aio/src/styles/2-modules/_deploy-theme.scss b/aio/src/styles/2-modules/_deploy-theme.scss index 89ddd3cb00..c90fb92d49 100644 --- a/aio/src/styles/2-modules/_deploy-theme.scss +++ b/aio/src/styles/2-modules/_deploy-theme.scss @@ -1,11 +1,11 @@ aio-shell.mode-archive { - @include deployTheme(#263238, #78909C); + @include deploy-theme($blue-grey-900, $blue-grey-400); } aio-shell.mode-next { - @include deployTheme(#DD0031, #C3002F); + @include deploy-theme($brightred, $darkred); } aio-shell.mode-rc { - @include deployTheme(#DDA302, #C3A300); + @include deploy-theme($tangerine, $darkgoldenrod); } diff --git a/aio/src/styles/2-modules/_details.scss b/aio/src/styles/2-modules/_details.scss index c14e4f2f40..02c71f110f 100644 --- a/aio/src/styles/2-modules/_details.scss +++ b/aio/src/styles/2-modules/_details.scss @@ -2,44 +2,44 @@ * General styling to make detail/summary tags look a bit more material * To get the best out of it you should structure your usage like this: * - * ``` + * ```html *
* Some title *
* Some content *
- *
- * + * + * ``` */ -summary { - cursor: pointer; - @include font-size(16); - position: relative; - padding: 16px 24px; - color: $black; - height: 16px; - display: block; // Remove the built in details marker in FF - - &::-webkit-details-marker { - display: none; // Remove the built in details marker in webkit - } - - overflow: hidden; -} - details { box-shadow: 0 1px 4px 0 rgba($black, 0.37); + > summary { + cursor: pointer; + @include font-size(16); + position: relative; + padding: 16px 24px; + color: $black; + height: 16px; + display: block; // Remove the built in details marker in FF + overflow: hidden; + + &::-webkit-details-marker { + display: none; // Remove the built in details marker in webkit + } + + // Rotate the icon + i.material-icons.expand { + @include rotate(0deg); + + @at-root #{selector-replace(&, 'details', 'details[open]')} { + @include rotate(180deg); + } + } + } + .detail-contents { padding: 16px 24px; } - - // Rotate the icon - summary i.material-icons.expand { - @include rotate(0deg); - } - &[open] > summary i.material-icons.expand { - @include rotate(180deg); - } } diff --git a/aio/src/styles/2-modules/_edit-page-cta.scss b/aio/src/styles/2-modules/_edit-page-cta.scss deleted file mode 100644 index 21fa5e581b..0000000000 --- a/aio/src/styles/2-modules/_edit-page-cta.scss +++ /dev/null @@ -1,10 +0,0 @@ -.edit-page-cta { - font-weight: 400; - @include font-size(14); - color: $blue; - text-align: right; - margin-right: 32px; - display: block; - position: absolute; - right: 0; -} diff --git a/aio/src/styles/2-modules/_features.scss b/aio/src/styles/2-modules/_features.scss index b7e44e00a1..896f48ed10 100644 --- a/aio/src/styles/2-modules/_features.scss +++ b/aio/src/styles/2-modules/_features.scss @@ -9,6 +9,7 @@ .feature-header img { margin: 16px; + max-width: 70px; } .feature-title { @@ -21,8 +22,11 @@ .feature-row { display: flex; flex-wrap: wrap; + justify-content: space-around; // This is required for browsers that do not support + // `space-evenly` (such as IE11). + justify-content: space-evenly; - @media (max-width: 600px) { + @media (max-width: 1057px) { flex-direction: column; } @@ -30,8 +34,13 @@ max-width: 300px; margin: 0 16px; - @media (max-width: 768px) { + @media (max-width: 1057px) { max-width: 100%; + padding: 8px 10%; + } + + @media (max-width: 768px) { + padding: 8px 0; } } } diff --git a/aio/src/styles/2-modules/_filetree.scss b/aio/src/styles/2-modules/_filetree.scss index 2f8251d029..9a62cd56cd 100644 --- a/aio/src/styles/2-modules/_filetree.scss +++ b/aio/src/styles/2-modules/_filetree.scss @@ -7,7 +7,6 @@ .file { display: block; - font-family: $main-font; @include letter-spacing(0.3); @include line-height(32); color: $darkgray; diff --git a/aio/src/styles/2-modules/_heading-anchors.scss b/aio/src/styles/2-modules/_heading-anchors.scss index cbf88cf111..d6ddc3dae1 100644 --- a/aio/src/styles/2-modules/_heading-anchors.scss +++ b/aio/src/styles/2-modules/_heading-anchors.scss @@ -3,12 +3,12 @@ .header-link { color: $mediumgray; - margin: 0 4px; + margin: 0 6px; text-decoration: none; user-select: none; visibility: hidden; - display: inline-block; - vertical-align: text-top; + display: inline-flex; + vertical-align: middle; } &:hover .header-link { diff --git a/aio/src/styles/2-modules/_label.scss b/aio/src/styles/2-modules/_label.scss index 8efca2d6e5..b68bd379fb 100644 --- a/aio/src/styles/2-modules/_label.scss +++ b/aio/src/styles/2-modules/_label.scss @@ -1,67 +1,55 @@ -label.raised, .api-header label { - border-radius: 4px; - padding: 4px 16px; - display: inline; - @include font-size(14); - color: $white; - margin-right: 8px; - font-weight: 500; - text-transform: uppercase; - - @media screen and (max-width: 600px) { - display: block; - margin: 8px 0; - } - - &.page-label { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - background-color: $mist; - color: $mediumgray; - margin-bottom: 8px; - width: 140px; - - .material-icons { - margin-right: 8px; - } - } - - &.property-type-label { - @include font-size(12); - background-color: $darkgray; - color: $white; - text-transform: none; - } -} - .api-header label { + border-radius: 4px; + padding: 2px 10px; + display: inline; + @include font-size(12); + color: $white; + margin-right: 8px; + font-weight: 500; + text-transform: uppercase; - // The API badges should be a little smaller - padding: 2px 10px; + @media screen and (max-width: 600px) { + display: block; + margin: 4px 0; + } + + &.api-status-label { + background-color: $mediumgray; + + &.deprecated, &.security, &.impure-pipe { + background-color: $brightred; + } + } + + &.api-type-label { + background-color: $accentblue; + + @each $name, $symbol in $api-symbols { + &.#{$name} { + background: map-get($symbol, background); + } + } + } + + &.page-label { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + background-color: $mist; + color: $mediumgray; + margin-bottom: 8px; + width: 140px; + + .material-icons { + margin-right: 8px; + } + } + + &.property-type-label { @include font-size(12); - - @media screen and (max-width: 600px) { - margin: 4px 0; - } - - &.api-status-label { - background-color: $mediumgray; - - &.deprecated, &.security, &.impure-pipe { - background-color: $brightred; - } - } - - &.api-type-label { - background-color: $accentblue; - - @each $name, $symbol in $api-symbols { - &.#{$name} { - background: map-get($symbol, background); - } - } - - } + background-color: $darkgray; + color: $white; + text-transform: none; + } } diff --git a/aio/src/styles/2-modules/_modules-dir.scss b/aio/src/styles/2-modules/_modules-dir.scss index 76302968a5..3fc23113e0 100644 --- a/aio/src/styles/2-modules/_modules-dir.scss +++ b/aio/src/styles/2-modules/_modules-dir.scss @@ -5,6 +5,7 @@ @import 'alert'; @import 'api-list'; @import 'api-pages'; + @import 'api-symbols'; @import 'buttons'; @import 'callout'; @import 'card'; @@ -14,9 +15,10 @@ @import 'contributor'; @import 'deploy-theme'; @import 'details'; - @import 'edit-page-cta'; + @import 'errors'; @import 'features'; @import 'filetree'; + @import 'guides'; @import 'heading-anchors'; @import 'hr'; @import 'images'; diff --git a/aio/src/styles/2-modules/_notification.scss b/aio/src/styles/2-modules/_notification.scss index a4cfcb4972..7494602acc 100644 --- a/aio/src/styles/2-modules/_notification.scss +++ b/aio/src/styles/2-modules/_notification.scss @@ -1,7 +1,7 @@ $notificationHeight: 56px; // we need to override some of the toolbar styling -.mat-toolbar mat-toolbar-row.notification-container { +.app-toolbar mat-toolbar-row.notification-container { padding: 0; height: auto; overflow: hidden; @@ -69,29 +69,35 @@ aio-notification { // Here are all the hacks to make the content and sidebars the right height // when the notification is visible .aio-notification-show { - .sidenav-content { - padding-top: 80px + $notificationHeight; - } - - mat-sidenav.mat-sidenav.sidenav { - top: 64px + $notificationHeight; - - @media (max-width: 600px) { - top: 56px + $notificationHeight; - } - } - .toc-container { top: 76px + $notificationHeight; } .search-results { - padding-top: 68px + $notificationHeight; + padding-top: $notificationHeight; } - &.page-home, &.page-resources, &.page-events, &.page-features, &.page-presskit, &.page-contribute { - main { - padding-top: $notificationHeight; + mat-sidenav-container.sidenav-container { + .sidenav-content { + padding-top: 80px + $notificationHeight; + } + + mat-sidenav.sidenav { + top: 64px + $notificationHeight; + + @media (max-width: 600px) { + top: 56px + $notificationHeight; + } + } + } +} + +@include marketing-pages { + &.aio-notification-show { + mat-sidenav-container.sidenav-container { + .sidenav-content { + padding-top: $notificationHeight; + } } } } @@ -104,7 +110,8 @@ aio-notification { .sidenav-content { transition: padding-top 250ms ease; } - mat-sidenav.mat-sidenav.sidenav, .toc-container { + + mat-sidenav.sidenav, .toc-container { transition: top 250ms ease; } } diff --git a/aio/src/styles/2-modules/_presskit.scss b/aio/src/styles/2-modules/_presskit.scss index 88921249ce..2ac62639ab 100644 --- a/aio/src/styles/2-modules/_presskit.scss +++ b/aio/src/styles/2-modules/_presskit.scss @@ -1,84 +1,69 @@ .presskit-container { padding: 0 32px 32px 32px; - h2 { - color: #37474F; + img { + height: 128px; + width: auto; } - .l-space-left-3 { - margin-left: 3 * 8px; - } + .presskit-section { + &:not(:first-child) { + border-top: 1px solid $lightgray; + margin-top: 4rem; + padding-top: 2rem; + } - .cc-by-anchor { - text-decoration: underline; - color: grey !important; - } - - .presskit-row { - margin: 48px 0; - width: 100%; - - .presskit-inner { + .presskit-icon-group { display: flex; - align-items: center; + flex-wrap: wrap; - @media(max-width: 599px) { - flex-direction: column; - } + .presskit-icon-item { + align-items: center; + display: flex; + margin: 1rem; + width: calc(50% - 2rem); - h3 { - font-weight: 500; - margin-top: 0; - margin-bottom: 0; - color: #455A64; - - @media(max-width: 599px) { - padding-bottom: 16px; - } - } - - .transparent-img-bg { - margin-top: 10px; - border-radius: 4px; - width: 128px; - height: 128px; - background-color: #34474F; - } - - ul { - padding: 0; - list-style-type: none; - - @media(max-width: 599px) { - padding: 0 !important; - margin: 0 !important; + @media screen and (max-width: 600px) { + align-items: flex-start; + flex-direction: column; + margin-bottom: 2rem; } - li { - margin: 0 0 8px 0; + .presskit-image-container { + flex: none; + margin-right: 2rem; + + @media (max-width: 600px) { + width: 100%; + margin-right: 0; + } + + .transparent-img { + background-color: $deepgray; + border-radius: 50%; + } + } + + .presskit-links-container { + list-style-type: none; + margin-bottom: 0; + padding: 0; + + a { + display: inline-flex; + padding-right: 3rem; + position: relative; + + &::after { + content: "cloud_download"; + font-family: "Material Icons"; + @include font-size(24); + position: absolute; + right: 0; + } + } } } } - - .presskit-image-container { - - @media(max-width: 599px) { - text-align: center; - } - - img { - height: 128px; - width: auto; - margin-bottom: 8px * 2; - } - } - } - - .presskit-row:first-child { - margin-top: 0; - - @media(max-width: 599px) { - margin-top: 48px; - } } } diff --git a/aio/src/styles/2-modules/_resources.scss b/aio/src/styles/2-modules/_resources.scss index ac6cf5be2f..2e7223231b 100644 --- a/aio/src/styles/2-modules/_resources.scss +++ b/aio/src/styles/2-modules/_resources.scss @@ -1,118 +1,12 @@ -.showcase { - width: 80%; - @media (max-width: 600px) { - width: 100%; - } -} - -.c-resource-nav { - width: 20%; -} - -.resources-container { - position: relative; - - .group-buttons { - - @media (max-width: 480px) { - .button { - font-size: 1.2rem; - padding: 0; - margin: 0; - } - } - } -} - -.grid-fixed:after, .grid-fixed:before { - content: '.'; - clear: both; - display: block; - overflow: hidden; - visibility: hidden; - @include font-size(0); - @include line-height(0); - width: 0; - height: 0; -} - -@media handheld and (max-width: 480px), screen and (max-width: 480px), screen and (max-width: 900px) { - .grid-fixed { - width: auto; - } -} - -@media handheld and (max-width: 480px), screen and (max-width: 480px), screen and (max-width: 900px) { - .grid-fixed .c3, .grid-fixed .c8 { - margin-left: 20px; - margin-right: 20px; - float: none; - display: block; - width: auto; - } -} - -@media handheld and (max-width: 480px), screen and (max-width: 480px), screen and (max-width: 480px) { - .grid-fixed .c3, .grid-fixed .c8 { - margin-left: 0px; - margin-right: 0px; - float: none; - display: block; - width: auto; - } -} - -@media handheld and (max-width: 900px), screen and (max-width: 900px) { - /* line 6, ../scss/_responsive.scss */ - .grid-fixed { - margin: 0 auto; - *zoom: 1; - } - .grid-fixed:after, .grid-fixed:before { - content: '.'; - clear: both; - display: block; - overflow: hidden; - visibility: hidden; - @include font-size(0); - @include line-height(0); - width: 0; - height: 0; - } -} - -@media handheld and (max-width: 480px), screen and (max-width: 480px) { - /* line 6, ../scss/_responsive.scss */ - .grid-fixed { - margin: 0 auto; - *zoom: 1; - } - .grid-fixed:after, .grid-fixed:before { - content: '.'; - clear: both; - display: block; - overflow: hidden; - visibility: hidden; - @include font-size(0); - @include line-height(0); - width: 0; - height: 0; - } -} - aio-resource-list { - - .shadow-1 { + .showcase { transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); box-shadow: 0 1px 4px 0 rgba($black, 0.37); - } - - .showcase { - margin-bottom: 8px * 6; border-radius: 4px; + margin-bottom: 8px * 6; } - .c-resource { + .resource-item { h4 { margin: 0; @include line-height(24); @@ -123,116 +17,25 @@ aio-resource-list { } } - .c-resource-nav { - position: fixed; - top: 142px; - right: 32px; - width: 8px * 20; - z-index: 1; - background-color: #fff; - border-radius: 2px; - - a { - color: #373E41; - text-decoration: none; - } - - .category { - padding: 10px 0; - - .category-link { - display: block; - margin: 2px 0; - padding: 3px 14px; - font-size: 18px !important; - font-size: 1.8rem !important; - - &:hover { - background: #edf0f2; - color: #2B85E7; - } - } - } - - .subcategory { - .subcategory-link { - display: block; - margin: 2px 0; - padding: 4px 14px; - - &:hover { - background: #edf0f2; - color: #2B85E7; - } - } - } - } - - .h-anchor-offset { - display: block; - position: relative; - top: -20px; - visibility: hidden; - } - - .l-flex--column { - display: flex; - flex-direction: column; - } - - .align-items-center { - align-items: center; - } - - .c-resource-header { - margin-bottom: 16px; - } - - .c-contribute { - margin-bottom: 24px; - } - - .c-resource-header h2 { - margin: 0; - } - .subcategory-title { padding: 16px 23px; margin: 0; background-color: $mist; - color: #373E41; - } - - .h-capitalize { - text-transform: capitalize; - } - - .h-hide { - display: none; } .resource-row-link { - color: #1a2326; + display: flex; + flex-direction: column; border: transparent solid 1px; - margin: 0; padding: 16px 23px 16px 23px; - position: relative; - text-decoration: none; transition: all .3s; - } - .resource-row-link:hover { - color: #1a2326; - text-decoration: none; - border-color: #2B85E7; - border-radius: 4px; - box-shadow: 0 8px 8px rgba(1, 67, 163, .24), 0 0 8px rgba(1, 67, 163, .12), 0 6px 18px rgba(43, 133, 231, .12); - transform: translateY(-2px); - } - - @media (max-width: 900px) { - .c-resource-nav { - display: none; + &:hover { + text-decoration: none; + border-color: rgba($blue, 0.5); + border-radius: 4px; + box-shadow: 0 8px 8px rgba(1, 67, 163, .24), 0 0 8px rgba(1, 67, 163, .12), 0 6px 18px rgba(43, 133, 231, .12); + transform: translateY(-2px); } } } diff --git a/aio/src/styles/2-modules/_search-results.scss b/aio/src/styles/2-modules/_search-results.scss index f6357629e3..67ecd17c03 100644 --- a/aio/src/styles/2-modules/_search-results.scss +++ b/aio/src/styles/2-modules/_search-results.scss @@ -4,7 +4,6 @@ aio-search-results { .search-results { display: flex; flex-direction: row; - justify-content: space-around; overflow: auto; padding: 0px 32px; border-top: 68px solid transparent; @@ -20,8 +19,8 @@ aio-search-results { box-sizing: border-box; .search-area { - margin: 16px; - height: 100%; + margin: 0 auto; + padding: 16px; .search-section-header { @include font-size(16); @@ -41,8 +40,11 @@ aio-search-results { .search-result-item { @include font-size(14); + @include line-height(24); color: $lightgray; + display: inline-block; font-weight: normal; + padding: 0.6rem 0; &a { text-decoration: none; @@ -51,10 +53,6 @@ aio-search-results { &:hover { color: $white; } - - .symbol { - margin-right: 8px; - } } &.priority-pages { diff --git a/aio/src/styles/2-modules/_select-menu.scss b/aio/src/styles/2-modules/_select-menu.scss index b54690fe5a..a1232ff312 100644 --- a/aio/src/styles/2-modules/_select-menu.scss +++ b/aio/src/styles/2-modules/_select-menu.scss @@ -1,89 +1,91 @@ /* SELECT MENU */ -.form-select-menu { - position: relative; -} - -.form-select-button { - background: $white; - box-shadow: 0 2px 2px rgba($black, 0.24), 0 0 2px rgba($black, 0.12); - box-sizing: border-box; - border: 1px solid $white; - border-radius: 4px; - color: $blue-grey-600; - @include font-size(14); - font-weight: 400; - @include line-height(32); - outline: none; - padding: 4px 16px; - text-align: left; - width: 100%; - cursor: pointer; - display: flex; - align-items: center; - flex-direction: row; - - strong { - font-weight: 600; - margin-right: 8px; - text-transform: capitalize; - } - - &:focus { - border: 1px solid $blue-400; - box-shadow: 0 2px 2px rgba($blue-400, 0.24), 0 0 2px rgba($blue-400, 0.12); - } - - &[disabled] { - color: lightgrey; - cursor: not-allowed; - } -} - -.form-select-dropdown { - background: $white; - box-shadow: 0 16px 16px rgba($black, 0.24), 0 0 16px rgba($black, 0.12); - border-radius: 4px; - list-style-type: none; - margin: 0; - padding: 0; - position: absolute; - top: 0; - width: 100%; - z-index: $layer-2; - - li { - cursor: pointer; - @include font-size(14); - @include line-height(32); - margin: 0; - padding: 4px 16px 4px 40px; +aio-select { + .form-select-menu { position: relative; - transition: all .2s; - border: 1px solid transparent; + } - &:first-child { - border-radius: 4px 4px 0 0; + .form-select-button { + background: $white; + box-shadow: 0 2px 2px rgba($black, 0.24), 0 0 2px rgba($black, 0.12); + box-sizing: border-box; + border: 1px solid $white; + border-radius: 4px; + color: $blue-grey-600; + @include font-size(14); + font-weight: 400; + @include line-height(32); + outline: none; + padding: 4px 16px; + text-align: left; + width: 100%; + cursor: pointer; + display: flex; + align-items: center; + flex-direction: row; + + strong { + font-weight: 600; + margin-right: 8px; + text-transform: capitalize; } - &:last-child { - border-radius: 0 0 4px 4px; + &:focus { + border: 1px solid $blue-400; + box-shadow: 0 2px 2px rgba($blue-400, 0.24), 0 0 2px rgba($blue-400, 0.12); } - &:hover { - background: $blue-grey-50; - color: $blue-500; + &[disabled] { + color: lightgrey; + cursor: not-allowed; } + } - &.selected { - background-color: $blue-grey-100; - } + .form-select-dropdown { + background: $white; + box-shadow: 0 16px 16px rgba($black, 0.24), 0 0 16px rgba($black, 0.12); + border-radius: 4px; + list-style-type: none; + margin: 0; + padding: 0; + position: absolute; + top: 0; + width: 100%; + z-index: $layer-2; - .symbol { - left: 16px; - position: absolute; - top: 12px; - z-index: $layer-5; + li { + cursor: pointer; + @include font-size(14); + @include line-height(32); + margin: 0; + padding: 4px 16px 4px 40px; + position: relative; + transition: all .2s; + border: 1px solid transparent; + + &:first-child { + border-radius: 4px 4px 0 0; + } + + &:last-child { + border-radius: 0 0 4px 4px; + } + + &:hover { + background: $blue-grey-50; + color: $blue-500; + } + + &.selected { + background-color: $blue-grey-100; + } + + .symbol { + left: 16px; + position: absolute; + top: 12px; + z-index: $layer-5; + } } } } diff --git a/aio/src/styles/2-modules/_table.scss b/aio/src/styles/2-modules/_table.scss index 24b6feb965..bb68b7a7f1 100644 --- a/aio/src/styles/2-modules/_table.scss +++ b/aio/src/styles/2-modules/_table.scss @@ -81,7 +81,7 @@ table { } } -#cheatsheet { +.page-guide-cheatsheet { table tbody td { overflow: auto; @@ -94,11 +94,6 @@ table { display: block; position: relative; max-width: 100%; - - code { - padding: 0; - background-color: inherit; - } } th { @@ -114,7 +109,7 @@ table { } } -.events-container { +.page-events { tr > td, tr > th { width: 33%; } diff --git a/aio/src/styles/2-modules/_toc.scss b/aio/src/styles/2-modules/_toc.scss index 6a71ed21bc..2a8463b9c5 100644 --- a/aio/src/styles/2-modules/_toc.scss +++ b/aio/src/styles/2-modules/_toc.scss @@ -1,3 +1,20 @@ +$tocItemLineHeight: 24; +$tocItemTopPadding: 9; +$tocMarkerRailSize: 1; +$tocMarkerSize: 6; + +@mixin tocMarker($color) { + background: $color; + border-radius: 50%; + content: ''; + height: #{$tocMarkerSize}px; + left: -#{($tocMarkerSize - $tocMarkerRailSize) / 2}px; + position: absolute; + top: calc(#{$tocItemTopPadding}px + #{$tocItemLineHeight / 2}px - #{$tocMarkerSize / 2}px); + top: calc(#{$tocItemTopPadding}px + #{$tocItemLineHeight / 2 / 10}rem - #{$tocMarkerSize / 2}px); + width: #{$tocMarkerSize}px; +} + .toc-container { width: 18%; position: fixed; @@ -8,201 +25,196 @@ overflow-x: hidden; } -.toc-inner { - @include font-size(13); - overflow-y: visible; - padding: 4px 0 0 10px; +aio-toc { + .toc-inner { + @include font-size(13); + overflow-y: visible; + padding: 4px 0 0 10px; - .toc-heading, - .toc-list .h1 { - @include font-size(16); - } - - .toc-heading { - font-weight: 500; - margin: 0 0 16px 8px; - padding: 0; - - &.secondary { - position: relative; - top: -8px; - - &:hover { - color: $accentblue; - } + .toc-heading, + .toc-list .h1 { + @include font-size(16); } - } - button { - &.toc-heading, - &.toc-more-items { - cursor: pointer; - display: inline-block; - background: 0; - background-color: transparent; - border: none; - box-shadow: none; + .toc-heading { + font-weight: 500; + margin: 0 0 16px 8px; padding: 0; - text-align: start; - &.embedded:focus { - outline: none; - background: $lightgray; - } - } - - &.toc-heading { - mat-icon.rotating-icon { - height: 18px; - width: 18px; + &.secondary { position: relative; - left: -4px; - top: 5px; - } + top: -8px; - &:hover:not(.embedded) { - color: $accentblue; + &:hover { + color: $accentblue; + } } } - &.toc-more-items { - color: $mediumgray; - top: 10px; - position: relative; + button { + &.toc-heading, + &.toc-more-items { + cursor: pointer; + display: inline-block; + background: 0; + background-color: transparent; + border: none; + box-shadow: none; + padding: 0; + text-align: start; - &:hover { - color: $accentblue; + &.embedded:focus { + outline: none; + background: $lightgray; + } } - &::after { - content: 'expand_less'; - } + &.toc-heading { + mat-icon.rotating-icon { + height: 18px; + width: 18px; + position: relative; + left: -4px; + top: 5px; + } - &.collapsed::after { - content: 'more_horiz'; - } - } - } - - .mat-icon { - &.collapsed { - @include rotate(0deg); - } - - &:not(.collapsed) { - @include rotate(90deg); - } - } - - ul.toc-list { - list-style-type: none; - margin: 0; - padding: 0 8px 0 0; - - @media (max-width: 800px) { - width: auto; - } - - li { - box-sizing: border-box; - padding: 7px 0 7px 12px; - position: relative; - transition: all 0.3s ease-in-out; - - &.h1:after { - content: ''; - display: block; - height: 1px; - width: 40%; - margin: 7px 0 4px 0; - background: $lightgray; - clear: both; - } - - &.h3 { - padding-left: 24px; - } - - a { - color: lighten($darkgray, 10); - overflow: visible; - @include font-size(12); - @include line-height(16); - display: table-cell; - } - - &:hover { - * { + &:hover:not(.embedded) { color: $accentblue; } } - &.active { - * { - color: $blue; - font-weight: 500; + &.toc-more-items { + color: $mediumgray; + top: 10px; + position: relative; - &:before { - content: ''; - border-radius: 50%; - left: -3px; - top: 12px; - background: $blue; - position: absolute; - width: 6px; - height: 6px; - } + &:hover { + color: $accentblue; + } + + &::after { + content: 'expand_less'; + } + + &.collapsed::after { + content: 'more_horiz'; } } } - &:not(.embedded) li { - &:before { - border-left: 1px solid $lightgray; - bottom: 0; - content: ''; - left: 0; - position: absolute; - top: 0; + .mat-icon { + &.collapsed { + @include rotate(0deg); } - &:first-child:before { - top: 15px; + &:not(.collapsed) { + @include rotate(90deg); + } + } + + ul.toc-list { + list-style-type: none; + margin: 0; + padding: 0 8px 0 0; + + @media (max-width: 800px) { + width: auto; } - &:last-child:before { - bottom: calc(100% - 15px); + li { + box-sizing: border-box; + @include line-height($tocItemLineHeight); + padding: #{$tocItemTopPadding}px 0 #{$tocItemTopPadding}px 12px; + position: relative; + transition: all 0.3s ease-in-out; + + &.h1:after { + content: ''; + display: block; + height: 1px; + width: 40%; + margin: 7px 0 4px 0; + background: $lightgray; + clear: both; + } + + &.h3 { + padding-left: 24px; + } + + a { + color: lighten($darkgray, 10); + overflow: visible; + @include font-size(14); + line-height: inherit; + display: table-cell; + } + + &:hover { + * { + color: $accentblue; + } + } + + &.active { + * { + color: $blue; + font-weight: 500; + } + + a:before { + @include tocMarker($blue); + } + } } - &:not(.active):hover a:before { - content: ''; - border-radius: 50%; - left: -3px; - top: 12px; - background: $lightgray; - position: absolute; - width: 6px; - height: 6px; + &:not(.embedded) li { + &:before { + border-left: #{$tocMarkerRailSize}px solid $lightgray; + bottom: 0; + content: ''; + left: 0; + position: absolute; + top: 0; + } + + &:first-child:before { + top: calc(#{$tocItemTopPadding}px + #{$tocItemLineHeight / 2}px - #{$tocMarkerSize / 2}px); + top: calc(#{$tocItemTopPadding}px + #{$tocItemLineHeight / 2 / 10}rem - #{$tocMarkerSize / 2}px); + } + + &:last-child:before { + bottom: calc(100% - (#{$tocItemTopPadding}px + #{$tocItemLineHeight / 2}px + #{$tocMarkerSize / 2}px)); + bottom: calc(100% - (#{$tocItemTopPadding}px + #{$tocItemLineHeight / 2 / 10}rem + #{$tocMarkerSize / 2}px)); + } + + &:not(.active):hover { + a:before { + @include tocMarker($lightgray); + } + } + } + } + } + + // Alternative TOC View for Smaller Screens + &.embedded { + @media (min-width: 801px) { + display: none; + } + + .toc-inner { + padding: 12px 0 0 0; + + .toc-heading { + margin: 0 0 8px; + } + + &.collapsed { + .secondary { + display: none; + } } } } } - -// Alternative TOC View for Smaller Screens -aio-toc.embedded { - @media (min-width: 801px) { - display: none; - } - - .toc-inner { - padding: 12px 0 0 0; - - .toc-heading { - margin: 0 0 8px; - } - } - - > div.collapsed li.secondary { - display: none; - } -} diff --git a/aio/src/styles/_constants.scss b/aio/src/styles/_constants.scss index 07affee69b..890ca7a974 100755 --- a/aio/src/styles/_constants.scss +++ b/aio/src/styles/_constants.scss @@ -1,6 +1,6 @@ // TYPOGRAPHY $main-font: "Roboto","Helvetica Neue Light","Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif; -$code-font: "Droid Sans Mono", monospace; +$code-font: "Roboto Mono", monospace; // Z-LAYER $layer-1: 1; @@ -11,12 +11,16 @@ $layer-5: 5; // COLOR PALETTE $blue: #1976D2; +$darkblue: #1669bb; $accentblue: #1E88E5; $brightred: #DD0031; $darkred: #C3002F; $white: #FFFFFF; $offwhite: #FAFAFA; +$tangerine: #DDA302; +$darkgoldenrod: #C3A300; $backgroundgray: #F1F1F1; +$superlightgray: #F2F2F2; $lightgray: #DBDBDB; $lightboxgray: #EBEBEB; $mist: #ECEFF1; diff --git a/aio/src/styles/_mixins.scss b/aio/src/styles/_mixins.scss index 1bfb279940..b1a516a1a4 100644 --- a/aio/src/styles/_mixins.scss +++ b/aio/src/styles/_mixins.scss @@ -1,45 +1,3 @@ -/************************************ - - Media queries - - To use these, put this snippet in the appropriate selector: - - @include bp(tiny) { - background-color: purple; - } - - Replace "tiny" with "medium" or "big" as necessary. -*************************************/ - -@mixin bp($point) { - - $bp-xsmall: "(min-width: 320px)"; - $bp-teeny: "(min-width: 480px)"; - $bp-tiny: "(min-width: 600px)"; - $bp-small: "(min-width: 650px)"; - $bp-medium: "(min-width: 800px)"; - $bp-big: "(min-width: 1000px)"; - - @if $point == big { - @media #{$bp-big} { @content; } - } - @else if $point == medium { - @media #{$bp-medium} { @content; } - } - @else if $point == small { - @media #{$bp-small} { @content; } - } - @else if $point == tiny { - @media #{$bp-tiny} { @content; } - } - @else if $point == teeny { - @media #{$bp-teeny} { @content; } - } - @else if $point == xsmall { - @media #{$bp-xsmall} { @content; } - } -} - // REM Font Adjustments @mixin font-size($sizeValue) { font-size: ($sizeValue) + px; @@ -100,7 +58,7 @@ } } -@mixin deployTheme($mainColor, $gradientTargetColor) { +@mixin deploy-theme($mainColor, $gradientTargetColor) { .mat-toolbar.mat-primary, footer { background: linear-gradient(145deg, $mainColor, $gradientTargetColor); } @@ -125,3 +83,110 @@ } } } + +/// Define some styles for docs (i.e. non-marketing) pages. +/// +/// @example scss - Example SCSS: +/// .foo { +/// @include docs-pages { +/// .bar { +/// color: orange; +/// } +/// } +/// } +/// +/// .baz { +/// @include docs-pages($nestParentSelector: true) { +/// .qux { +/// color: orange; +/// } +/// } +/// } +/// +/// @example css - Output CSS: +/// .foo .folder-api .bar, .foo .folder-cli .bar, ... { +/// color: orange; +/// } +/// +/// .folder-api .baz .qux, .folder-cli .baz .qux, ... { +/// color: orange; +/// } +/// +/// @param {boolean} $nestParentSelector +/// If true, the parent selector (`&`) is nested inside the docs pages selectors. +@mixin docs-pages($nestParentSelector: false) { + $selectors: ( + '.folder-api', + '.folder-cli', + '.folder-docs', + '.folder-errors', + '.folder-guide', + '.folder-start', + '.folder-tutorial', + ); + + @if $nestParentSelector and & { + @at-root #{selector-nest(#{$selectors}, &)} { + @content; + } + } @else { + #{$selectors} { + @content; + } + } +} + +/// Define some styles for marketing (i.e. non-docs) pages. +/// +/// @example scss - Example SCSS: +/// .foo { +/// @include marketing-pages { +/// .bar { +/// color: orange; +/// } +/// } +/// } +/// +/// .baz { +/// @include marketing-pages($extraSelectors: ('.other-page'), $nestParentSelector: true) { +/// .qux { +/// color: orange; +/// } +/// } +/// } +/// +/// @example css - Output CSS: +/// .foo .page-about .bar, .foo .page-contribute .bar, ... { +/// color: orange; +/// } +/// +/// .page-about .baz .qux, .page-contribute .baz .qux, ..., .other-page .baz .qux { +/// color: orange; +/// } +/// +/// @param {string[]} $extraSelectors +/// A list of additional page selectors to apply the styles to. +/// @param {boolean} $nestParentSelector +/// If true, the parent selector (`&`) is nested inside the marketing pages selectors. +@mixin marketing-pages($extraSelectors: (), $nestParentSelector: false) { + $marketingPagesSelectors: ( + '.page-about', + '.page-contribute', + '.page-events', + '.page-features', + '.page-home', + '.page-presskit', + '.page-resources', + ); + $selectors: join($marketingPagesSelectors, $extraSelectors, $separator: comma); + + @if $nestParentSelector and & { + @at-root #{selector-nest(#{$selectors}, &)} { + @content; + } + } @else { + #{$selectors} { + @content; + } + } +} diff --git a/aio/src/styles/_print.scss b/aio/src/styles/_print.scss index 240ac41e50..3a1fc50810 100644 --- a/aio/src/styles/_print.scss +++ b/aio/src/styles/_print.scss @@ -18,7 +18,7 @@ page-break-after: avoid; } - ul, ol, img, code-example, table, tr, .alert, .feature { + ul, ol, img, code-example, table, tr, .alert, .feature, .lightbox { page-break-inside: avoid; } diff --git a/aio/src/styles/_translator.scss b/aio/src/styles/_translator.scss deleted file mode 100644 index c851b044c8..0000000000 --- a/aio/src/styles/_translator.scss +++ /dev/null @@ -1,34 +0,0 @@ -.debug { - [translation-result], code-tabs, code-example, img, code, pre, .filetree { - display: none; - } -} - -[translation-result] + [translation-origin=off] { - display: none; -} - -[translation-result] + aio-toc + [translation-origin=off] { - display: none; -} - -[translation-origin=on] { - border-top: 1px dashed #0273D4; -} - -[id^=translations-cn] { - aio-doc-viewer { - blockquote { - color: #455A64; - margin: 0 0 32px 0; - padding: 16px 32px; - background: rgba(236, 239, 241, 0.24); - border-left: 4px solid #00BCD4; - } - } -} - -em { - font-style: normal; - font-weight: bold; -} diff --git a/aio/src/styles/main.scss b/aio/src/styles/main.scss index 5d5e9a8a95..b3edeaa6f1 100755 --- a/aio/src/styles/main.scss +++ b/aio/src/styles/main.scss @@ -12,7 +12,5 @@ @import './1-layouts/layouts-dir'; @import './2-modules/modules-dir'; -@import './translator'; - // import print styles @import './print'; diff --git a/aio/src/test.ts b/aio/src/test.ts index 4e7f1a5bbd..3ee82289a1 100644 --- a/aio/src/test.ts +++ b/aio/src/test.ts @@ -1,8 +1,11 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; -import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; declare const require: any; diff --git a/aio/src/testing/location.service.ts b/aio/src/testing/location.service.ts index 2991580801..429dde495b 100644 --- a/aio/src/testing/location.service.ts +++ b/aio/src/testing/location.service.ts @@ -5,7 +5,7 @@ export class MockLocationService { urlSubject = new BehaviorSubject(this.initialUrl); currentUrl = this.urlSubject.asObservable().pipe(map(url => this.stripSlashes(url))); // strip off query and hash - currentPath = this.currentUrl.pipe(map(url => url.match(/[^?#]*/)![0])); + currentPath = this.currentUrl.pipe(map(url => url.match(/[^?#]*/)?.[0] || '')); search = jasmine.createSpy('search').and.returnValue({}); setSearch = jasmine.createSpy('setSearch'); fullPageNavigationNeeded = jasmine.createSpy('Location.fullPageNavigationNeeded'); @@ -23,4 +23,3 @@ export class MockLocationService { return url.replace(/^\/+/, '').replace(/\/+(\?|#|$)/, '$1'); } } - diff --git a/aio/tests/deployment/e2e/smoke-tests.e2e-spec.ts b/aio/tests/deployment/e2e/smoke-tests.e2e-spec.ts index 77dda0a48f..2d58484fc6 100644 --- a/aio/tests/deployment/e2e/smoke-tests.e2e-spec.ts +++ b/aio/tests/deployment/e2e/smoke-tests.e2e-spec.ts @@ -18,8 +18,8 @@ describe(browser.baseUrl, () => { await page.goTo(''); const text = await page.getDocViewerText(); - expect(text).toContain('one framework'); - expect(text).toContain('mobile & desktop'); + expect(text).toContain('modern web'); + expect(text).toContain('developer\'s platform'); }); describe('(marketing pages)', () => { @@ -103,11 +103,10 @@ describe(browser.baseUrl, () => { }); it('should show relevant results on 404', async () => { - await page.goTo('http/router'); + await page.goTo('common/http'); const results = await page.getSearchResults(); - expect(results).toContain('HttpClient'); - expect(results).toContain('Router'); + expect(results).toContain('common/http package'); }); }); }); diff --git a/aio/tests/deployment/shared/URLS_TO_REDIRECT.txt b/aio/tests/deployment/shared/URLS_TO_REDIRECT.txt index 887ad52883..9c1fbb3be6 100644 --- a/aio/tests/deployment/shared/URLS_TO_REDIRECT.txt +++ b/aio/tests/deployment/shared/URLS_TO_REDIRECT.txt @@ -185,6 +185,7 @@ /guide/service-worker-getstart /guide/service-worker-getting-started /guide/service-worker-comm /guide/service-worker-communications /guide/service-worker-configref /guide/service-worker-config +/guide/updating-to-version-10 /guide/updating-to-version-11 /guide/webpack https://v5.angular.io/guide/webpack /news https://blog.angular.io/ /news.html https://blog.angular.io/ diff --git a/aio/tests/e2e/protractor.conf.js b/aio/tests/e2e/protractor.conf.js index 6a00131893..f11670a403 100644 --- a/aio/tests/e2e/protractor.conf.js +++ b/aio/tests/e2e/protractor.conf.js @@ -21,7 +21,12 @@ exports.config = { }, }, directConnect: true, - SELENIUM_PROMISE_MANAGER: false, + // Keep the Selenium Promise Manager enabled to avoid flakiness on CI. + // See https://github.com/angular/angular/issues/39872 for more details. + // + // TODO(gkalpak): Set this back to `false` to align with CLI-generated apps when the flakiness is + // fixed in the future. + SELENIUM_PROMISE_MANAGER: true, baseUrl: 'http://localhost:4200/', framework: 'jasmine', jasmineNodeOpts: { diff --git a/aio/tests/e2e/src/app.e2e-spec.ts b/aio/tests/e2e/src/app.e2e-spec.ts index 50ac669b7c..e341324858 100644 --- a/aio/tests/e2e/src/app.e2e-spec.ts +++ b/aio/tests/e2e/src/app.e2e-spec.ts @@ -36,7 +36,7 @@ describe('site App', () => { // Test all headings (and sub-headings). expect(await navItemHeadings.count()).toBeGreaterThan(0); - await navItemHeadings.each(heading => testNavItemHeading(heading!, 1)); + await navItemHeadings.each(heading => heading && testNavItemHeading(heading, 1)); // Helpers async function expectToBeCollapsed(elementFinder: ElementFinder) { @@ -63,7 +63,7 @@ describe('site App', () => { // Recursively test child-headings (while this heading is expanded). const nextLevel = level + 1; const childNavItemHeadings = page.getNavItemHeadings(children, nextLevel); - await childNavItemHeadings.each(childHeading => testNavItemHeading(childHeading!, nextLevel)); + await childNavItemHeadings.each(childHeading => childHeading && testNavItemHeading(childHeading, nextLevel)); // Ensure heading does not cause navigation when collapsing. await page.click(heading); @@ -144,7 +144,7 @@ describe('site App', () => { it('should have contributors listed in each group', async () => { // WebDriver calls `scrollIntoView()` on the element to bring it into the visible area of the // browser, before clicking it. By default, this aligns the top of the element to the top of - // the window. As a result, the element may end up behing the fixed top menu, thus being + // the window. As a result, the element may end up behind the fixed top menu, thus being // unclickable. To avoid this, we click the element directly using JavaScript instead. const clickButton = (elementFinder: ElementFinder) => browser.executeScript('arguments[0].click()', elementFinder); @@ -217,11 +217,10 @@ describe('site App', () => { }); it('should search the index for words found in the url', async () => { - await page.navigateTo('http/router'); + await page.navigateTo('common/http'); const results = await page.getSearchResults(); - expect(results).toContain('HttpRequest'); - expect(results).toContain('Router'); + expect(results).toContain('common/http package'); }); }); @@ -233,11 +232,11 @@ describe('site App', () => { expect(await page.ghLinks.get(0).getAttribute('href')) .toMatch(/https:\/\/github\.com\/angular\/angular\/edit\/master\/aio\/content\/tutorial\/toh-pt1\.md\?message=docs%3A%20describe%20your%20change\.\.\./); - await page.navigateTo('guide/http'); + await page.navigateTo('guide/router'); expect(await page.ghLinks.count()).toEqual(1); /* tslint:disable:max-line-length */ expect(await page.ghLinks.get(0).getAttribute('href')) - .toMatch(/https:\/\/github\.com\/angular\/angular\/edit\/master\/aio\/content\/guide\/http\.md\?message=docs%3A%20describe%20your%20change\.\.\./); + .toMatch(/https:\/\/github\.com\/angular\/angular\/edit\/master\/aio\/content\/guide\/router\.md\?message=docs%3A%20describe%20your%20change\.\.\./); }); it('should not be present on top level pages', async () => { diff --git a/aio/tests/e2e/src/app.po.ts b/aio/tests/e2e/src/app.po.ts index 9904eb42ad..f10ab9fe6b 100644 --- a/aio/tests/e2e/src/app.po.ts +++ b/aio/tests/e2e/src/app.po.ts @@ -41,7 +41,7 @@ export class SitePage { async navigateTo(pageUrl: string) { // Navigate to the page, disable animations, and wait for Angular. - await browser.get(`/${pageUrl}`); + await browser.get(`/${pageUrl.replace(/^\//, '')}`); await browser.executeScript('document.body.classList.add(\'no-animations\')'); await browser.waitForAngular(); } diff --git a/aio/tools/examples/README.md b/aio/tools/examples/README.md index 12bf0830ab..20c3459a36 100644 --- a/aio/tools/examples/README.md +++ b/aio/tools/examples/README.md @@ -136,7 +136,7 @@ The [example-boilerplate.js](./example-boilerplate.js) script installs the depen It also contains a function to remove all the boilerplate. It uses `git clean -xdf` to do the job. -It will remove all files that are not tracked by git, **including any new files that you are working on that haven't been stageg yet.** +It will remove all files that are not tracked by git, **including any new files that you are working on that haven't been staged yet.** So, be sure to commit your work before removing the boilerplate. diff --git a/aio/tools/examples/UPDATING.md b/aio/tools/examples/UPDATING.md index a0a4a66d92..beb3612d95 100644 --- a/aio/tools/examples/UPDATING.md +++ b/aio/tools/examples/UPDATING.md @@ -24,7 +24,7 @@ Any necessary changes to boilerplate files will be done automatically through mi > You have to make these changes (if any) manually. > Again, the [angular-cli-diff](https://github.com/cexbrayat/angular-cli-diff) repo can be a useful resource for discovering changes between versions. -- In the [shared/boilerplate/cli/](./shared/boilerplate/cli) directory, run the following commands to migrate the the project to the current versions of Angular CLI and the Angular framework (updated in previous steps): +- In the [shared/boilerplate/cli/](./shared/boilerplate/cli) directory, run the following commands to migrate the project to the current versions of Angular CLI and the Angular framework (updated in previous steps): ```sh # Ensure dependencies are installed. yarn install diff --git a/aio/tools/examples/shared/boilerplate/cli-ajs/package.json b/aio/tools/examples/shared/boilerplate/cli-ajs/package.json index 7976895e6f..e85bf9dffe 100644 --- a/aio/tools/examples/shared/boilerplate/cli-ajs/package.json +++ b/aio/tools/examples/shared/boilerplate/cli-ajs/package.json @@ -26,7 +26,7 @@ "angular-route": "1.8.0", "rxjs": "~6.6.0", "tslib": "^2.0.0", - "zone.js": "~0.10.3" + "zone.js": "~0.11.4" }, "devDependencies": { "@angular-devkit/build-angular": "~0.1100.2", diff --git a/aio/tools/examples/shared/boilerplate/cli/package.json b/aio/tools/examples/shared/boilerplate/cli/package.json index 650e25fa45..b77ad97a8b 100644 --- a/aio/tools/examples/shared/boilerplate/cli/package.json +++ b/aio/tools/examples/shared/boilerplate/cli/package.json @@ -24,7 +24,7 @@ "angular-in-memory-web-api": "~0.11.0", "rxjs": "~6.6.0", "tslib": "^2.0.0", - "zone.js": "~0.10.3" + "zone.js": "~0.11.4" }, "devDependencies": { "@angular-devkit/build-angular": "~0.1100.2", diff --git a/aio/tools/examples/shared/boilerplate/cli/src/environments/environment.ts b/aio/tools/examples/shared/boilerplate/cli/src/environments/environment.ts index 7b4f817adb..30d7bccb19 100644 --- a/aio/tools/examples/shared/boilerplate/cli/src/environments/environment.ts +++ b/aio/tools/examples/shared/boilerplate/cli/src/environments/environment.ts @@ -13,4 +13,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/aio/tools/examples/shared/boilerplate/cli/src/polyfills.ts b/aio/tools/examples/shared/boilerplate/cli/src/polyfills.ts index f38ef7d0e5..0eda6f23d9 100644 --- a/aio/tools/examples/shared/boilerplate/cli/src/polyfills.ts +++ b/aio/tools/examples/shared/boilerplate/cli/src/polyfills.ts @@ -57,7 +57,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS diff --git a/aio/tools/examples/shared/boilerplate/cli/src/test.ts b/aio/tools/examples/shared/boilerplate/cli/src/test.ts index 50193eb0f2..2042356408 100644 --- a/aio/tools/examples/shared/boilerplate/cli/src/test.ts +++ b/aio/tools/examples/shared/boilerplate/cli/src/test.ts @@ -1,6 +1,6 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/aio/tools/examples/shared/boilerplate/cli/tsconfig.json b/aio/tools/examples/shared/boilerplate/cli/tsconfig.json index b7d7a9ad98..741d3212d9 100644 --- a/aio/tools/examples/shared/boilerplate/cli/tsconfig.json +++ b/aio/tools/examples/shared/boilerplate/cli/tsconfig.json @@ -25,7 +25,6 @@ "angularCompilerOptions": { "strictInjectionParameters": true, "strictInputAccessModifiers": true, - // TODO(gkalpak): Fix the code and enable this (i.e. switch from `fullTemplateTypeCheck` to `strictTemplates`). - "fullTemplateTypeCheck": true,// "strictTemplates": true + "strictTemplates": true } } diff --git a/aio/tools/examples/shared/boilerplate/common/src/styles.css b/aio/tools/examples/shared/boilerplate/common/src/styles.css index d4e07e4175..0584e4a793 100644 --- a/aio/tools/examples/shared/boilerplate/common/src/styles.css +++ b/aio/tools/examples/shared/boilerplate/common/src/styles.css @@ -1,36 +1,49 @@ /* Global Styles */ -h1 { - color: #369; +* { font-family: Arial, Helvetica, sans-serif; - font-size: 250%; +} +h1 { + color: #264D73; + font-size: 2.5rem; } h2, h3 { color: #444; - font-family: Arial, Helvetica, sans-serif; font-weight: lighter; } -body { - margin: 2em; +h3 { + font-size: 1.3rem; } -body, input[text], button { +body { + padding: .5rem; + max-width: 1000px; + margin: auto; +} +@media (min-width: 600px) { + body { + padding: 2rem; + } +} +body, input[text] { color: #333; - font-family: Cambria, Georgia; + font-family: Cambria, Georgia, serif; } a { cursor: pointer; - cursor: hand; } button { - font-family: Arial; background-color: #eee; border: none; - padding: 5px 10px; border-radius: 4px; cursor: pointer; - cursor: hand; + color: black; + font-size: 1.2rem; + padding: 1rem; + margin-right: 1rem; + margin-bottom: 1rem; } button:hover { - background-color: #cfd8dc; + background-color: black; + color: white; } button:disabled { background-color: #eee; @@ -45,21 +58,24 @@ nav a { margin-right: 10px; margin-top: 10px; display: inline-block; - background-color: #eee; + background-color: #e8e8e8; + color: #3d3d3d; border-radius: 4px; } -nav a:visited, a:link { - color: #607D8B; -} + nav a:hover { - color: #039be5; - background-color: #CFD8DC; + color: white; + background-color: #42545C; } nav a.active { - color: #039be5; + background-color: black; + color: white; } - -/* everywhere else */ -* { - font-family: Arial, Helvetica, sans-serif; +hr { + margin: 1.5rem 0; +} +input[type="text"] { + box-sizing: border-box; + width: 100%; + padding: .5rem; } diff --git a/aio/tools/examples/shared/boilerplate/elements/package.json b/aio/tools/examples/shared/boilerplate/elements/package.json index 9d1f1edd39..2be24254a2 100644 --- a/aio/tools/examples/shared/boilerplate/elements/package.json +++ b/aio/tools/examples/shared/boilerplate/elements/package.json @@ -26,7 +26,7 @@ "angular-in-memory-web-api": "~0.11.0", "rxjs": "~6.6.0", "tslib": "^2.0.0", - "zone.js": "~0.10.3" + "zone.js": "~0.11.4" }, "devDependencies": { "@angular-devkit/build-angular": "~0.1100.2", diff --git a/aio/tools/examples/shared/boilerplate/elements/src/polyfills.ts b/aio/tools/examples/shared/boilerplate/elements/src/polyfills.ts index 08b4d9cd60..0cbc235b5d 100644 --- a/aio/tools/examples/shared/boilerplate/elements/src/polyfills.ts +++ b/aio/tools/examples/shared/boilerplate/elements/src/polyfills.ts @@ -57,7 +57,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS diff --git a/aio/tools/examples/shared/boilerplate/i18n/package.json b/aio/tools/examples/shared/boilerplate/i18n/package.json index 00d0b723f8..1bf9886fbf 100644 --- a/aio/tools/examples/shared/boilerplate/i18n/package.json +++ b/aio/tools/examples/shared/boilerplate/i18n/package.json @@ -28,7 +28,7 @@ "angular-in-memory-web-api": "~0.11.0", "rxjs": "~6.6.0", "tslib": "^2.0.0", - "zone.js": "~0.10.3" + "zone.js": "~0.11.4" }, "devDependencies": { "@angular-devkit/build-angular": "~0.1100.2", diff --git a/aio/tools/examples/shared/boilerplate/i18n/src/polyfills.ts b/aio/tools/examples/shared/boilerplate/i18n/src/polyfills.ts index 3424b9d3f4..baa1b8bd36 100644 --- a/aio/tools/examples/shared/boilerplate/i18n/src/polyfills.ts +++ b/aio/tools/examples/shared/boilerplate/i18n/src/polyfills.ts @@ -61,7 +61,7 @@ import '@angular/localize/init'; /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS diff --git a/aio/tools/examples/shared/boilerplate/service-worker/package.json b/aio/tools/examples/shared/boilerplate/service-worker/package.json index a2857856f9..0944c066c2 100644 --- a/aio/tools/examples/shared/boilerplate/service-worker/package.json +++ b/aio/tools/examples/shared/boilerplate/service-worker/package.json @@ -25,7 +25,7 @@ "angular-in-memory-web-api": "~0.11.0", "rxjs": "~6.6.0", "tslib": "^2.0.0", - "zone.js": "~0.10.3" + "zone.js": "~0.11.4" }, "devDependencies": { "@angular-devkit/build-angular": "~0.1100.2", diff --git a/aio/tools/examples/shared/boilerplate/systemjs/package.json b/aio/tools/examples/shared/boilerplate/systemjs/package.json index 74268723ea..d7f606185e 100644 --- a/aio/tools/examples/shared/boilerplate/systemjs/package.json +++ b/aio/tools/examples/shared/boilerplate/systemjs/package.json @@ -39,7 +39,7 @@ "core-js": "^2.5.4", "rxjs": "~6.6.0", "tslib": "^2.0.0", - "zone.js": "~0.10.3" + "zone.js": "~0.11.4" }, "devDependencies": { "@angular/compiler-cli": "~11.0.1", diff --git a/aio/tools/examples/shared/boilerplate/universal/package.json b/aio/tools/examples/shared/boilerplate/universal/package.json index 95d746fcaa..225affe31a 100644 --- a/aio/tools/examples/shared/boilerplate/universal/package.json +++ b/aio/tools/examples/shared/boilerplate/universal/package.json @@ -31,7 +31,7 @@ "express": "^4.15.2", "rxjs": "~6.6.0", "tslib": "^2.0.0", - "zone.js": "~0.10.3" + "zone.js": "~0.11.4" }, "devDependencies": { "@angular-devkit/build-angular": "~0.1100.2", diff --git a/aio/tools/examples/shared/boilerplate/viewengine/cli/tsconfig.json b/aio/tools/examples/shared/boilerplate/viewengine/cli/tsconfig.json index 2394d7fc83..2c974301ff 100644 --- a/aio/tools/examples/shared/boilerplate/viewengine/cli/tsconfig.json +++ b/aio/tools/examples/shared/boilerplate/viewengine/cli/tsconfig.json @@ -26,7 +26,6 @@ "enableIvy": false, "strictInjectionParameters": true, "strictInputAccessModifiers": true, - // TODO(gkalpak): Fix the code and enable this (i.e. switch from `fullTemplateTypeCheck` to `strictTemplates`). - "fullTemplateTypeCheck": true,// "strictTemplates": true + "strictTemplates": true } } diff --git a/aio/tools/examples/shared/package.json b/aio/tools/examples/shared/package.json index e65d24a53c..35ea0ac965 100644 --- a/aio/tools/examples/shared/package.json +++ b/aio/tools/examples/shared/package.json @@ -13,7 +13,7 @@ }, "//engines-comment": "Keep this in sync with /package.json and /aio/package.json", "engines": { - "node": ">=10.9.0 <13.0.0", + "node": ">=10.19.0 <13.0.0", "yarn": ">=1.21.1 <2" }, "keywords": [], @@ -44,7 +44,7 @@ "rxjs": "~6.6.0", "systemjs": "0.19.39", "tslib": "^2.0.0", - "zone.js": "~0.10.3" + "zone.js": "~0.11.4" }, "devDependencies": { "@angular-devkit/build-angular": "~0.1100.2", @@ -75,7 +75,7 @@ "lite-server": "^2.2.2", "lodash": "^4.16.2", "protractor": "~7.0.0", - "puppeteer": "5.1.0", + "puppeteer": "5.4.1", "rimraf": "^2.5.4", "rollup": "^1.1.0", "rollup-plugin-commonjs": "^9.2.1", diff --git a/aio/tools/examples/shared/yarn.lock b/aio/tools/examples/shared/yarn.lock index 49aa795efd..d2bc19e4ec 100644 --- a/aio/tools/examples/shared/yarn.lock +++ b/aio/tools/examples/shared/yarn.lock @@ -4370,10 +4370,10 @@ dev-ip@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0" -devtools-protocol@0.0.767361: - version "0.0.767361" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.767361.tgz#5977f2558b84f9df36f62501bdddb82f3ae7b66b" - integrity sha512-ziRTdhEVQ9jEwedaUaXZ7kl9w9TF/7A3SXQ0XuqrJB+hMS62POHZUWTbumDN2ehRTfvWqTPc2Jw4gUl/jggmHA== +devtools-protocol@0.0.809251: + version "0.0.809251" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.809251.tgz#300b3366be107d5c46114ecb85274173e3999518" + integrity sha512-pf+2OY6ghMDPjKkzSWxHMq+McD+9Ojmq5XVRYpv/kPd9sTMQxzEt21592a31API8qRjro0iYYOc3ag46qF/1FA== di@^0.0.1: version "0.0.1" @@ -7557,7 +7557,7 @@ mime@1.6.0, mime@^1.6.0: version "2.0.3" resolved "https://registry.yarnpkg.com/mime/-/mime-2.0.3.tgz#4353337854747c48ea498330dc034f9f4bbbcc0b" -mime@^2.0.3, mime@^2.4.4: +mime@^2.4.4: version "2.4.4" resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== @@ -7705,11 +7705,6 @@ mitt@^1.1.3: resolved "https://registry.yarnpkg.com/mitt/-/mitt-1.2.0.tgz#cb24e6569c806e31bd4e3995787fe38a04fdf90d" integrity sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw== -mitt@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mitt/-/mitt-2.1.0.tgz#f740577c23176c6205b121b2973514eade1b2230" - integrity sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg== - mixin-deep@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.0.tgz#47a8732ba97799457c8c1eca28f95132d7e8150a" @@ -7901,6 +7896,11 @@ node-fetch-npm@^2.0.2: json-parse-better-errors "^1.0.0" safe-buffer "^5.1.1" +node-fetch@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + node-forge@0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" @@ -9375,17 +9375,16 @@ punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -puppeteer@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.1.0.tgz#e7bae2caa6e3a13a622755e4c27689d9812c38ca" - integrity sha512-IZBFG8XcA+oHxYo5rEpJI/HQignUis2XPijPoFpNxla2O+WufonGsUsSqrhRXgBKOME5zNfhRdUY2LvxAiKlhw== +puppeteer@5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.4.1.tgz#f2038eb23a0f593ed2cce0d6e7cd5c43aecd6756" + integrity sha512-8u6r9tFm3gtMylU4uCry1W/CeAA8uczKMONvGvivkTsGqKA7iB7DWO2CBFYlB9GY6/IEoq9vkI5slJWzUBkwNw== dependencies: debug "^4.1.0" - devtools-protocol "0.0.767361" + devtools-protocol "0.0.809251" extract-zip "^2.0.0" https-proxy-agent "^4.0.0" - mime "^2.0.3" - mitt "^2.0.1" + node-fetch "^2.6.1" pkg-dir "^4.2.0" progress "^2.0.1" proxy-from-env "^1.0.0" @@ -12066,9 +12065,9 @@ webdriver-js-extender@2.1.0: selenium-webdriver "^3.0.1" webdriver-manager@^12.1.7: - version "12.1.7" - resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.1.7.tgz#ed4eaee8f906b33c146e869b55e850553a1b1162" - integrity sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA== + version "12.1.8" + resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.1.8.tgz#5e70e73eaaf53a0767d5745270addafbc5905fd4" + integrity sha512-qJR36SXG2VwKugPcdwhaqcLQOD7r8P2Xiv9sfNbfZrKBnX243iAkOueX1yAmeNgIKhJ3YAT/F2gq6IiEZzahsg== dependencies: adm-zip "^0.4.9" chalk "^1.1.1" @@ -12664,3 +12663,10 @@ 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== + +zone.js@~0.11.4: + version "0.11.4" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.11.4.tgz#0f70dcf6aba80f698af5735cbb257969396e8025" + integrity sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw== + dependencies: + tslib "^2.0.0" diff --git a/aio/tools/ng-packages-installer/index.js b/aio/tools/ng-packages-installer/index.js index b76d4abd4b..33551acfcf 100644 --- a/aio/tools/ng-packages-installer/index.js +++ b/aio/tools/ng-packages-installer/index.js @@ -15,6 +15,7 @@ const LOCAL_MARKER_PATH = 'node_modules/_local_.json'; const ANGULAR_ROOT_DIR = path.resolve(__dirname, '../../..'); const ANGULAR_DIST_PACKAGES_DIR = path.join(ANGULAR_ROOT_DIR, 'dist/packages-dist'); const ZONEJS_DIST_PACKAGES_DIR = path.join(ANGULAR_ROOT_DIR, 'dist/zone.js-dist'); +const ANGULAR_MISC_DIST_PACKAGES = path.join(ANGULAR_ROOT_DIR, 'dist/packages-dist/misc'); const DIST_PACKAGES_BUILD_SCRIPT = path.join(ANGULAR_ROOT_DIR, 'scripts/build/build-packages-dist.js'); const DIST_PACKAGES_BUILD_CMD = `"${process.execPath}" "${DIST_PACKAGES_BUILD_SCRIPT}"`; @@ -183,7 +184,7 @@ class NgPackagesInstaller { } else { this._warn([ 'Automatically building the local Angular/Zone.js packages is currently not supported on Windows.', - `Please, ensure '${ANGULAR_DIST_PACKAGES_DIR}' and '${ZONEJS_DIST_PACKAGES_DIR}' exist and are up-to-date ` + + `Please, ensure '${ANGULAR_DIST_PACKAGES_DIR}', ${ZONEJS_DIST_PACKAGES_DIR} and '${ANGULAR_MISC_DIST_PACKAGES}' exist and are up-to-date ` + `(e.g. by running '${DIST_PACKAGES_BUILD_SCRIPT}' in Git Bash for Windows, Windows Subsystem for Linux or ` + 'a Linux docker container or VM).', '', @@ -222,6 +223,7 @@ class NgPackagesInstaller { _getDistPackages() { this._log(`Angular distributable directory: ${ANGULAR_DIST_PACKAGES_DIR}.`); this._log(`Zone.js distributable directory: ${ZONEJS_DIST_PACKAGES_DIR}.`); + this._log(`angular-in-memory-web-api distributable directory: ${ANGULAR_MISC_DIST_PACKAGES}.`); if (this.buildPackages) { this._buildDistPackages(); @@ -260,6 +262,7 @@ class NgPackagesInstaller { const packageConfigs = { ...collectPackages(ANGULAR_DIST_PACKAGES_DIR), ...collectPackages(ZONEJS_DIST_PACKAGES_DIR), + ...collectPackages(ANGULAR_MISC_DIST_PACKAGES), }; this._log('Found the following Angular distributables:', Object.keys(packageConfigs).map(key => `\n - ${key}`)); diff --git a/aio/tools/stackblitz-builder/builder.js b/aio/tools/stackblitz-builder/builder.js index c9e5af714c..52fc3b1f1e 100644 --- a/aio/tools/stackblitz-builder/builder.js +++ b/aio/tools/stackblitz-builder/builder.js @@ -5,7 +5,7 @@ const path = require('canonical-path'); const fs = require('fs-extra'); const globby = require('globby'); const jsdom = require('jsdom'); -const {JSDOM} = jsdom; +const json5 = require('json5'); const regionExtractor = require('../transforms/examples-package/services/region-parser'); @@ -24,7 +24,7 @@ class StackblitzBuilder { // When testing it sometimes helps to look a just one example directory like so: // const stackblitzPaths = path.join(this.basePath, '**/testing/*stackblitz.json'); const stackblitzPaths = path.join(this.basePath, '**/*stackblitz.json'); - const fileNames = globby.sync(stackblitzPaths, {ignore: ['**/node_modules/**']}); + const fileNames = globby.sync(stackblitzPaths, { ignore: ['**/node_modules/**'] }); fileNames.forEach((configFileName) => { try { // console.log('***'+configFileName) @@ -67,8 +67,8 @@ class StackblitzBuilder { _buildCopyrightStrings() { const copyright = 'Copyright Google LLC. All Rights Reserved.\n' + - 'Use of this source code is governed by an MIT-style license that\n' + - 'can be found in the LICENSE file at https://angular.io/license'; + 'Use of this source code is governed by an MIT-style license that\n' + + 'can be found in the LICENSE file at https://angular.io/license'; const pad = '\n\n'; return { @@ -118,16 +118,16 @@ class StackblitzBuilder { _checkForOutdatedConfig() { // Ensure that nobody is trying to use the old config filenames (i.e. `plnkr.json`). const plunkerPaths = path.join(this.basePath, '**/*plnkr.json'); - const fileNames = globby.sync(plunkerPaths, {ignore: ['**/node_modules/**']}); + const fileNames = globby.sync(plunkerPaths, { ignore: ['**/node_modules/**'] }); if (fileNames.length) { const readmePath = path.join(__dirname, 'README.md'); const errorMessage = - 'One or more examples are still trying to use \'plnkr.json\' files for configuring ' + - 'live examples. This is not supported any more. \'stackblitz.json\' should be used ' + - 'instead.\n' + - `(Slight modifications may be required. See '${readmePath}' for more info.\n\n` + - fileNames.map(name => `- ${name}`).join('\n'); + 'One or more examples are still trying to use \'plnkr.json\' files for configuring ' + + 'live examples. This is not supported any more. \'stackblitz.json\' should be used ' + + 'instead.\n' + + `(Slight modifications may be required. See '${readmePath}' for more info.\n\n` + + fileNames.map(name => `- ${name}`).join('\n'); throw Error(errorMessage); } @@ -141,7 +141,7 @@ class StackblitzBuilder { return config.file; } else { const defaultPrimaryFiles = ['src/app/app.component.html', 'src/app/app.component.ts', 'src/app/main.ts']; - const primaryFile = defaultPrimaryFiles.find(fileName => fs.existsSync(path.join(config.basePath, fileName))); + const primaryFile = defaultPrimaryFiles.find(fileName => fs.existsSync(path.join(config.basePath, fileName))); if (!primaryFile) { throw new Error(`None of the default primary files (${defaultPrimaryFiles.join(', ')}) exists in '${config.basePath}'.`); @@ -227,6 +227,18 @@ class StackblitzBuilder { postData[`files[${relativeFileName}]`] = content; }); + // Stackblitz defaults to ViewEngine unless `"enableIvy": true` + // So if there is a tsconfig.json file and there is no `enableIvy` property, we need to + // explicitly set it. + const tsConfigJSON = postData['files[tsconfig.json]']; + if (tsConfigJSON !== undefined) { + const tsConfig = json5.parse(tsConfigJSON); + if (tsConfig.angularCompilerOptions.enableIvy === undefined) { + tsConfig.angularCompilerOptions.enableIvy = true; + postData['files[tsconfig.json]'] = JSON.stringify(tsConfig, null, 2); + } + } + const tags = ['angular', 'example', ...config.tags || []]; tags.forEach((tag, ix) => postData[`tags[${ix}]`] = tag); @@ -237,10 +249,10 @@ class StackblitzBuilder { _createStackblitzHtml(config, postData) { const baseHtml = this._createBaseStackblitzHtml(config); - const doc = new JSDOM(baseHtml).window.document; + const doc = jsdom.jsdom(baseHtml); const form = doc.querySelector('form'); - for (const [key, value] of Object.entries(postData)) { + for(const [key, value] of Object.entries(postData)) { const ele = this._htmlToElement(doc, ``); ele.setAttribute('value', value); form.appendChild(ele); @@ -251,7 +263,7 @@ class StackblitzBuilder { _encodeBase64(file) { // read binary data - return fs.readFileSync(file, {encoding: 'base64'}); + return fs.readFileSync(file, { encoding: 'base64' }); } _htmlToElement(document, html) { @@ -264,7 +276,7 @@ class StackblitzBuilder { const config = this._parseConfig(configFileName); const defaultIncludes = ['**/*.ts', '**/*.js', '**/*.css', '**/*.html', '**/*.md', '**/*.json', '**/*.png', '**/*.svg']; - const boilerplateIncludes = ['src/environments/*.*', 'angular.json', 'src/polyfills.ts']; + const boilerplateIncludes = ['src/environments/*.*', 'angular.json', 'src/polyfills.ts', 'tsconfig.json']; if (config.files) { if (config.files.length > 0) { if (config.files[0][0] === '!') { @@ -289,7 +301,6 @@ class StackblitzBuilder { const defaultExcludes = [ '!**/e2e/**/*.*', - '!**/tsconfig.json', '!**/package.json', '!**/example-config.json', '!**/tslint.json', @@ -304,12 +315,12 @@ class StackblitzBuilder { // exclude all specs if no spec is mentioned in `files[]` if (!includeSpec) { - defaultExcludes.push('!**/*.spec.*', '!**/spec.js'); + defaultExcludes.push('!**/*.spec.*','!**/spec.js'); } gpaths.push(...defaultExcludes); - config.fileNames = globby.sync(gpaths, {ignore: ['**/node_modules/**']}); + config.fileNames = globby.sync(gpaths, { ignore: ['**/node_modules/**'] }); return config; } diff --git a/aio/tools/transforms/angular-api-package/index.js b/aio/tools/transforms/angular-api-package/index.js index 8b6e065ed2..79eef89996 100644 --- a/aio/tools/transforms/angular-api-package/index.js +++ b/aio/tools/transforms/angular-api-package/index.js @@ -17,7 +17,6 @@ module.exports = // Register the processors .processor(require('./processors/mergeParameterInfo')) .processor(require('./processors/processPseudoClasses')) - .processor(require('./processors/splitDescription')) .processor(require('./processors/convertPrivateClassesToInterfaces')) .processor(require('./processors/generateApiListDoc')) .processor(require('./processors/addNotYetDocumentedProperty')) @@ -87,7 +86,7 @@ module.exports = readTypeScriptModules.ignoreExportsMatching = [/^_|^ɵɵ|^VERSION$/]; readTypeScriptModules.hidePrivateMembers = true; - // NOTE: This list should be in sync with tools/public_api_guard/BUILD.bazel + // NOTE: This list should be in sync with the folders/files in `goldens/public-api`. readTypeScriptModules.sourceFiles = [ 'animations/index.ts', 'animations/browser/index.ts', @@ -102,15 +101,15 @@ module.exports = 'core/testing/index.ts', 'elements/index.ts', 'forms/index.ts', - // Current plan for Angular v8 is to hide documentation for the @angular/http package - // 'http/index.ts', - // 'http/testing/index.ts', + 'localize/index.ts', + 'localize/init/index.ts', 'platform-browser/index.ts', 'platform-browser/animations/index.ts', 'platform-browser/testing/index.ts', 'platform-browser-dynamic/index.ts', 'platform-browser-dynamic/testing/index.ts', 'platform-server/index.ts', + 'platform-server/init/index.ts', 'platform-server/testing/index.ts', 'platform-webworker/index.ts', 'platform-webworker-dynamic/index.ts', diff --git a/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.js b/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.js index 414e742b62..3ee25a8cb0 100644 --- a/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.js +++ b/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.js @@ -56,7 +56,6 @@ module.exports = function mergeDecoratorDocs(log) { {type: 'Param', description: 'parameter', functionName: 'makeParamDecorator'}, ], $process(docs) { - const decoratorDocs = Object.create(null); // find all the decorators, signified by a call to `make...Decorator(metadata)` @@ -69,7 +68,8 @@ module.exports = function mergeDecoratorDocs(log) { // For example the `X` of `createDecorator(...)`. const decoratorType = initializer.arguments[0].text; - log.debug('mergeDecoratorDocs: found decorator', doc.docType, doc.name, decoratorType); + log.debug( + 'mergeDecoratorDocs: found decorator', doc.docType, doc.name, decoratorType); doc.docType = 'decorator'; doc.decoratorLocation = call.description; @@ -84,8 +84,8 @@ module.exports = function mergeDecoratorDocs(log) { // merge the info from the associated metadata interfaces into the decorator docs docs = docs.filter(doc => { if (decoratorDocs[doc.name]) { - - // We have found an `XxxDecorator` document that will hold the call signature of the decorator + // We have found an `XxxDecorator` document that will hold the call signature of the + // decorator var decoratorDoc = decoratorDocs[doc.name]; var callMember = doc.members.find(member => member.isCallMember); @@ -109,18 +109,44 @@ module.exports = function mergeDecoratorDocs(log) { }; function getInitializer(doc) { - var initializer = doc.symbol && doc.symbol.valueDeclaration && doc.symbol.valueDeclaration.initializer; + const declaration = doc.symbol && doc.symbol.valueDeclaration; + if (!declaration || !declaration.initializer || !declaration.initializer.expression) { + return; + } + + let initializer = declaration.initializer; + // There appear to be two forms of initializer: - // export var Injectable: InjectableFactory = - // makeDecorator(InjectableMetadata); + // + // ``` + // export const Injectable: InjectableFactory = + // makeDecorator(InjectableMetadata); + // ``` + // // and - // export var RouteConfig: (configs: RouteDefinition[]) => ClassDecorator = - // makeDecorator(RouteConfigAnnotation); - // In the first case, the type assertion `` causes the AST to contain an - // extra level of expression - // to hold the new type of the expression. - if (initializer && initializer.expression && initializer.expression.expression) { + // + // ``` + // export const RouteConfig: (configs: RouteDefinition[]) => ClassDecorator = + // makeDecorator(RouteConfigAnnotation); + // ``` + // + // In the first case, the type assertion `` causes the AST to contain an extra + // level of expression to hold the new type of the expression. + if (initializer.type && initializer.expression.expression) { initializer = initializer.expression; } + + // It is also possible that the decorator call is wrapped in a call to `attachInjectFlag()`: + // + // ``` + // const Optional: OptionalDecorator = + // attachInjectFlag(makeParamDecorator('Optional'), InternalInjectFlags.Optional); + // ``` + // + // If so, use the first argument of the call. + if (initializer.arguments && initializer.expression.text === 'attachInjectFlag') { + initializer = initializer.arguments[0]; + } + return initializer; } diff --git a/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.spec.js b/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.spec.js index 77bc9f8141..9940fb9d9e 100644 --- a/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.spec.js +++ b/aio/tools/transforms/angular-api-package/processors/mergeDecoratorDocs.spec.js @@ -1,5 +1,5 @@ -var testPackage = require('../../helpers/test-package'); -var Dgeni = require('dgeni'); +const testPackage = require('../../helpers/test-package'); +const Dgeni = require('dgeni'); describe('mergeDecoratorDocs processor', () => { let processor, moduleDoc, decoratorDoc, metadataDoc, otherDoc; @@ -20,11 +20,10 @@ describe('mergeDecoratorDocs processor', () => { shortDescription: 'decorator - short description', description: 'decorator - description', symbol: { - valueDeclaration: { initializer: { expression: { text: 'makeDecorator' }, arguments: [{ text: 'X' }] } } + valueDeclaration: + {initializer: {expression: {text: 'makeDecorator'}, arguments: [{text: 'X'}]}} }, - members: [ - { name: 'templateUrl', description: 'templateUrl - description' } - ], + members: [{name: 'templateUrl', description: 'templateUrl - description'}], moduleDoc }; @@ -38,9 +37,7 @@ describe('mergeDecoratorDocs processor', () => { description: 'call interface - call member - description', usageNotes: 'call interface - call member - usageNotes', }, - { - description: 'call interface - non call member - description' - } + {description: 'call interface - non call member - description'} ], moduleDoc }; @@ -49,7 +46,8 @@ describe('mergeDecoratorDocs processor', () => { name: 'Y', docType: 'const', symbol: { - valueDeclaration: { initializer: { expression: { text: 'otherCall' }, arguments: [{ text: 'param1' }] } } + valueDeclaration: + {initializer: {expression: {text: 'otherCall'}, arguments: [{text: 'param1'}]}} }, moduleDoc }; @@ -58,11 +56,12 @@ describe('mergeDecoratorDocs processor', () => { }); - it('should change the docType of only the docs that are initialized by a call to makeDecorator', () => { - processor.$process([decoratorDoc, metadataDoc, otherDoc]); - expect(decoratorDoc.docType).toEqual('decorator'); - expect(otherDoc.docType).toEqual('const'); - }); + it('should change the docType of only the docs that are initialized by a call to makeDecorator', + () => { + processor.$process([decoratorDoc, metadataDoc, otherDoc]); + expect(decoratorDoc.docType).toEqual('decorator'); + expect(otherDoc.docType).toEqual('const'); + }); it('should extract the "type" of the decorator meta data', () => { processor.$process([decoratorDoc, metadataDoc, otherDoc]); @@ -88,4 +87,23 @@ describe('mergeDecoratorDocs processor', () => { processor.$process([decoratorDoc, metadataDoc, otherDoc]); expect(decoratorDoc.docType).toEqual('decorator'); }); + + it('should handle a type cast before the "make decorator" call', () => { + decoratorDoc.symbol.valueDeclaration.initializer = { + expression: decoratorDoc.symbol.valueDeclaration.initializer, + type: {}, + }; + processor.$process([decoratorDoc, metadataDoc, otherDoc]); + expect(decoratorDoc.docType).toEqual('decorator'); + }); + + it('should handle the "make decorator" call being wrapped in a call to `attachInjectFlag()`', + () => { + decoratorDoc.symbol.valueDeclaration.initializer = { + expression: {text: 'attachInjectFlag'}, + arguments: [decoratorDoc.symbol.valueDeclaration.initializer] + }; + processor.$process([decoratorDoc, metadataDoc, otherDoc]); + expect(decoratorDoc.docType).toEqual('decorator'); + }); }); diff --git a/aio/tools/transforms/angular-api-package/processors/processPackages.js b/aio/tools/transforms/angular-api-package/processors/processPackages.js index b4cb80fd59..d9af8ac3a1 100644 --- a/aio/tools/transforms/angular-api-package/processors/processPackages.js +++ b/aio/tools/transforms/angular-api-package/processors/processPackages.js @@ -18,6 +18,7 @@ module.exports = function processPackages(collectPackageContentDocsProcessor) { // Partition the exports into groups by type if (doc.exports) { const publicExports = doc.exports.filter(doc => !doc.privateExport); + doc.hasPublicExports = publicExports.length > 0; doc.ngmodules = publicExports.filter(doc => doc.docType === 'ngmodule').sort(byId); doc.classes = publicExports.filter(doc => doc.docType === 'class').sort(byId); doc.decorators = publicExports.filter(doc => doc.docType === 'decorator').sort(byId); @@ -26,7 +27,7 @@ module.exports = function processPackages(collectPackageContentDocsProcessor) { doc.directives = publicExports.filter(doc => doc.docType === 'directive').sort(byId); doc.pipes = publicExports.filter(doc => doc.docType === 'pipe').sort(byId); doc.types = publicExports.filter(doc => doc.docType === 'type-alias' || doc.docType === 'const').sort(byId); - if (publicExports.every(doc => !!doc.deprecated)) { + if (doc.hasPublicExports && publicExports.every(doc => !!doc.deprecated)) { doc.deprecated = 'all exports of this entry point are deprecated.'; } } diff --git a/aio/tools/transforms/angular-api-package/processors/processPackages.spec.js b/aio/tools/transforms/angular-api-package/processors/processPackages.spec.js index 13fc326338..7fa4b11acc 100644 --- a/aio/tools/transforms/angular-api-package/processors/processPackages.spec.js +++ b/aio/tools/transforms/angular-api-package/processors/processPackages.spec.js @@ -163,6 +163,47 @@ describe('processPackages processor', () => { ]); }); + it('should compute whether the entry point has public exports', () => { + const docs = [ + { + fileInfo: { filePath: 'some/package-1/index' }, + docType: 'module', + id: 'package-1', + exports: [ + { docType: 'class', id: 'class-1' }, + ] + }, + { + fileInfo: { filePath: 'some/package-1/sub-1index' }, + docType: 'module', + id: 'package-1/sub-1', + exports: [], + }, + { + fileInfo: { filePath: 'some/package-2/index' }, + docType: 'module', + id: 'package-2', + exports: [], + }, + { + fileInfo: { filePath: 'some/package-1/sub-1index' }, + docType: 'module', + id: 'package-1/sub-1', + exports: [ + { docType: 'const', id: 'const-2' }, + { docType: 'enum', id: 'enum-3' }, + ], + }, + ]; + const processor = processorFactory({ packageContentFiles: {} }); + processor.$process(docs); + + expect(docs[0].hasPublicExports).toBeTrue(); + expect(docs[1].hasPublicExports).toBeFalse(); + expect(docs[2].hasPublicExports).toBeFalse(); + expect(docs[3].hasPublicExports).toBeTrue(); + }); + it('should compute the deprecated status of each entry point', () => { const docs = [ { @@ -199,6 +240,12 @@ describe('processPackages processor', () => { { docType: 'class', id: 'class-6' }, ] }, + { + fileInfo: { filePath: 'some/package-4/index' }, + docType: 'module', + id: 'package-4', + exports: [] + }, ]; const processor = processorFactory({ packageContentFiles: {} }); processor.$process(docs); @@ -207,6 +254,7 @@ describe('processPackages processor', () => { expect(docs[1].deprecated).toBeTruthy(); expect(docs[2].deprecated).toBeUndefined(); expect(docs[3].deprecated).toBeUndefined(); + expect(docs[4].deprecated).toBeUndefined(); }); it('should compute the deprecated status of packages', () => { diff --git a/aio/tools/transforms/angular-api-package/processors/splitDescription.js b/aio/tools/transforms/angular-api-package/processors/splitDescription.js deleted file mode 100644 index 0f43896a9a..0000000000 --- a/aio/tools/transforms/angular-api-package/processors/splitDescription.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Split the description (of selected docs) into: - * * `shortDescription`: the first paragraph - * * `description`: the rest of the paragraphs - */ -module.exports = function splitDescription() { - return { - $runAfter: ['tags-extracted'], - $runBefore: ['processing-docs'], - docTypes: [], - $process(docs) { - docs.forEach(doc => { - if (this.docTypes.indexOf(doc.docType) !== -1 && doc.description !== undefined) { - const description = doc.description.trim(); - const endOfParagraph = description.search(/\n\s*\n+(?!.*[\u4e00-\u9fa5])/); // 从第一个非汉字行开始拆分 - if (endOfParagraph === -1) { - doc.shortDescription = description; - doc.description = ''; - } else { - doc.shortDescription = description.substr(0, endOfParagraph).trim(); - doc.description = description.substr(endOfParagraph).trim(); - } - } - }); - } - }; -}; - diff --git a/aio/tools/transforms/angular-api-package/processors/splitDescription.spec.js b/aio/tools/transforms/angular-api-package/processors/splitDescription.spec.js deleted file mode 100644 index 313aa51cab..0000000000 --- a/aio/tools/transforms/angular-api-package/processors/splitDescription.spec.js +++ /dev/null @@ -1,71 +0,0 @@ -const testPackage = require('../../helpers/test-package'); -const processorFactory = require('./splitDescription'); -const Dgeni = require('dgeni'); - -describe('splitDescription processor', () => { - - it('should be available on the injector', () => { - const dgeni = new Dgeni([testPackage('angular-api-package')]); - const injector = dgeni.configureInjector(); - const processor = injector.get('splitDescription'); - expect(processor.$process).toBeDefined(); - }); - - it('should run before the correct processor', () => { - const processor = processorFactory(); - expect(processor.$runBefore).toEqual(['processing-docs']); - }); - - it('should run after the correct processor', () => { - const processor = processorFactory(); - expect(processor.$runAfter).toEqual(['tags-extracted']); - }); - - it('should split the `description` property into the first paragraph and other paragraphs', () => { - const processor = processorFactory(); - processor.docTypes = ['test']; - const docs = [ - { docType: 'test' }, - { docType: 'test', description: '' }, - { docType: 'test', description: 'abc' }, - { docType: 'test', description: 'abc\n' }, - { docType: 'test', description: 'abc\n\n' }, - { docType: 'test', description: 'abc\ncde' }, - { docType: 'test', description: 'abc\ncde\n' }, - { docType: 'test', description: 'abc\n\ncde' }, - { docType: 'test', description: 'abc\n \ncde' }, - { docType: 'test', description: 'abc\n\n\ncde' }, - { docType: 'test', description: 'abc\n\ncde\nfgh' }, - { docType: 'test', description: 'abc\n\ncde\n\nfgh' }, - ]; - processor.$process(docs); - expect(docs).toEqual([ - { docType: 'test' }, - { docType: 'test', shortDescription: '', description: '' }, - { docType: 'test', shortDescription: 'abc', description: '' }, - { docType: 'test', shortDescription: 'abc', description: '' }, - { docType: 'test', shortDescription: 'abc', description: '' }, - { docType: 'test', shortDescription: 'abc\ncde', description: '' }, - { docType: 'test', shortDescription: 'abc\ncde', description: '' }, - { docType: 'test', shortDescription: 'abc', description: 'cde' }, - { docType: 'test', shortDescription: 'abc', description: 'cde' }, - { docType: 'test', shortDescription: 'abc', description: 'cde' }, - { docType: 'test', shortDescription: 'abc', description: 'cde\nfgh' }, - { docType: 'test', shortDescription: 'abc', description: 'cde\n\nfgh' }, - ]); - }); - - it('should ignore docs that do not match the specified doc types', () => { - const processor = processorFactory(); - processor.docTypes = ['test']; - const docs = [ - { docType: 'test', description: 'abc\n\ncde' }, - { docType: 'other', description: 'abc\n\ncde' } - ]; - processor.$process(docs); - expect(docs).toEqual([ - { docType: 'test', shortDescription: 'abc', description: 'cde' }, - { docType: 'other', description: 'abc\n\ncde' } - ]); - }); -}); \ No newline at end of file diff --git a/aio/tools/transforms/angular-base-package/ignore.words b/aio/tools/transforms/angular-base-package/ignore.words deleted file mode 100644 index 82b9f2fc3f..0000000000 --- a/aio/tools/transforms/angular-base-package/ignore.words +++ /dev/null @@ -1,701 +0,0 @@ -a -able -about -above -abst -accordance -according -accordingly -across -act -actually -added -adj -adopted -affected -affecting -affects -after -afterwards -again -against -ah -all -almost -alone -along -already -also -although -always -am -among -amongst -an -and -announce -another -any -anybody -anyhow -anymore -anyone -anything -anyway -anyways -anywhere -apparently -approximately -are -aren -arent -arise -around -as -aside -ask -asking -at -auth -available -away -awfully -b -back -be -became -because -become -becomes -becoming -been -before -beforehand -begin -beginning -beginnings -begins -behind -being -believe -below -beside -besides -between -beyond -biol -both -brief -briefly -but -by -c -ca -came -can -cannot -can't -cant -cause -causes -certain -certainly -co -com -come -comes -contain -containing -contains -could -couldnt -d -date -did -didn't -didnt -different -do -does -doesn't -doesnt -doing -done -don't -dont -down -downwards -due -during -e -each -ed -edu -effect -eg -eight -eighty -either -else -elsewhere -end -ending -enough -especially -et -et-al -etc -even -ever -every -everybody -everyone -everything -everywhere -ex -except -f -far -few -ff -fifth -first -five -fix -followed -following -follows -for -former -formerly -forth -found -four -from -further -furthermore -g -gave -get -gets -getting -give -given -gives -giving -go -goes -gone -got -gotten -h -had -happens -hardly -has -hasn't -hasnt -have -haven't -havent -having -he -hed -hence -her -here -hereafter -hereby -herein -heres -hereupon -hers -herself -hes -hi -hid -him -himself -his -hither -home -how -howbeit -however -hundred -i -id -ie -if -i'll -ill -im -immediate -immediately -importance -important -in -inc -indeed -index -information -instead -into -invention -inward -is -isn't -isnt -it -itd -it'll -itll -its -itself -i've -ive -j -just -k -keep -keeps -kept -keys -kg -km -know -known -knows -l -largely -last -lately -later -latter -latterly -least -less -lest -let -lets -like -liked -likely -line -little -'ll -'ll -look -looking -looks -ltd -m -made -mainly -make -makes -many -may -maybe -me -mean -means -meantime -meanwhile -merely -mg -might -million -miss -ml -more -moreover -most -mostly -mr -mrs -much -mug -must -my -myself -n -na -name -namely -nay -nd -near -nearly -necessarily -necessary -need -needs -neither -never -nevertheless -new -next -nine -ninety -no -nobody -non -none -nonetheless -noone -nor -normally -nos -not -noted -nothing -now -nowhere -o -obtain -obtained -obviously -of -off -often -oh -ok -okay -old -omitted -on -once -one -ones -only -onto -or -ord -other -others -otherwise -ought -our -ours -ourselves -out -outside -over -overall -owing -own -p -page -pages -part -particular -particularly -past -per -perhaps -placed -please -plus -poorly -possible -possibly -potentially -pp -predominantly -present -previously -primarily -probably -promptly -proud -provides -put -q -que -quickly -quite -qv -r -ran -rather -rd -re -readily -really -recent -recently -ref -refs -regarding -regardless -regards -related -relatively -research -respectively -resulted -resulting -results -right -run -s -said -same -saw -say -saying -says -sec -section -see -seeing -seem -seemed -seeming -seems -seen -self -selves -sent -seven -several -shall -she -shed -she'll -shell -shes -should -shouldn't -shouldnt -show -showed -shown -showns -shows -significant -significantly -similar -similarly -since -six -slightly -so -some -somebody -somehow -someone -somethan -something -sometime -sometimes -somewhat -somewhere -soon -sorry -specifically -specified -specify -specifying -state -states -still -stop -strongly -sub -substantially -successfully -such -sufficiently -suggest -sup -sure -t -take -taken -taking -tell -tends -th -than -thank -thanks -thanx -that -that'll -thatll -thats -that've -thatve -the -their -theirs -them -themselves -then -thence -there -thereafter -thereby -thered -therefore -therein -there'll -therell -thereof -therere -theres -thereto -thereupon -there've -thereve -these -they -theyd -they'll -theyll -theyre -they've -theyve -think -this -those -thou -though -thoughh -thousand -throug -through -throughout -thru -thus -til -tip -to -together -too -took -toward -towards -tried -tries -truly -try -trying -ts -twice -two -u -un -under -unfortunately -unless -unlike -unlikely -until -unto -up -upon -ups -us -use -used -useful -usefully -usefulness -uses -using -usually -v -value -various -'ve -'ve -very -via -viz -vol -vols -vs -w -want -wants -was -wasn't -wasnt -way -we -wed -welcome -we'll -well -went -were -weren't -werent -we've -weve -what -whatever -what'll -whatll -whats -when -whence -whenever -where -whereafter -whereas -whereby -wherein -wheres -whereupon -wherever -whether -which -while -whim -whither -who -whod -whoever -whole -who'll -wholl -whom -whomever -whos -whose -why -widely -will -willing -wish -with -within -without -won't -wont -words -would -wouldn't -wouldnt -www -x -y -yes -yet -you -youd -you'll -youll -your -youre -yours -yourself -yourselves -you've -youve -z -zero diff --git a/aio/tools/transforms/angular-base-package/index.js b/aio/tools/transforms/angular-base-package/index.js index 5f1f6e5fc8..8c946c89ec 100644 --- a/aio/tools/transforms/angular-base-package/index.js +++ b/aio/tools/transforms/angular-base-package/index.js @@ -5,18 +5,22 @@ * 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 { extname, resolve } = require('canonical-path'); +const { existsSync } = require('fs'); const path = require('path'); -const Package = require('dgeni').Package; +const Package = require('dgeni').Package; const gitPackage = require('dgeni-packages/git'); const jsdocPackage = require('dgeni-packages/jsdoc'); const nunjucksPackage = require('dgeni-packages/nunjucks'); -const linksPackage = require('../links-package'); -const examplesPackage = require('../examples-package'); -const targetPackage = require('../target-package'); -const remarkPackage = require('../remark-package'); const postProcessPackage = require('dgeni-packages/post-process-html'); +const { SRC_PATH } = require('../config'); +const examplesPackage = require('../examples-package'); +const linksPackage = require('../links-package'); +const remarkPackage = require('../remark-package'); +const targetPackage = require('../target-package'); + const { PROJECT_ROOT, CONTENTS_PATH, OUTPUT_PATH, DOCS_OUTPUT_PATH, TEMPLATES_PATH, AIO_PATH, requireFolder } = require('../config'); module.exports = new Package('angular-base', [ @@ -32,6 +36,7 @@ module.exports = new Package('angular-base', [ .processor(require('./processors/copyContentAssets')) .processor(require('./processors/renderLinkInfo')) .processor(require('./processors/checkContentRules')) + .processor(require('./processors/splitDescription')) // overrides base packageInfo and returns the one for the 'angular/angular' repo. .factory('packageInfo', function() { return require(path.resolve(PROJECT_ROOT, 'package.json')); }) @@ -52,12 +57,6 @@ module.exports = new Package('angular-base', [ inlineTagProcessor.inlineTagDefinitions.push(require('./inline-tag-defs/custom-search-defs/')); }) - .config(function(checkAnchorLinksProcessor) { - // This is disabled here to prevent false negatives for the `docs-watch` task. - // It is re-enabled in the main `angular.io-package` - checkAnchorLinksProcessor.$enabled = false; - }) - // Where do we get the source files? .config(function(readFilesProcessor, collectExamples, generateKeywordsProcessor, jsonFileReader) { @@ -66,9 +65,9 @@ module.exports = new Package('angular-base', [ readFilesProcessor.sourceFiles = []; collectExamples.exampleFolders = []; - generateKeywordsProcessor.ignoreWordsFile = path.resolve(__dirname, 'ignore.words'); + generateKeywordsProcessor.ignoreWords = require(path.resolve(__dirname, 'ignore-words'))['en']; generateKeywordsProcessor.docTypesToIgnore = ['example-region']; - generateKeywordsProcessor.propertiesToIgnore = ['basePath', 'renderedContent']; + generateKeywordsProcessor.propertiesToIgnore = ['basePath', 'renderedContent', 'docType', 'searchTitle']; }) // Where do we write the output files? @@ -123,7 +122,25 @@ module.exports = new Package('angular-base', [ getLinkInfo.useFirstAmbiguousLink = false; }) - + .config(function(checkAnchorLinksProcessor) { + // since we encode the HTML to JSON we need to ensure that this processor runs before that encoding happens. + checkAnchorLinksProcessor.$runBefore = ['convertToJsonProcessor']; + checkAnchorLinksProcessor.$runAfter = ['fixInternalDocumentLinks']; + // We only want to check docs that are going to be output as JSON docs. + checkAnchorLinksProcessor.checkDoc = (doc) => doc.path && doc.outputPath && extname(doc.outputPath) === '.json' && doc.docType !== 'json-doc'; + // Since we have a `base[href="/"]` arrangement all links are relative to that and not relative to the source document's path + checkAnchorLinksProcessor.base = '/'; + // Ignore links to local assets + // (This is not optimal in terms of performance without making changes to dgeni-packages there is no other way. + // That being said do this only add 500ms onto the ~30sec doc-gen run - so not a huge issue) + checkAnchorLinksProcessor.ignoredLinks.push({ + test(url) { + return (existsSync(resolve(SRC_PATH, url))); + } + }); + checkAnchorLinksProcessor.pathVariants = ['', '/', '.html', '/index.html', '#top-of-page']; + checkAnchorLinksProcessor.errorOnUnmatchedLinks = true; + }) .config(function(computePathsProcessor, generateKeywordsProcessor) { @@ -140,6 +157,7 @@ module.exports = new Package('angular-base', [ .config(function(postProcessHtml, addImageDimensions, autoLinkCode, filterPipes, filterAmbiguousDirectiveAliases, ignoreHttpInUrls, ignoreGenericWords) { addImageDimensions.basePath = path.resolve(AIO_PATH, 'src'); autoLinkCode.customFilters = [ignoreGenericWords, ignoreHttpInUrls, filterPipes, filterAmbiguousDirectiveAliases]; + autoLinkCode.failOnMissingDocPath = true; postProcessHtml.plugins = [ require('./post-processors/autolink-headings'), addImageDimensions, 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 278205eef7..91bfbf6330 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 @@ -19,12 +19,23 @@ const textContent = require('hast-util-to-string'); * @property codeElements an array of strings. * Only text contained in these elements will be linked to. * Usually set to "code" but also "code-example" for angular.io. + * + * @property ignoredLanguages an array of languages that should not be auto-linked + * + * @property ignoredLanguages an array of languages that should not be auto-linked + * + * @property failOnMissingDocPath if set to true then this post-processor will cause the doc-gen + * to fail when it attempts to auto-link to a doc that has no `doc.path` property, which implies + * that it exists but is not public (nor rendered). + * */ module.exports = function autoLinkCode(getDocFromAlias) { autoLinkCodeImpl.docTypes = []; autoLinkCodeImpl.customFilters = []; autoLinkCodeImpl.codeElements = ['code']; autoLinkCodeImpl.ignoredLanguages = ['bash', 'sh', 'shell', 'json', 'markdown']; + autoLinkCodeImpl.failOnMissingDocPath = false; + return autoLinkCodeImpl; function autoLinkCodeImpl() { @@ -64,8 +75,10 @@ module.exports = function autoLinkCode(getDocFromAlias) { // * 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 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; } @@ -76,19 +89,19 @@ module.exports = function autoLinkCode(getDocFromAlias) { 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)); + .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}; - }); + 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}; + }); } /** @@ -112,12 +125,16 @@ module.exports = function autoLinkCode(getDocFromAlias) { return false; } - if (doc.path === '') { + 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); + if (autoLinkCodeImpl.failOnMissingDocPath) { + file.fail(message); + } else { + file.message(message); + } return false; } 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 5f18756f63..e36203a5af 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 @@ -2,7 +2,7 @@ var createTestPackage = require('../../helpers/test-package'); var Dgeni = require('dgeni'); describe('autoLinkCode post-processor', () => { - let processor, autoLinkCode, aliasMap, filterPipes; + let processor, autoLinkCode, aliasMap, filterPipes, log; beforeEach(() => { const testPackage = createTestPackage('angular-base-package'); @@ -15,6 +15,7 @@ describe('autoLinkCode post-processor', () => { processor.docTypes = ['test-doc']; processor.plugins = [autoLinkCode]; filterPipes = injector.get('filterPipes'); + log = injector.get('log'); }); it('should insert an anchor into every code item that matches the id of an API doc', () => { @@ -126,19 +127,9 @@ describe('autoLinkCode post-processor', () => { expect(doc.renderedContent).toEqual('MyClass'); }); - 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: 'MyClass'}; - processor.$process([doc]); - expect(doc.renderedContent).toEqual('MyClass'); - }); - 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'}); + aliasMap.addDoc({docType: 'member', id: 'MyEnum', aliases: ['MyEnum'], path: 'a/b/c'}); const doc = {docType: 'test-doc', renderedContent: 'MyEnum'}; processor.$process([doc]); expect(doc.renderedContent).toEqual('MyEnum'); @@ -193,4 +184,25 @@ describe('autoLinkCode post-processor', () => { processor.$process([doc]); expect(doc.renderedContent).toEqual('MyClass'); }); + + it('should record a warning if the autolinked doc has no `path` and `failOnMissingDocPath` is false', + () => { + aliasMap.addDoc({docType: 'class', id: 'MyClass', aliases: ['MyClass']}); + const doc = {docType: 'test-doc', renderedContent: 'MyClass'}; + autoLinkCode.failOnMissingDocPath = false; + processor.$process([doc]); + + expect(log.warn).toHaveBeenCalledWith(` + autoLinkCode: Doc path is empty for "MyClass" - link will not be generated for "MyClass". + Please make sure if the doc should be public. If not, it should probably not be referenced in the docs. - doc (test-doc) `); + }); + + it('should fail if the autolinked doc has no `path` and `failOnMissingDocPath` is true', () => { + aliasMap.addDoc({docType: 'class', id: 'MyClass', aliases: ['MyClass']}); + const doc = {docType: 'test-doc', renderedContent: 'MyClass'}; + autoLinkCode.failOnMissingDocPath = true; + expect(() => processor.$process([doc])).toThrowError(` + autoLinkCode: Doc path is empty for "MyClass" - link will not be generated for "MyClass". + Please make sure if the doc should be public. If not, it should probably not be referenced in the docs. - doc (test-doc) `); + }); }); diff --git a/aio/tools/transforms/angular-base-package/post-processors/h1-checker.js b/aio/tools/transforms/angular-base-package/post-processors/h1-checker.js index 06559a1e8c..d54c5b4880 100644 --- a/aio/tools/transforms/angular-base-package/post-processors/h1-checker.js +++ b/aio/tools/transforms/angular-base-package/post-processors/h1-checker.js @@ -20,13 +20,9 @@ module.exports = function h1CheckerPostProcessor() { } }); - if (file.headings.h1.length > 2) { - file.fail(`More than two h1 found in ${file}`); - } else if (file.headings.h1.length > 1) { - file.titleCn = file.headings.h1[0]; - file.title = file.headings.h1[1]; - } else if (file.headings.h1.length === 1) { - file.title = file.headings.h1[0]; + file.title = file.headings.h1[0]; + if (file.headings.h1.length > 1) { + file.fail(`More than one h1 found in ${file}`); } }; }; diff --git a/aio/tools/transforms/angular-base-package/post-processors/h1-checker.spec.js b/aio/tools/transforms/angular-base-package/post-processors/h1-checker.spec.js index 4dd2537df4..22443afcdd 100644 --- a/aio/tools/transforms/angular-base-package/post-processors/h1-checker.spec.js +++ b/aio/tools/transforms/angular-base-package/post-processors/h1-checker.spec.js @@ -14,12 +14,11 @@ describe('h1Checker postprocessor', () => { processor.plugins = [plugin]; }); - it('should complain if there is more than two h1 in a document', () => { + it('should complain if there is more than one h1 in a document', () => { const doc = { docType: 'a', renderedContent: `

Heading 1

-

标题1

Heading 2

Heading 1a

` diff --git a/aio/tools/transforms/angular-base-package/processors/generateKeywords.js b/aio/tools/transforms/angular-base-package/processors/generateKeywords.js index de689c54d8..020d460de8 100644 --- a/aio/tools/transforms/angular-base-package/processors/generateKeywords.js +++ b/aio/tools/transforms/angular-base-package/processors/generateKeywords.js @@ -1,7 +1,6 @@ 'use strict'; -var fs = require('fs'); -var path = require('canonical-path'); +const stem = require('stemmer'); /** * @dgProcessor generateKeywordsProcessor @@ -10,103 +9,98 @@ var path = require('canonical-path'); * a new document that will be rendered as a JavaScript file containing all * this data. */ -module.exports = function generateKeywordsProcessor(log, readFilesProcessor) { +module.exports = function generateKeywordsProcessor(log) { return { - ignoreWordsFile: undefined, + ignoreWords: [], propertiesToIgnore: [], docTypesToIgnore: [], outputFolder: '', $validate: { - ignoreWordsFile: {}, + ignoreWords: {}, docTypesToIgnore: {}, propertiesToIgnore: {}, outputFolder: {presence: true} }, $runAfter: ['postProcessHtml'], $runBefore: ['writing-files'], - $process: function(docs) { + $process(docs) { + + const dictionary = new Map(); // Keywords to ignore - var wordsToIgnore = []; - var propertiesToIgnore; - var docTypesToIgnore; - - // Load up the keywords to ignore, if specified in the config - if (this.ignoreWordsFile) { - var ignoreWordsPath = path.resolve(readFilesProcessor.basePath, this.ignoreWordsFile); - wordsToIgnore = fs.readFileSync(ignoreWordsPath, 'utf8').toString().split(/[,\s\n\r]+/gm); - - log.debug('Loaded ignore words from "' + ignoreWordsPath + '"'); - log.silly(wordsToIgnore); - } - - propertiesToIgnore = convertToMap(this.propertiesToIgnore); + const ignoreWords = new Set(this.ignoreWords); + log.debug('Words to ignore', ignoreWords); + const propertiesToIgnore = new Set(this.propertiesToIgnore); log.debug('Properties to ignore', propertiesToIgnore); - docTypesToIgnore = convertToMap(this.docTypesToIgnore); + const docTypesToIgnore = new Set(this.docTypesToIgnore); log.debug('Doc types to ignore', docTypesToIgnore); - var ignoreWordsMap = convertToMap(wordsToIgnore); const filteredDocs = docs // We are not interested in some docTypes - .filter(function(doc) { return !docTypesToIgnore[doc.docType]; }) + .filter(doc => !docTypesToIgnore.has(doc.docType)) // Ignore internals and private exports (indicated by the ɵ prefix) - .filter(function(doc) { return !doc.internal && !doc.privateExport; }); + .filter(doc => !doc.internal && !doc.privateExport); - filteredDocs.forEach(function(doc) { - - var words = []; - var keywordMap = Object.assign({}, ignoreWordsMap); - var members = []; - var membersMap = Object.assign({}, ignoreWordsMap); - const headingWords = []; - const headingWordMap = Object.assign({}, ignoreWordsMap); - + for(const doc of filteredDocs) { // Search each top level property of the document for search terms - Object.keys(doc).forEach(function(key) { + let mainTokens = []; + for(const key of Object.keys(doc)) { const value = doc[key]; - - if (isString(value) && !propertiesToIgnore[key]) { - extractWords(value, words, keywordMap); + if (isString(value) && !propertiesToIgnore.has(key)) { + mainTokens.push(...tokenize(value, ignoreWords, dictionary)); } - }); - - extractMemberWords(doc, members, membersMap); - - // Extract all the keywords from the headings - if (doc.vFile && doc.vFile.headings) { - Object.keys(doc.vFile.headings).forEach(function(headingTag) { - doc.vFile.headings[headingTag].forEach(function(headingText) { - extractWords(headingText, headingWords, headingWordMap); - }); - }); } + const memberTokens = extractMemberTokens(doc, ignoreWords, dictionary); + + // Extract all the keywords from the headings + let headingTokens = []; + if (doc.vFile && doc.vFile.headings) { + for(const headingTag of Object.keys(doc.vFile.headings)) { + for(const headingText of doc.vFile.headings[headingTag]) { + headingTokens.push(...tokenize(headingText, ignoreWords, dictionary)); + } + } + } + + // Extract the title to use in searches - doc.searchTitle = doc.searchTitle || doc.title || doc.vFile && (doc.vFile.titleCn || doc.vFile.title) || doc.name || ''; + doc.searchTitle = doc.searchTitle || doc.title || doc.vFile && doc.vFile.title || doc.name || ''; // Attach all this search data to the document - doc.searchTerms = { - titleWords: tokenize(doc.searchTitle).join(' '), - headingWords: headingWords.sort().join(' '), - keywords: words.sort().join(' '), - members: members.sort().join(' '), - topics: doc.searchKeywords - }; - - }); + doc.searchTerms = {}; + if (headingTokens.length > 0) { + doc.searchTerms.headings = headingTokens; + } + if (mainTokens.length > 0) { + doc.searchTerms.keywords = mainTokens; + } + if (memberTokens.length > 0) { + doc.searchTerms.members = memberTokens; + } + if (doc.searchKeywords) { + doc.searchTerms.topics = doc.searchKeywords.trim(); + } + } // Now process all the search data and collect it up to be used in creating a new document - var searchData = filteredDocs.map(function(page) { - // Copy the properties from the searchTerms object onto the search data object - return Object.assign({ - path: page.path, - title: page.searchTitle, - type: page.docType, - deprecated: !!page.deprecated, - }, page.searchTerms); - }); + const searchData = { + dictionary: Array.from(dictionary.keys()), + pages: filteredDocs.map(page => { + // Copy the properties from the searchTerms object onto the search data object + const searchObj = { + path: page.path, + title: page.searchTitle, + type: page.docType, + }; + if (page.deprecated) { + searchObj.deprecated = true; + } + return Object.assign(searchObj, page.searchTerms); + }), + }; docs.push({ docType: 'json-doc', @@ -120,63 +114,64 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) { }; }; - function isString(value) { return typeof value == 'string'; } -function convertToMap(collection) { - const obj = {}; - collection.forEach(key => { obj[key] = true; }); - return obj; -} - -// If the heading contains a name starting with ng, e.g. "ngController", then add the -// name without the ng to the text, e.g. "controller". -function tokenize(text) { - const rawTokens = text.split(/[\s\/]+/mg); +function tokenize(text, ignoreWords, dictionary) { + // Split on whitespace and things that are likely to be HTML tags (this is not exhaustive but reduces the unwanted tokens that are indexed). + const rawTokens = text.split(/[\s\/]+|<\/?[a-z]+(?:\s+\w+(?:="[^"]+")?)*>/img); const tokens = []; - rawTokens.forEach(token => { + for(let token of rawTokens) { + token = token.trim(); + // Strip off unwanted trivial characters - token = token - .trim() - .replace(/^[_\-"'`({[<$*)}\]>.]+/, '') - .replace(/[_\-"'`({[<$*)}\]>.]+$/, ''); - // Ignore tokens that contain weird characters - if (/^[\w.\-]+$/.test(token)) { - tokens.push(token.toLowerCase()); - const ngTokenMatch = /^[nN]g([A-Z]\w*)/.exec(token); - if (ngTokenMatch) { - tokens.push(ngTokenMatch[1].toLowerCase()); - } + token = token.replace(/^[_\-"'`({[<$*)}\]>.]+/, '').replace(/[_\-"'`({[<$*)}\]>.]+$/, ''); + + // Skip if in the ignored words list + if (ignoreWords.has(token.toLowerCase())) { + continue; } - }); + + // Skip tokens that contain weird characters + if (!/^[\w._-]+$/.test(token)) { + continue; + } + + storeToken(token, tokens, dictionary); + if (token.startsWith('ng')) { + storeToken(token.substr(2), tokens, dictionary); + } + } + return tokens; } -function extractWords(text, words, keywordMap) { - var tokens = tokenize(text); - tokens.forEach(function(token) { - if (!keywordMap[token]) { - words.push(token); - keywordMap[token] = true; - } - }); +function storeToken(token, tokens, dictionary) { + token = stem(token); + if (!dictionary.has(token)) { + dictionary.set(token, dictionary.size); + } + tokens.push(dictionary.get(token)); } -function extractMemberWords(doc, members, membersMap) { - if (!doc) return; +function extractMemberTokens(doc, ignoreWords, dictionary) { + if (!doc) return ''; + + let memberContent = []; if (doc.members) { - doc.members.forEach(member => extractWords(member.name, members, membersMap)); + doc.members.forEach(member => memberContent.push(...tokenize(member.name, ignoreWords, dictionary))); } if (doc.statics) { - doc.statics.forEach(member => extractWords(member.name, members, membersMap)); + doc.statics.forEach(member => memberContent.push(...tokenize(member.name, ignoreWords, dictionary))); } if (doc.extendsClauses) { - doc.extendsClauses.forEach(clause => extractMemberWords(clause.doc, members, membersMap)); + doc.extendsClauses.forEach(clause => memberContent.push(...extractMemberTokens(clause.doc, ignoreWords, dictionary))); } if (doc.implementsClauses) { - doc.implementsClauses.forEach(clause => extractMemberWords(clause.doc, members, membersMap)); + doc.implementsClauses.forEach(clause => memberContent.push(...extractMemberTokens(clause.doc, ignoreWords, dictionary))); } + + return memberContent; } diff --git a/aio/tools/transforms/angular-base-package/processors/generateKeywords.spec.js b/aio/tools/transforms/angular-base-package/processors/generateKeywords.spec.js index e482ee4a1b..3065a1c16e 100644 --- a/aio/tools/transforms/angular-base-package/processors/generateKeywords.spec.js +++ b/aio/tools/transforms/angular-base-package/processors/generateKeywords.spec.js @@ -1,12 +1,22 @@ +const path = require('canonical-path'); +const Dgeni = require('dgeni'); + const testPackage = require('../../helpers/test-package'); const mockLogger = require('dgeni/lib/mocks/log')(false); const processorFactory = require('./generateKeywords'); -const Dgeni = require('dgeni'); const mockReadFilesProcessor = { basePath: 'base/path' }; +const ignoreWords = require(path.resolve(__dirname, '../ignore-words'))['en']; + +function createProcessor() { + const processor = processorFactory(mockLogger, mockReadFilesProcessor); + processor.ignoreWords = ignoreWords; + return processor; +} + describe('generateKeywords processor', () => { it('should be available on the injector', () => { @@ -17,30 +27,81 @@ describe('generateKeywords processor', () => { }); it('should run after the correct processor', () => { - const processor = processorFactory(mockLogger, mockReadFilesProcessor); + const processor = createProcessor(); expect(processor.$runAfter).toEqual(['postProcessHtml']); }); it('should run before the correct processor', () => { - const processor = processorFactory(mockLogger, mockReadFilesProcessor); + const processor = createProcessor(); expect(processor.$runBefore).toEqual(['writing-files']); }); it('should ignore internal and private exports', () => { - const processor = processorFactory(mockLogger, mockReadFilesProcessor); + const processor = createProcessor(); const docs = [ { docType: 'class', name: 'PublicExport' }, { docType: 'class', name: 'PrivateExport', privateExport: true }, { docType: 'class', name: 'InternalExport', internal: true } ]; processor.$process(docs); - expect(docs[docs.length - 1].data).toEqual([ - jasmine.objectContaining({ title: 'PublicExport', type: 'class'}) + expect(docs[docs.length - 1].data.pages).toEqual([ + jasmine.objectContaining({ title: 'PublicExport', type: 'class' }) ]); }); + it('should ignore docs that are in the `docTypesToIgnore` list', () => { + const processor = createProcessor(); + processor.docTypesToIgnore = ['interface']; + const docs = [ + { docType: 'class', name: 'Class' }, + { docType: 'interface', name: 'Interface' }, + { docType: 'content', name: 'Guide' }, + ]; + processor.$process(docs); + expect(docs[docs.length - 1].data.pages).toEqual([ + jasmine.objectContaining({ title: 'Class', type: 'class' }), + jasmine.objectContaining({ title: 'Guide', type: 'content' }), + ]); + }); + + it('should not collect keywords from properties that are in the `propertiesToIgnore` list', () => { + const processor = createProcessor(); + processor.propertiesToIgnore = ['docType', 'ignore']; + const docs = [ + { docType: 'class', name: 'FooClass', ignore: 'ignore this content' }, + { docType: 'interface', name: 'BarInterface', capture: 'capture this content' }, + ]; + processor.$process(docs); + expect(docs[docs.length - 1].data).toEqual({ + dictionary: [ 'fooclass', 'barinterfac', 'captur', 'content' ], + pages: [ + jasmine.objectContaining({ title: 'FooClass', type: 'class', keywords: [0] }), + jasmine.objectContaining({ title: 'BarInterface', type: 'interface', keywords: [1, 2, 3] }), + ], + }); + }); + + it('should not collect keywords that look like HTML tags', () => { + const processor = createProcessor(); + const docs = [ + { docType: 'class', name: 'FooClass', content: ` + + + + +
Content inside a table
` }, + ]; + processor.$process(docs); + expect(docs[docs.length - 1].data).toEqual({ + dictionary: ['class', 'fooclass', 'content', 'insid', 'tabl'], + pages: [ + jasmine.objectContaining({keywords: [0, 1, 2, 3, 4] }) + ], + }); + }); + it('should compute `doc.searchTitle` from the doc properties if not already provided', () => { - const processor = processorFactory(mockLogger, mockReadFilesProcessor); + const processor = createProcessor(); const docs = [ { docType: 'class', name: 'A', searchTitle: 'searchTitle A', title: 'title A', vFile: { headings: { h1: ['vFile A'] } } }, { docType: 'class', name: 'B', title: 'title B', vFile: { headings: { h1: ['vFile B'] } } }, @@ -48,7 +109,7 @@ describe('generateKeywords processor', () => { { docType: 'class', name: 'D' }, ]; processor.$process(docs); - expect(docs[docs.length - 1].data).toEqual([ + expect(docs[docs.length - 1].data.pages).toEqual([ jasmine.objectContaining({ title: 'searchTitle A' }), jasmine.objectContaining({ title: 'title B' }), jasmine.objectContaining({ title: 'vFile C' }), @@ -57,34 +118,19 @@ describe('generateKeywords processor', () => { }); it('should use `doc.searchTitle` as the title in the search index', () => { - const processor = processorFactory(mockLogger, mockReadFilesProcessor); + const processor = createProcessor(); const docs = [ { docType: 'class', name: 'PublicExport', searchTitle: 'class PublicExport' }, ]; processor.$process(docs); const keywordsDoc = docs[docs.length - 1]; - expect(keywordsDoc.data).toEqual([ - jasmine.objectContaining({ title: 'class PublicExport', type: 'class'}) + expect(keywordsDoc.data.pages).toEqual([ + jasmine.objectContaining({ title: 'class PublicExport', type: 'class' }) ]); }); - it('should add title words to the search terms', () => { - const processor = processorFactory(mockLogger, mockReadFilesProcessor); - const docs = [ - { - docType: 'class', - name: 'PublicExport', - searchTitle: 'class PublicExport', - vFile: { headings: { h2: ['heading A', 'heading B'] } } - }, - ]; - processor.$process(docs); - const keywordsDoc = docs[docs.length - 1]; - expect(keywordsDoc.data[0].titleWords).toEqual('class publicexport'); - }); - it('should add heading words to the search terms', () => { - const processor = processorFactory(mockLogger, mockReadFilesProcessor); + const processor = createProcessor(); const docs = [ { docType: 'class', @@ -95,11 +141,16 @@ describe('generateKeywords processor', () => { ]; processor.$process(docs); const keywordsDoc = docs[docs.length - 1]; - expect(keywordsDoc.data[0].headingWords).toEqual('heading important secondary'); + expect(keywordsDoc.data).toEqual({ + dictionary: ['class', 'publicexport', 'head', 'secondari'], + pages: [ + jasmine.objectContaining({ headings: [2, 3, 2] }) + ] + }); }); it('should add member doc properties to the search terms', () => { - const processor = processorFactory(mockLogger, mockReadFilesProcessor); + const processor = createProcessor(); const docs = [ { docType: 'class', @@ -123,13 +174,18 @@ describe('generateKeywords processor', () => { ]; processor.$process(docs); const keywordsDoc = docs[docs.length - 1]; - expect(keywordsDoc.data[0].members).toEqual( - 'instancemethoda instancemethodb instancepropertya instancepropertyb staticmethoda staticmethodb staticpropertya staticpropertyb' - ); + expect(keywordsDoc.data).toEqual({ + dictionary: ['class', 'publicexport', 'content', 'ngclass', 'instancemethoda','instancepropertya','instancemethodb','instancepropertyb','staticmethoda','staticpropertya','staticmethodb','staticpropertyb', 'head'], + pages: [ + jasmine.objectContaining({ + members: [4, 5, 6, 7, 8, 9, 10, 11] + }) + ] + }); }); it('should add inherited member doc properties to the search terms', () => { - const processor = processorFactory(mockLogger, mockReadFilesProcessor); + const processor = createProcessor(); const parentClass = { docType: 'class', name: 'ParentClass', @@ -163,13 +219,27 @@ describe('generateKeywords processor', () => { const docs = [childClass, parentClass, parentInterface]; processor.$process(docs); const keywordsDoc = docs[docs.length - 1]; - expect(keywordsDoc.data[0].members.split(' ').sort().join(' ')).toEqual( - 'childmember1 childmember2 parentmember1 parentmember2 parentmember3' - ); + expect(keywordsDoc.data).toEqual({ + dictionary: ['class', 'child', 'childmember1', 'childmember2', 'parentmember1', 'parentmember2', 'parentmember3', 'parentclass', 'interfac', 'parentinterfac'], + pages: [ + jasmine.objectContaining({ + title: 'Child', + members: [2, 3, 4, 5, 6] + }), + jasmine.objectContaining({ + title: 'ParentClass', + members: [4, 5] + }), + jasmine.objectContaining({ + title: 'ParentInterface', + members: [6] + }) + ] + }); }); - it('should process terms prefixed with "ng" to include the term stripped of "ng"', () => { - const processor = processorFactory(mockLogger, mockReadFilesProcessor); + it('should include both stripped and unstripped "ng" prefixed tokens', () => { + const processor = createProcessor(); const docs = [ { docType: 'class', @@ -181,14 +251,19 @@ describe('generateKeywords processor', () => { ]; processor.$process(docs); const keywordsDoc = docs[docs.length - 1]; - expect(keywordsDoc.data[0].titleWords).toEqual('ngcontroller controller'); - expect(keywordsDoc.data[0].headingWords).toEqual('model ngmodel'); - expect(keywordsDoc.data[0].keywords).toContain('class'); - expect(keywordsDoc.data[0].keywords).toContain('ngclass'); + expect(keywordsDoc.data).toEqual({ + dictionary: ['class', 'publicexport', 'ngcontrol', 'control', 'content', 'ngclass', 'ngmodel', 'model'], + pages: [ + jasmine.objectContaining({ + headings: [6, 7], + keywords: [0, 1, 2, 3, 4, 5, 0], + }) + ], + }); }); - it('should generate renderedContent property', () => { - const processor = processorFactory(mockLogger, mockReadFilesProcessor); + it('should generate compressed encoded renderedContent property', () => { + const processor = createProcessor(); const docs = [ { docType: 'class', @@ -196,19 +271,33 @@ describe('generateKeywords processor', () => { description: 'The is the documentation for the SomeClass API.', vFile: { headings: { h1: ['SomeClass'], h2: ['Some heading'] } } }, + { + docType: 'class', + name: 'SomeClass2', + description: 'description', + members: [ + { name: 'member1' }, + ], + deprecated: true + }, ]; processor.$process(docs); const keywordsDoc = docs[docs.length - 1]; - expect(JSON.parse(keywordsDoc.renderedContent)).toEqual( - [{ + expect(JSON.parse(keywordsDoc.renderedContent)).toEqual({ + dictionary: ['class', 'someclass', 'document', 'api', 'head', 'someclass2', 'descript', 'member1'], + pages: [{ 'title':'SomeClass', 'type':'class', - 'titleWords':'someclass', - 'headingWords':'heading some someclass', - 'keywords':'api class documentation for is someclass the', - 'members':'', - 'deprecated': false, + 'headings': [1, 4], + 'keywords': [0, 1, 2, 1, 3], + }, + { + 'title':'SomeClass2', + 'type':'class', + 'keywords': [0, 5, 6], + 'members': [7], + 'deprecated': true, }] - ); + }); }); }); diff --git a/aio/tools/transforms/angular-content-package/index.js b/aio/tools/transforms/angular-content-package/index.js index 6aebd7faf3..37b8ce8c5c 100644 --- a/aio/tools/transforms/angular-content-package/index.js +++ b/aio/tools/transforms/angular-content-package/index.js @@ -18,7 +18,7 @@ const { CONTENTS_PATH, GUIDE_EXAMPLES_PATH } = require('../config'); module.exports = new Package('angular-content', [basePackage, contentPackage]) // Where do we get the source files? - .config(function(readFilesProcessor, collectExamples, renderExamples) { + .config(function(readFilesProcessor, collectExamples) { const gitignoreFilePath = path.resolve(GUIDE_EXAMPLES_PATH, '.gitignore'); const gitignoreFile = fs.readFileSync(gitignoreFilePath, 'utf8'); @@ -82,11 +82,6 @@ module.exports = new Package('angular-content', [basePackage, contentPackage]) include: CONTENTS_PATH + '/marketing/resources.json', fileReader: 'jsonFileReader' }, - { - basePath: CONTENTS_PATH, - include: CONTENTS_PATH + '/translations/**/*.md', - fileReader: 'contentFileReader' - }, { basePath: CONTENTS_PATH, include: CONTENTS_PATH + '/marketing/events.json', @@ -96,8 +91,6 @@ module.exports = new Package('angular-content', [basePackage, contentPackage]) collectExamples.exampleFolders.push('examples'); collectExamples.registerIgnoredExamples(ignoredExamplePaths, gitignoreFilePath); - - renderExamples.ignoreBrokenExamples = true; }) diff --git a/aio/tools/transforms/angular.io-package/index.js b/aio/tools/transforms/angular.io-package/index.js index 1e4b50ea4d..f891ab05cb 100644 --- a/aio/tools/transforms/angular.io-package/index.js +++ b/aio/tools/transforms/angular.io-package/index.js @@ -9,12 +9,10 @@ const Package = require('dgeni').Package; const gitPackage = require('dgeni-packages/git'); const apiPackage = require('../angular-api-package'); const contentPackage = require('../angular-content-package'); +const errorsPackage = require('../angular-errors-package'); const cliDocsPackage = require('../cli-docs-package'); -const { extname, resolve } = require('canonical-path'); -const { existsSync } = require('fs'); -const { SRC_PATH } = require('../config'); -module.exports = new Package('angular.io', [gitPackage, apiPackage, contentPackage, cliDocsPackage]) +module.exports = new Package('angular.io', [gitPackage, apiPackage, contentPackage, cliDocsPackage, errorsPackage]) // This processor relies upon the versionInfo. See below... .processor(require('./processors/processNavigationMap')) @@ -28,35 +26,6 @@ module.exports = new Package('angular.io', [gitPackage, apiPackage, contentPacka renderDocsProcessor.extraData.versionInfo = versionInfo; }) - .config(function(checkAnchorLinksProcessor, linkInlineTagDef, renderExamples) { - - // Fail the processing if there is an invalid link - linkInlineTagDef.failOnBadLink = true; - - checkAnchorLinksProcessor.$enabled = true; - // since we encode the HTML to JSON we need to ensure that this processor runs before that encoding happens. - checkAnchorLinksProcessor.$runBefore = ['convertToJsonProcessor']; - checkAnchorLinksProcessor.$runAfter = ['fixInternalDocumentLinks']; - // We only want to check docs that are going to be output as JSON docs. - checkAnchorLinksProcessor.checkDoc = (doc) => doc.path && doc.outputPath && extname(doc.outputPath) === '.json' && doc.docType !== 'json-doc'; - // Since we have a `base[href="/"]` arrangement all links are relative to that and not relative to the source document's path - checkAnchorLinksProcessor.base = '/'; - // Ignore links to local assets - // (This is not optimal in terms of performance without making changes to dgeni-packages there is no other way. - // That being said do this only add 500ms onto the ~30sec doc-gen run - so not a huge issue) - checkAnchorLinksProcessor.ignoredLinks.push({ - test(url) { - return (existsSync(resolve(SRC_PATH, url))); - } - }); - checkAnchorLinksProcessor.pathVariants = ['', '/', '.html', '/index.html', '#top-of-page']; - checkAnchorLinksProcessor.errorOnUnmatchedLinks = true; - - // Make sure we fail if the examples are not right - renderExamples.ignoreBrokenExamples = false; - - }) - .config(function(renderLinkInfo, postProcessHtml) { renderLinkInfo.docTypes = postProcessHtml.docTypes; }); diff --git a/aio/tools/transforms/angular.io-package/processors/processNavigationMap.js b/aio/tools/transforms/angular.io-package/processors/processNavigationMap.js index c5f0375c60..41877db209 100644 --- a/aio/tools/transforms/angular.io-package/processors/processNavigationMap.js +++ b/aio/tools/transforms/angular.io-package/processors/processNavigationMap.js @@ -23,16 +23,8 @@ module.exports = function processNavigationMap(versionInfo, getPreviousMajorVers throw new Error('processNavigationMap failed'); } - function getArchiveUrl(v) { - if ([4, 5].indexOf(v) !== -1) { - return `https://v${v}.angular.io/`; - } else { - return `https://v${v}.angular.cn/`; - } - } - navigationDoc.data['docVersions'] = getPreviousMajorVersions().map( - v => ({title: `v${v.major}`, url: getArchiveUrl(v.major)})); + v => ({title: `v${v.major}`, url: `https://v${v.major}.angular.io/`})); // Add in the version data in a "secret" field to be extracted in the docs app navigationDoc.data['__versionInfo'] = versionInfo.currentVersion; diff --git a/aio/tools/transforms/authors-package/api-package.js b/aio/tools/transforms/authors-package/api-package.js index d8d34a2412..a7d42bd103 100644 --- a/aio/tools/transforms/authors-package/api-package.js +++ b/aio/tools/transforms/authors-package/api-package.js @@ -8,6 +8,7 @@ const Package = require('dgeni').Package; const apiPackage = require('../angular-api-package'); const { API_SOURCE_PATH } = require('../config'); +const baseAuthoringPackage = require('./base-authoring-package'); const packageMap = { animations: ['animations/index.ts', 'animations/browser/index.ts', 'animations/browser/testing/index.ts'], @@ -15,11 +16,9 @@ const packageMap = { core: ['core/index.ts', 'core/testing/index.ts'], elements: ['elements/index.ts'], forms: ['forms/index.ts'], - // Current plan for Angular v8 is to hide the @angular/http package - // http: ['http/index.ts', 'http/testing/index.ts'], 'platform-browser': ['platform-browser/index.ts', 'platform-browser/animations/index.ts', 'platform-browser/testing/index.ts'], 'platform-browser-dynamic': ['platform-browser-dynamic/index.ts', 'platform-browser-dynamic/testing/index.ts'], - 'platform-server': ['platform-server/index.ts', 'platform-server/testing/index.ts'], + 'platform-server': ['platform-server/index.ts', 'platform-server/init/index.ts', 'platform-server/testing/index.ts'], router: ['router/index.ts', 'router/testing/index.ts', 'router/upgrade/index.ts'], 'service-worker': ['service-worker/index.ts'], upgrade: ['upgrade/index.ts', 'upgrade/static/index.ts', 'upgrade/static/testing/index.ts'] @@ -28,19 +27,19 @@ const packageMap = { function createPackage(packageName) { - return new Package('author-api', [apiPackage]) - .config(function(readTypeScriptModules) { - readTypeScriptModules.sourceFiles = packageMap[packageName]; - }) - .config(function(readFilesProcessor) { - readFilesProcessor.sourceFiles = [ - { - basePath: API_SOURCE_PATH, - include: `${API_SOURCE_PATH}/examples/${packageName}/**/*`, - fileReader: 'exampleFileReader' - } - ]; - }); + return new Package('author-api', [baseAuthoringPackage, apiPackage]) + .config(function(readTypeScriptModules) { + readTypeScriptModules.sourceFiles = packageMap[packageName]; + }) + .config(function(readFilesProcessor) { + readFilesProcessor.sourceFiles = [ + { + basePath: API_SOURCE_PATH, + include: `${API_SOURCE_PATH}/examples/${packageName}/**/*`, + fileReader: 'exampleFileReader' + } + ]; + }); } diff --git a/aio/tools/transforms/authors-package/getting-started-package.js b/aio/tools/transforms/authors-package/getting-started-package.js index 02fa87e39b..a94af77fa4 100644 --- a/aio/tools/transforms/authors-package/getting-started-package.js +++ b/aio/tools/transforms/authors-package/getting-started-package.js @@ -6,42 +6,39 @@ * found in the LICENSE file at https://angular.io/license */ +const {resolve} = require('canonical-path'); const Package = require('dgeni').Package; +const {readFileSync} = require('fs'); + const contentPackage = require('../angular-content-package'); -const { readFileSync } = require('fs'); -const { resolve } = require('canonical-path'); -const { CONTENTS_PATH } = require('../config'); +const {CONTENTS_PATH} = require('../config'); +const baseAuthoringPackage = require('./base-authoring-package'); +const {codeExampleMatcher} = require('./utils'); /* eslint no-console: "off" */ function createPackage(tutorialName) { - const tutorialFilePath = `${CONTENTS_PATH}/start/${tutorialName}.md`; const tutorialFile = readFileSync(tutorialFilePath, 'utf8'); const examples = []; - tutorialFile.replace(/]*path="([^"]+)"/g, (_, path) => examples.push('examples/' + path)); + tutorialFile.replace(codeExampleMatcher, (_, path) => examples.push('examples/' + path)); if (examples.length) { console.log('The following example files are referenced in this getting-started:'); console.log(examples.map(example => ' - ' + example).join('\n')); } - return new Package('author-getting-started', [contentPackage]) - .config(function(readFilesProcessor) { - readFilesProcessor.sourceFiles = [ - { - basePath: CONTENTS_PATH, - include: tutorialFilePath, - fileReader: 'contentFileReader' - }, - { - basePath: CONTENTS_PATH, - include: examples.map(example => resolve(CONTENTS_PATH, example)), - fileReader: 'exampleFileReader' - } - ]; - }); + return new Package('author-getting-started', [baseAuthoringPackage, contentPackage]) + .config(function(readFilesProcessor) { + readFilesProcessor.sourceFiles = [ + {basePath: CONTENTS_PATH, include: tutorialFilePath, fileReader: 'contentFileReader'}, { + basePath: CONTENTS_PATH, + include: examples.map(example => resolve(CONTENTS_PATH, example)), + fileReader: 'exampleFileReader' + } + ]; + }); } -module.exports = { createPackage }; +module.exports = {createPackage}; diff --git a/aio/tools/transforms/authors-package/guide-package.js b/aio/tools/transforms/authors-package/guide-package.js index 3926e21856..e7cffffb07 100644 --- a/aio/tools/transforms/authors-package/guide-package.js +++ b/aio/tools/transforms/authors-package/guide-package.js @@ -8,39 +8,35 @@ /* eslint no-console: "off" */ +const {resolve} = require('canonical-path'); const Package = require('dgeni').Package; +const {readFileSync} = require('fs'); + const contentPackage = require('../angular-content-package'); -const { readFileSync } = require('fs'); -const { resolve } = require('canonical-path'); -const { CONTENTS_PATH } = require('../config'); +const {CONTENTS_PATH} = require('../config'); +const baseAuthoringPackage = require('./base-authoring-package'); +const {codeExampleMatcher} = require('./utils'); function createPackage(guideName) { - const guideFilePath = `${CONTENTS_PATH}/guide/${guideName}.md`; const guideFile = readFileSync(guideFilePath, 'utf8'); const examples = []; - guideFile.replace(/]*path="([^"]+)"/g, (_, path) => examples.push('examples/' + path)); + guideFile.replace(codeExampleMatcher, (_, path) => examples.push('examples/' + path)); if (examples.length) { console.log('The following example files are referenced in this guide:'); console.log(examples.map(example => ' - ' + example).join('\n')); } - return new Package('author-guide', [contentPackage]) - .config(function(readFilesProcessor) { - readFilesProcessor.sourceFiles = [ - { - basePath: CONTENTS_PATH, - include: guideFilePath, - fileReader: 'contentFileReader' - }, - { - basePath: CONTENTS_PATH, - include: examples.map(example => resolve(CONTENTS_PATH, example)), - fileReader: 'exampleFileReader' - } - ]; - }); + return new Package('author-guide', [baseAuthoringPackage, contentPackage]).config(function(readFilesProcessor) { + readFilesProcessor.sourceFiles = [ + {basePath: CONTENTS_PATH, include: guideFilePath, fileReader: 'contentFileReader'}, { + basePath: CONTENTS_PATH, + include: examples.map(example => resolve(CONTENTS_PATH, example)), + fileReader: 'exampleFileReader' + } + ]; + }); } -module.exports = { createPackage }; +module.exports = {createPackage}; diff --git a/aio/tools/transforms/authors-package/index.js b/aio/tools/transforms/authors-package/index.js index cfb5dac96a..bfd523e008 100644 --- a/aio/tools/transforms/authors-package/index.js +++ b/aio/tools/transforms/authors-package/index.js @@ -51,6 +51,10 @@ module.exports = { generateDocs: function(changedFile, options = {}) { const {Dgeni} = require('dgeni'); const package = createPackage(changedFile); + if (package === undefined) { + console.log('The changed file was not matched to a dgeni package - skipping doc-gen'); + return Promise.resolve(); + } if (options.silent) { package.config(function(log) { log.level = 'error'; }); } diff --git a/aio/tools/transforms/authors-package/marketing-package.js b/aio/tools/transforms/authors-package/marketing-package.js index dd762096fe..8a51250fe8 100644 --- a/aio/tools/transforms/authors-package/marketing-package.js +++ b/aio/tools/transforms/authors-package/marketing-package.js @@ -7,10 +7,11 @@ */ const Package = require('dgeni').Package; const contentPackage = require('../angular-content-package'); -const { CONTENTS_PATH } = require('../config'); +const {CONTENTS_PATH} = require('../config'); +const baseAuthoringPackage = require('./base-authoring-package'); function createPackage() { - return new Package('author-marketing', [contentPackage]) + return new Package('author-marketing', [baseAuthoringPackage, contentPackage]) .config(function(readFilesProcessor) { readFilesProcessor.sourceFiles = [ { diff --git a/aio/tools/transforms/authors-package/tutorial-package.js b/aio/tools/transforms/authors-package/tutorial-package.js index 0bcc72ca04..020deb08fb 100644 --- a/aio/tools/transforms/authors-package/tutorial-package.js +++ b/aio/tools/transforms/authors-package/tutorial-package.js @@ -6,42 +6,38 @@ * found in the LICENSE file at https://angular.io/license */ +const {resolve} = require('canonical-path'); const Package = require('dgeni').Package; +const {readFileSync} = require('fs'); + const contentPackage = require('../angular-content-package'); -const { readFileSync } = require('fs'); -const { resolve } = require('canonical-path'); -const { CONTENTS_PATH } = require('../config'); +const {CONTENTS_PATH} = require('../config'); +const baseAuthoringPackage = require('./base-authoring-package'); +const {codeExampleMatcher} = require('./utils'); /* eslint no-console: "off" */ function createPackage(tutorialName) { - const tutorialFilePath = `${CONTENTS_PATH}/tutorial/${tutorialName}.md`; const tutorialFile = readFileSync(tutorialFilePath, 'utf8'); const examples = []; - tutorialFile.replace(/]*path="([^"]+)"/g, (_, path) => examples.push('examples/' + path)); + tutorialFile.replace(codeExampleMatcher, (_, path) => examples.push('examples/' + path)); if (examples.length) { console.log('The following example files are referenced in this tutorial:'); console.log(examples.map(example => ' - ' + example).join('\n')); } - return new Package('author-tutorial', [contentPackage]) - .config(function(readFilesProcessor) { - readFilesProcessor.sourceFiles = [ - { - basePath: CONTENTS_PATH, - include: tutorialFilePath, - fileReader: 'contentFileReader' - }, - { - basePath: CONTENTS_PATH, - include: examples.map(example => resolve(CONTENTS_PATH, example)), - fileReader: 'exampleFileReader' - } - ]; - }); + return new Package('author-tutorial', [baseAuthoringPackage, contentPackage]).config(function(readFilesProcessor) { + readFilesProcessor.sourceFiles = [ + {basePath: CONTENTS_PATH, include: tutorialFilePath, fileReader: 'contentFileReader'}, { + basePath: CONTENTS_PATH, + include: examples.map(example => resolve(CONTENTS_PATH, example)), + fileReader: 'exampleFileReader' + } + ]; + }); } -module.exports = { createPackage }; +module.exports = {createPackage}; diff --git a/aio/tools/transforms/authors-package/watchr.js b/aio/tools/transforms/authors-package/watchr.js index 70982f43dc..d9cf254ef3 100644 --- a/aio/tools/transforms/authors-package/watchr.js +++ b/aio/tools/transforms/authors-package/watchr.js @@ -29,15 +29,11 @@ if (process.argv.indexOf('--watch-only') === -1) { console.log('Skip the full doc-gen by running: `yarn docs-watch --watch-only`'); console.log('================================================================'); const {Dgeni} = require('dgeni'); - const dgeni = new Dgeni([require('../angular.io-package')]); - - // Turn off all the potential failures for this doc-gen one-off run. - // This enables authors to run `docs-watch` while the docs are still in an unstable state. - const injector = dgeni.configureInjector(); - injector.get('linkInlineTagDef').failOnBadLink = false; - injector.get('checkAnchorLinksProcessor').$enabled = false; - injector.get('renderExamples').ignoreBrokenExamples = true; - + const dgeni = new Dgeni([ + // The base-authoring-package will turn off potential failures for this doc-gen one-off run. + // This enables authors to run `docs-watch` while the docs are still in an unstable state. + require('./base-authoring-package'), + require('../angular.io-package')]); p = dgeni.generate(); } diff --git a/aio/tools/transforms/cli-docs-package/extract-cli-commands.js b/aio/tools/transforms/cli-docs-package/extract-cli-commands.js index 740de4925d..13eed58796 100644 --- a/aio/tools/transforms/cli-docs-package/extract-cli-commands.js +++ b/aio/tools/transforms/cli-docs-package/extract-cli-commands.js @@ -5,7 +5,7 @@ const {CONTENTS_PATH} = require('../config'); const cliGitRef = process.argv[2] || 'master'; // Can be a branch, commit or tag. const pkgContent = JSON.stringify({ dependencies: { - '@angular/cli': `https://github.com/ng-docs/angular-cli-cn-prebuilt#11.2.x`, + '@angular/cli': `https://github.com/angular/cli-builds#${cliGitRef}`, }, }, null, 2); diff --git a/aio/tools/transforms/examples-package/index.js b/aio/tools/transforms/examples-package/index.js index 436cde974f..39da1cb346 100644 --- a/aio/tools/transforms/examples-package/index.js +++ b/aio/tools/transforms/examples-package/index.js @@ -13,6 +13,7 @@ module.exports = .processor(require('./processors/collect-examples')) .processor(require('./processors/render-examples')) + .processor(require('./processors/check-for-unused-example-regions')) .config(function(readFilesProcessor, exampleFileReader) { readFilesProcessor.fileReaders.push(exampleFileReader); diff --git a/aio/tools/transforms/examples-package/services/getExampleRegion.js b/aio/tools/transforms/examples-package/services/getExampleRegion.js index 62defda9ba..94fd9274f9 100644 --- a/aio/tools/transforms/examples-package/services/getExampleRegion.js +++ b/aio/tools/transforms/examples-package/services/getExampleRegion.js @@ -1,4 +1,4 @@ -module.exports = function getExampleRegion(exampleMap, createDocMessage, collectExamples) { +module.exports = function getExampleRegion(exampleMap, createDocMessage, collectExamples, log) { return function getExampleRegionImpl(doc, relativePath, regionName) { const EXAMPLES_FOLDERS = collectExamples.exampleFolders; @@ -29,10 +29,12 @@ module.exports = function getExampleRegion(exampleMap, createDocMessage, collect var sourceCodeDoc = exampleFile.regions[regionName || '']; if (!sourceCodeDoc) { const message = createDocMessage('Missing example region... relativePath: "' + relativePath + '", region: "' + regionName + '".', doc) + '\n' + - 'Regions available are: ' + Object.keys(exampleFile.regions).map(function(region) { return '"' + region + '"'; }).join(', '); + 'Regions available are: ' + Object.keys(exampleFile.regions).map(function(region) { return '"' + region + '"'; }).join(', '); throw new Error(message); } + sourceCodeDoc.usedInDoc = doc; + log.debug(`Example region ${sourceCodeDoc.id} used in ${doc.id}`); return sourceCodeDoc.renderedContent; }; }; diff --git a/aio/tools/transforms/examples-package/services/getExampleRegion.spec.js b/aio/tools/transforms/examples-package/services/getExampleRegion.spec.js index ba42d11805..362ff0a2cc 100644 --- a/aio/tools/transforms/examples-package/services/getExampleRegion.spec.js +++ b/aio/tools/transforms/examples-package/services/getExampleRegion.spec.js @@ -43,4 +43,15 @@ describe('getExampleRegion', () => { }).toThrowError('Ignored example file... relativePath: "filtered/path" - doc\n' + 'This example file exists but has been ignored by a rule, in "some/gitignore".'); }); + + it('should mark the example as having been "used"', () => { + const doc1 = {}; + const doc2 = {}; + expect(exampleMap['examples']['test/url'].regions['region-1'].usedInDoc).toBeUndefined(); + getExampleRegion(doc1, 'test/url', 'region-1'); + expect(exampleMap['examples']['test/url'].regions['region-1'].usedInDoc).toBe(doc1); + expect(exampleMap['examples']['test/url'].regions[''].usedInDoc).toBeUndefined(); + getExampleRegion(doc2, 'test/url'); + expect(exampleMap['examples']['test/url'].regions[''].usedInDoc).toBe(doc2); + }); }); diff --git a/aio/tools/transforms/links-package/inline-tag-defs/link.js b/aio/tools/transforms/links-package/inline-tag-defs/link.js index 16f44baaf7..ff671bae5f 100644 --- a/aio/tools/transforms/links-package/inline-tag-defs/link.js +++ b/aio/tools/transforms/links-package/inline-tag-defs/link.js @@ -14,7 +14,7 @@ var INLINE_LINK = /(\S+)(?:\s+([\s\S]+))?/; module.exports = function linkInlineTagDef(getLinkInfo, createDocMessage, log) { return { name: 'link', - failOnBadLink: false, + failOnBadLink: true, description: 'Process inline link tags (of the form {@link some/uri Some Title}), replacing them with HTML anchors', handler(doc, tagName, tagDescription) { diff --git a/aio/tools/transforms/links-package/inline-tag-defs/link.spec.js b/aio/tools/transforms/links-package/inline-tag-defs/link.spec.js index c380b2ce3c..878ab04859 100644 --- a/aio/tools/transforms/links-package/inline-tag-defs/link.spec.js +++ b/aio/tools/transforms/links-package/inline-tag-defs/link.spec.js @@ -25,7 +25,7 @@ describe('link inline-tag-def', function() { const doc = {}; const tagName = 'link'; const tagDescription = 'doc-id link text'; - getLinkInfo.and.returnValue({ url: 'url/to/doc', title: 'link text' }); + getLinkInfo.and.returnValue({ url: 'url/to/doc', title: 'link text', valid: true }); tag.handler(doc, tagName, tagDescription); expect(getLinkInfo).toHaveBeenCalledWith('doc-id', 'link text', doc); }); @@ -34,7 +34,7 @@ describe('link inline-tag-def', function() { const doc = {}; const tagName = 'link'; const tagDescription = 'doc-id link text'; - getLinkInfo.and.returnValue({ url: 'url/to/doc', title: 'link text' }); + getLinkInfo.and.returnValue({ url: 'url/to/doc', title: 'link text', valid: true }); const result = tag.handler(doc, tagName, tagDescription); expect(result).toEqual('link text'); }); @@ -43,6 +43,7 @@ describe('link inline-tag-def', function() { const doc = {}; const tagName = 'link'; const tagDescription = 'doc-id link text'; + tag.failOnBadLink = false; getLinkInfo.and.returnValue({ valid: false, error: 'Error message', errorType: 'error' }); expect(() => tag.handler(doc, tagName, tagDescription)).not.toThrow(); expect(log.warn).toHaveBeenCalledWith('Error in {@link doc-id link text} - Error message - doc'); diff --git a/aio/tools/transforms/remark-package/services/renderMarkdown.js b/aio/tools/transforms/remark-package/services/renderMarkdown.js index ec13d23239..4bdaf90021 100644 --- a/aio/tools/transforms/remark-package/services/renderMarkdown.js +++ b/aio/tools/transforms/remark-package/services/renderMarkdown.js @@ -3,8 +3,6 @@ const html = require('remark-html'); const code = require('./handlers/code'); const mapHeadings = require('./plugins/mapHeadings'); -const {mark} = require('./translator'); - /** * @dgService renderMarkdown * @description @@ -26,7 +24,7 @@ module.exports = function renderMarkdown() { .use(mapHeadings(headingMap)) .use(html, { handlers: { code } }); - return mark(renderer.processSync(content).toString()); + return renderer.processSync(content).toString(); }; /** @@ -213,4 +211,4 @@ function createOpenMatcher(elementNameMatcher) { function createCloseMatcher(elementNameMatcher) { return ``; -} +} \ No newline at end of file diff --git a/aio/tools/transforms/remark-package/services/translator.js b/aio/tools/transforms/remark-package/services/translator.js deleted file mode 100644 index 4435d51a5c..0000000000 --- a/aio/tools/transforms/remark-package/services/translator.js +++ /dev/null @@ -1,13 +0,0 @@ -const {Marker, defaultSelectors, DomDocumentFragment} = require('@awesome-fe/translate'); - -function mark(text) { - const marker = new Marker([...defaultSelectors, (node) => node.isTagOf('header') || node.isTagOf('section')]); - const doc = DomDocumentFragment.parse(text); - marker.addIdForHeaders(doc); - marker.markAndSwapAll(doc); - return doc.toHtml(); -} - -module.exports = { - mark, -}; diff --git a/aio/tools/transforms/remark-package/services/translator.mocha.js b/aio/tools/transforms/remark-package/services/translator.mocha.js deleted file mode 100644 index ffadafeb10..0000000000 --- a/aio/tools/transforms/remark-package/services/translator.mocha.js +++ /dev/null @@ -1,12 +0,0 @@ -const { expect } = require('chai'); - -const { mark } = require('./translator'); - -describe('translator', () => { - it('mark translation results and origins', () => { - expect(mark(`

One or two

-

一或二

`)) - .eql(`

One or two

-

一或二

`); - }); -}); diff --git a/aio/tools/transforms/templates/api/base.template.html b/aio/tools/transforms/templates/api/base.template.html index bf7e103037..a30f8c3862 100644 --- a/aio/tools/transforms/templates/api/base.template.html +++ b/aio/tools/transforms/templates/api/base.template.html @@ -10,7 +10,7 @@ "@type": "BreadcrumbList", "itemListElement": [ {%- for crumb in doc.breadCrumbs %}{$ comma() $} - { "@type": "ListItem", "position": {$ loop.index $}, "item": { "@id": "https://angular.cn/{$ crumb.path $}", "name": "{$ crumb.text $}" } }{% endfor %} + { "@type": "ListItem", "position": {$ loop.index $}, "item": { "@id": "https://angular.io/{$ crumb.path $}", "name": "{$ crumb.text $}" } }{% endfor %} ] } @@ -23,9 +23,9 @@

{$ doc.name $}

{% if doc.global %}{% endif %} - {% if doc.deprecated !== undefined %}{% endif %} - {% if doc.security !== undefined %}{% endif %} - {% if doc.pipeOptions.pure === 'false' %}{% endif %} + {% if doc.deprecated !== undefined %}{% endif %} + {% if doc.security !== undefined %}{% endif %} + {% if doc.pipeOptions.pure === 'false' %}{% endif %} {% endblock %} diff --git a/aio/tools/transforms/templates/api/decorator.template.html b/aio/tools/transforms/templates/api/decorator.template.html index 9adffe3315..7c24766dd6 100644 --- a/aio/tools/transforms/templates/api/decorator.template.html +++ b/aio/tools/transforms/templates/api/decorator.template.html @@ -8,7 +8,7 @@ {% include "includes/description.html" %}
-

选项

+

Options

{% for option in doc.members %} diff --git a/aio/tools/transforms/templates/api/directive.template.html b/aio/tools/transforms/templates/api/directive.template.html index d72a01cfd0..1596da9825 100644 --- a/aio/tools/transforms/templates/api/directive.template.html +++ b/aio/tools/transforms/templates/api/directive.template.html @@ -7,22 +7,22 @@ {% include "includes/ngmodule.html" %} {% include "includes/selectors.html" %} - {$ memberHelpers.renderDirectiveProperties(doc, '属性') $} + {$ memberHelpers.renderDirectiveProperties(doc, 'Properties') $} {% include "includes/export-as.html" %} {% if doc.description or doc.usageNotes %}
-

说明

+

Description

{$ (doc.description or '') | trimBlankLines | marked $} {$ (doc.usageNotes or '') | trimBlankLines | marked $}
{% endif %} - {$ memberHelpers.renderProperties(doc.staticProperties, 'static-properties', 'static-property', '静态属性') $} - {$ memberHelpers.renderMethodDetails(versionInfo, doc.staticMethods, 'static-methods', 'static-method', '静态方法') $} - {$ memberHelpers.renderMethodDetails(versionInfo, doc.methods, 'instance-methods', 'instance-method', '方法') $} + {$ memberHelpers.renderProperties(doc.staticProperties, 'static-properties', 'static-property', 'Static properties') $} + {$ memberHelpers.renderMethodDetails(versionInfo, doc.staticMethods, 'static-methods', 'static-method', 'Static methods') $} + {$ memberHelpers.renderMethodDetails(versionInfo, doc.methods, 'instance-methods', 'instance-method', 'Methods') $} {$ memberHelpers.renderDirectiveAncestors(doc, 'methods') $} {% endblock %} -{% block endNotes %}{% endblock %} +{% block endNotes %}{% endblock %} \ No newline at end of file diff --git a/aio/tools/transforms/templates/api/enum.template.html b/aio/tools/transforms/templates/api/enum.template.html index c2913b8abc..93321b4ced 100644 --- a/aio/tools/transforms/templates/api/enum.template.html +++ b/aio/tools/transforms/templates/api/enum.template.html @@ -10,5 +10,5 @@ enum {$ doc.name $} {{$ memberHelpers.renderMembers(doc) $} {% include "includes/description.html" %} -{$ memberHelpers.renderProperties(doc.properties, 'members', 'member', '成员列表', ['成员']) $} -{% endblock %} +{$ memberHelpers.renderProperties(doc.properties, 'members', 'member', 'Members', ['Member']) $} +{% endblock %} \ No newline at end of file diff --git a/aio/tools/transforms/templates/api/export-base.template.html b/aio/tools/transforms/templates/api/export-base.template.html index 51d8e3eebf..9b94d5a312 100644 --- a/aio/tools/transforms/templates/api/export-base.template.html +++ b/aio/tools/transforms/templates/api/export-base.template.html @@ -3,7 +3,7 @@ {% block body %}
{$ doc.shortDescription | marked $} - {% if doc.description %}

查看"说明"...

{% endif %} + {% if doc.description %}

See more...

{% endif %}
{% include "includes/security-notes.html" %} {% include "includes/deprecation.html" %} diff --git a/aio/tools/transforms/templates/api/function.template.html b/aio/tools/transforms/templates/api/function.template.html index 8234d80c4e..c5707f1f2a 100644 --- a/aio/tools/transforms/templates/api/function.template.html +++ b/aio/tools/transforms/templates/api/function.template.html @@ -18,7 +18,7 @@ {% include "includes/description.html" %} {% if doc.overloads.length >= 3 %}
-

重载形式

+

Overloads

{% for overload in doc.overloads %} @@ -30,4 +30,4 @@
{% endif %} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/aio/tools/transforms/templates/api/includes/annotations.html b/aio/tools/transforms/templates/api/includes/annotations.html index eee044fd0e..637a7305a7 100644 --- a/aio/tools/transforms/templates/api/includes/annotations.html +++ b/aio/tools/transforms/templates/api/includes/annotations.html @@ -1,6 +1,6 @@ {%- if doc.decorators.length %}
-

注解

+

Annotations

{%- for decorator in doc.decorators %} @{$ decorator.name $}({$ decorator.arguments $}) {% if not decorator.notYetDocumented %}{$ decorator.description | marked $}{% endif %} diff --git a/aio/tools/transforms/templates/api/includes/class-members.html b/aio/tools/transforms/templates/api/includes/class-members.html index 56c14d6a23..7faf3ebaa7 100644 --- a/aio/tools/transforms/templates/api/includes/class-members.html +++ b/aio/tools/transforms/templates/api/includes/class-members.html @@ -1,14 +1,14 @@ {% import "lib/memberHelpers.html" as memberHelpers -%} -{$ memberHelpers.renderProperties(doc.staticProperties, 'static-properties', 'static-properties', '静态属性') $} +{$ memberHelpers.renderProperties(doc.staticProperties, 'static-properties', 'static-property', 'Static properties') $} -{$ memberHelpers.renderMethodDetails(versionInfo, doc.staticMethods, 'static-methods', 'static-methods', '静态方法') $} +{$ memberHelpers.renderMethodDetails(versionInfo, doc.staticMethods, 'static-methods', 'static-method', 'Static methods') $} {% if doc.constructorDoc %} -

构造函数

+

Constructor

{$ memberHelpers.renderMethodDetail(versionInfo, doc.constructorDoc, 'constructor') $} {% endif %} -{$ memberHelpers.renderProperties(doc.properties, 'instance-properties', 'instance-properties', '属性') $} +{$ memberHelpers.renderProperties(doc.properties, 'instance-properties', 'instance-property', 'Properties') $} -{$ memberHelpers.renderMethodDetails(versionInfo, doc.methods, 'instance-methods', 'instance-methods', '方法') $} +{$ memberHelpers.renderMethodDetails(versionInfo, doc.methods, 'instance-methods', 'instance-method', 'Methods') $} diff --git a/aio/tools/transforms/templates/api/includes/class-overview.html b/aio/tools/transforms/templates/api/includes/class-overview.html index 58ffc20312..26c95f9793 100644 --- a/aio/tools/transforms/templates/api/includes/class-overview.html +++ b/aio/tools/transforms/templates/api/includes/class-overview.html @@ -6,5 +6,5 @@ {% if doc.isAbstract %}abstract {% endif%}class {$ doc.name $}{$ doc.typeParams | escape $}{$ memberHelper.renderHeritage(doc) $} {{$ memberHelper.renderMembers(doc) $} } -{$ descendants.renderDescendants(doc, 'class', '子类', true, r/class|directive|pipe|decorator/) $} +{$ descendants.renderDescendants(doc, 'class', 'Subclasses', true, r/class|directive|pipe|decorator/) $}
diff --git a/aio/tools/transforms/templates/api/includes/decorator-overview.html b/aio/tools/transforms/templates/api/includes/decorator-overview.html index 94f590b6ff..20d20db6c4 100644 --- a/aio/tools/transforms/templates/api/includes/decorator-overview.html +++ b/aio/tools/transforms/templates/api/includes/decorator-overview.html @@ -3,7 +3,7 @@ {% macro renderOptionsTable(doc) %} - + {%- for option in doc.members %} @@ -20,7 +20,7 @@
选项说明
OptionDescription
{% for ancestor in doc.extendsClauses %}{% if ancestor.doc %} -

继承自 {$ ancestor.doc.name $} 装饰器

+

Inherited from {$ ancestor.doc.name $} decorator

{$ renderOptionsTable(ancestor.doc) $} {% endif %}{% endfor %} {% endmacro %} @@ -28,6 +28,6 @@ {% if doc.members.length %}
{$ renderOptionsTable(doc) $} - {$ descendants.renderDescendants(doc, 'decorator', '子类') $} + {$ descendants.renderDescendants(doc, 'decorator', 'Subclasses') $}
{% endif %} diff --git a/aio/tools/transforms/templates/api/includes/deprecation.html b/aio/tools/transforms/templates/api/includes/deprecation.html index dd8dfc268b..2cf5b963f2 100644 --- a/aio/tools/transforms/templates/api/includes/deprecation.html +++ b/aio/tools/transforms/templates/api/includes/deprecation.html @@ -1,5 +1,5 @@ {% if doc.deprecated !== undefined %}
- {$ ('**已弃用:** ' + doc.deprecated) | marked $} + {$ ('**Deprecated:** ' + doc.deprecated) | marked $}
-{% endif %} +{% endif %} \ No newline at end of file diff --git a/aio/tools/transforms/templates/api/includes/description.html b/aio/tools/transforms/templates/api/includes/description.html index 79bfd4edc7..22cede4edd 100644 --- a/aio/tools/transforms/templates/api/includes/description.html +++ b/aio/tools/transforms/templates/api/includes/description.html @@ -1,6 +1,8 @@ {% if doc.description %}
-

说明

+

Description

{$ doc.description | trimBlankLines | marked $} + {% if doc.usageNotes %}

Further information available in the Usage Notes...

{% + endif %}
-{% endif %} +{% endif %} \ No newline at end of file diff --git a/aio/tools/transforms/templates/api/includes/export-as.html b/aio/tools/transforms/templates/api/includes/export-as.html index d2976f2300..f003731da8 100644 --- a/aio/tools/transforms/templates/api/includes/export-as.html +++ b/aio/tools/transforms/templates/api/includes/export-as.html @@ -1,11 +1,11 @@ {%- if doc.exportAs %}
-

模板变量参考手册

+

Template variable references

- - + + diff --git a/aio/tools/transforms/templates/api/includes/interface-overview.html b/aio/tools/transforms/templates/api/includes/interface-overview.html index a351a59d13..3e52b9b983 100644 --- a/aio/tools/transforms/templates/api/includes/interface-overview.html +++ b/aio/tools/transforms/templates/api/includes/interface-overview.html @@ -5,6 +5,6 @@ interface {$ doc.name $}{$ doc.typeParams | escape $}{$ memberHelper.renderHeritage(doc) $} {{$ memberHelper.renderMembers(doc) $} } -{$ descendants.renderDescendants(doc, 'interface', '子接口') $} -{$ descendants.renderDescendants(doc, 'class', '实现类', true, r/class|directive|pipe|decorator/) $} +{$ descendants.renderDescendants(doc, 'interface', 'Child interfaces') $} +{$ descendants.renderDescendants(doc, 'class', 'Class implementations', true, r/class|directive|pipe|decorator/) $} diff --git a/aio/tools/transforms/templates/api/includes/metadata.html b/aio/tools/transforms/templates/api/includes/metadata.html index e37e9a0e48..9aa301e44d 100644 --- a/aio/tools/transforms/templates/api/includes/metadata.html +++ b/aio/tools/transforms/templates/api/includes/metadata.html @@ -1,6 +1,6 @@ {% if doc.members.length %} diff --git a/aio/tools/transforms/templates/api/includes/security-notes.html b/aio/tools/transforms/templates/api/includes/security-notes.html index baae166cd4..ff13159b9d 100644 --- a/aio/tools/transforms/templates/api/includes/security-notes.html +++ b/aio/tools/transforms/templates/api/includes/security-notes.html @@ -1,6 +1,6 @@ {% if doc.security %}
-

安全风险

+

Security risk

{$ doc.security | marked $}
-{% endif %} +{% endif %} \ No newline at end of file diff --git a/aio/tools/transforms/templates/api/includes/see-also.html b/aio/tools/transforms/templates/api/includes/see-also.html index 2cb42b764e..5d37c3340f 100644 --- a/aio/tools/transforms/templates/api/includes/see-also.html +++ b/aio/tools/transforms/templates/api/includes/see-also.html @@ -1,6 +1,6 @@ {%- if doc.see.length %}
-

参见

+

See also

    {% for see in doc.see %}
  • {$ see | marked $}
  • {% endfor %} diff --git a/aio/tools/transforms/templates/api/includes/selectors.html b/aio/tools/transforms/templates/api/includes/selectors.html index cdca019e68..63c92ece55 100644 --- a/aio/tools/transforms/templates/api/includes/selectors.html +++ b/aio/tools/transforms/templates/api/includes/selectors.html @@ -1,6 +1,6 @@ {%- if doc.selector %}
    -

    选择器

    +

    Selectors

    {% if doc.selectors %} {$ doc.selectors | marked $} {% else %} diff --git a/aio/tools/transforms/templates/api/includes/usageNotes.html b/aio/tools/transforms/templates/api/includes/usageNotes.html index 8495c5a62c..7b5b0b20ea 100644 --- a/aio/tools/transforms/templates/api/includes/usageNotes.html +++ b/aio/tools/transforms/templates/api/includes/usageNotes.html @@ -1,6 +1,6 @@ {% if doc.usageNotes %}
    -

    使用说明

    +

    Usage notes

    {$ doc.usageNotes | marked $}
    {% endif %} diff --git a/aio/tools/transforms/templates/api/interface.template.html b/aio/tools/transforms/templates/api/interface.template.html index 0406969834..79fb8c5c12 100644 --- a/aio/tools/transforms/templates/api/interface.template.html +++ b/aio/tools/transforms/templates/api/interface.template.html @@ -6,6 +6,6 @@ {% block overview %}{% include "includes/interface-overview.html" %}{% endblock %} {% block details %} {% include "includes/description.html" %} - {$ memberHelper.renderProperties(doc.properties, 'instance-properties', 'instance-property', '属性') $} - {$ memberHelper.renderMethodDetails(versionInfo, doc.methods, 'instance-methods', 'instance-method', '方法') $} + {$ memberHelper.renderProperties(doc.properties, 'instance-properties', 'instance-property', 'Properties') $} + {$ memberHelper.renderMethodDetails(versionInfo, doc.methods, 'instance-methods', 'instance-method', 'Methods') $} {% endblock %} diff --git a/aio/tools/transforms/templates/api/lib/descendants.html b/aio/tools/transforms/templates/api/lib/descendants.html index 3bfd3c95bb..2ce10441c9 100644 --- a/aio/tools/transforms/templates/api/lib/descendants.html +++ b/aio/tools/transforms/templates/api/lib/descendants.html @@ -4,7 +4,7 @@ {% for descendant in descendants %}
  • {$ descendant.name $} - {$ renderDescendantList(descendant.descendants | filterByPropertyValue('docType', docTypeMatcher), docType, recursed, docTypeMatcher) $} + {$ renderDescendantList(descendant.descendants | filterByPropertyValue('docType', docTypeMatcher) | filterByPropertyValue('privateExport', undefined), docType, recursed, docTypeMatcher) $}
  • {% endfor %}
diff --git a/aio/tools/transforms/templates/api/lib/memberHelpers.html b/aio/tools/transforms/templates/api/lib/memberHelpers.html index 7e3a0a6bb0..49dc46c02a 100644 --- a/aio/tools/transforms/templates/api/lib/memberHelpers.html +++ b/aio/tools/transforms/templates/api/lib/memberHelpers.html @@ -24,7 +24,7 @@ {%- for ancestor in doc.extendsClauses %}{% if ancestor.doc %} - // 继承自 {$ ancestor.doc.id $}{$ renderMembers(ancestor.doc) $}{% endif %}{% endfor -%} + // inherited from {$ ancestor.doc.id $}{$ renderMembers(ancestor.doc) $}{% endif %}{% endfor -%} {%- endmacro -%} {%- macro renderMemberSyntax(member, truncateLines) -%} @@ -53,19 +53,19 @@ {$ ('**Deprecated** ' + overload.deprecated) | marked $} {% endif %} -
参数
+
Parameters
{$ params.renderParameters(overload.parameterDocs, cssClass + '-parameters', cssClass + '-parameter', true) $} {% if overload.type or overload.returns.type %} -
返回值
+
Returns
{% marked %}`{$ (overload.type or overload.returns.type) $}`{% if overload.returns %}: {$ overload.returns.description $}{% endif %}{% endmarked %} {% endif %} {% if overload.throws.length %} -
异常
+
Throws
{% for error in overload.throws %} - {% marked %}`{$ (error.typeList or '错误') $}` {$ error.description $}{% endmarked %} + {% marked %}`{$ (error.typeList or 'Error') $}` {$ error.description $}{% endmarked %} {% endfor %} {% endif %} @@ -83,8 +83,8 @@ {% if method.name !== 'constructor' %}
@@ -179,8 +179,7 @@ {% set nonInternalMethods = methods | filterByPropertyValue('internal', undefined) %} {% if nonInternalMethods.length %}
- -

{$ headingText $}

+

{$ headingText $}

{% for member in nonInternalMethods %} {$ renderMethodDetail(versionInfo, member, itemClass) $} {% endfor %} @@ -193,13 +192,12 @@ {% set nonInternalProperties = properties | filterByPropertyValue('internal', undefined) %} {% if nonInternalProperties.length -%}
- - {$ headingText $} + {$ headingText $}
标识符用途IdentifierUsage
-

使用说明

+

Usage Notes

{$ method.usageNotes | marked({ h3: 'h5' }) $}
- - + + @@ -210,13 +208,13 @@ {$ renderMemberSyntax(property) $}{% endfor %}
{$ headings[0] or '属性' $}{$ headings[1] or '说明' $}{$ headings[0] or 'Property' $}{$ headings[1] or 'Description' $}
- {%- if (property.isGetAccessor or property.isReadonly) and not property.isSetAccessor %}只读{% endif %} - {%- if property.isSetAccessor and not property.isGetAccessor %}只写{% endif %} - {% if property.constructorParamDoc %} 声明在构造函数中{% endif %} - {% if property.shortDescription %}
{$ property.shortDescription | marked $}
{% endif %} + {%- if (property.isGetAccessor or property.isReadonly) and not property.isSetAccessor %}Read-Only{% endif %} + {%- if property.isSetAccessor and not property.isGetAccessor %}Write-Only{% endif %} + {% if property.constructorParamDoc %} Declared in Constructor{% endif %} + {% if property.shortDescription %}{$ property.shortDescription | marked $}{% endif %} {$ (property.description or property.constructorParamDoc.description) | marked $} {%- if property.see.length %} -

参见:

+

See also:

    {% for see in property.see %}
  • {$ see | marked $}
  • {% endfor %} @@ -236,7 +234,7 @@ {% set nonInternalMembers = ancestor.doc[collectionName] | filterByPropertyValue('internal', undefined) %} {% if nonInternalMembers.length -%}
    -

    继承自 {$ ancestor.doc.name $}

    +

    Inherited from {$ ancestor.doc.name $}

      {% for member in nonInternalMembers %}
    • diff --git a/aio/tools/transforms/templates/api/lib/paramList.html b/aio/tools/transforms/templates/api/lib/paramList.html index 8973e7727d..da0a9e78f9 100644 --- a/aio/tools/transforms/templates/api/lib/paramList.html +++ b/aio/tools/transforms/templates/api/lib/paramList.html @@ -27,13 +27,13 @@ {% if (parameter.shortDescription | trim) or (parameter.description | trim) %}{$ parameter.shortDescription + '\n\n' + parameter.description $} {% endif %} - {% if parameter.isOptional or parameter.defaultValue !== undefined %}
      可选. 默认值是 `{$ parameter.defaultValue === undefined and 'undefined' or parameter.defaultValue $}`.
      {% endif %} + {% if parameter.isOptional or parameter.defaultValue !== undefined %}Optional. Default is `{$ parameter.defaultValue === undefined and 'undefined' or parameter.defaultValue $}`.{% endif %} {% endmarked %}
{%- else -%} -

没有参数。

+

There are no parameters.

{%- endif -%} {%- endmacro -%} diff --git a/aio/tools/transforms/templates/api/ngmodule.template.html b/aio/tools/transforms/templates/api/ngmodule.template.html index 1bf726aad2..c124a03096 100644 --- a/aio/tools/transforms/templates/api/ngmodule.template.html +++ b/aio/tools/transforms/templates/api/ngmodule.template.html @@ -32,7 +32,7 @@

{$ itemType $}

- + {% for item in items %} @@ -43,7 +43,7 @@ @@ -59,28 +59,25 @@ {% block details %} {% block additional %}{% endblock %} {% include "includes/description.html" %} - {$ memberHelpers.renderProperties(doc.staticProperties, 'static-properties', 'static-property', '静态属性') $} - {$ memberHelpers.renderMethodDetails(versionInfo, doc.staticMethods, 'static-methods', 'static-method', '静态方法') $} + {$ memberHelpers.renderProperties(doc.staticProperties, 'static-properties', 'static-property', 'Static properties') $} + {$ memberHelpers.renderMethodDetails(versionInfo, doc.staticMethods, 'static-methods', 'static-method', 'Static methods') $} {% if doc.constructorDoc %} -

构造函数

+

Constructor

{$ memberHelpers.renderMethodDetail(versionInfo, doc.constructorDoc, 'constructor') $}{% endif %} - {$ memberHelpers.renderProperties(doc.properties, 'instance-properties', 'instance-property', '属性') $} + {$ memberHelpers.renderProperties(doc.properties, 'instance-properties', 'instance-property', 'Properties') $} - {$ memberHelpers.renderMethodDetails(versionInfo, doc.methods, 'instance-methods', 'instance-method', '方法') $} + {$ memberHelpers.renderMethodDetails(versionInfo, doc.methods, 'instance-methods', 'instance-method', 'Methods') $} {% if doc.ngmoduleOptions.providers %} - - {$ renderTable(doc.ngmoduleOptions.providers, 'providers', '提供商', '提供商') $} + {$ renderTable(doc.ngmoduleOptions.providers, 'providers', 'Providers', 'Provider') $} {% endif %} {% if doc.directives.length %} - - {$ renderExports(doc.directives, 'directive', '指令') $} + {$ renderExports(doc.directives, 'directive', 'Directives') $} {% endif %} {% if doc.pipes.length %} - - {$ renderExports(doc.pipes, 'pipe', '管道') $} + {$ renderExports(doc.pipes, 'pipe', 'Pipes') $} {% endif %} {% endblock %} diff --git a/aio/tools/transforms/templates/api/package.template.html b/aio/tools/transforms/templates/api/package.template.html index 7c432b6ad9..4d0952ac8d 100644 --- a/aio/tools/transforms/templates/api/package.template.html +++ b/aio/tools/transforms/templates/api/package.template.html @@ -1,16 +1,16 @@ {% extends 'base.template.html' -%} -{% macro listItems(items, title, originalTitle, overridePath) %} +{% macro listItems(items, title, overridePath) %} {% set filteredItems = items | filterByPropertyValue('internal', undefined) %} {% if filteredItems.length %}
-

{$ title $}

+

{$ title $}

名称说明
NameDescription
- {% if item.deprecated !== undefined %}
{$ ('**已弃用:** ' + item.deprecated) | marked $}
{% endif %} + {% if item.deprecated !== undefined %}{$ ('**Deprecated:** ' + item.deprecated) | marked $}{% endif %} {$ item.shortDescription | marked $}
{% for item in filteredItems %} @@ -24,9 +24,9 @@

{$ doc.name $}

{% if doc.isPrimaryPackage %}{% else %}{% endif %} - {% if doc.packageDeprecated or (not doc.isPrimaryPackage and doc.deprecated !== undefined) %}{% endif %} - {% if doc.security !== undefined %}{% endif %} - {% if doc.pipeOptions.pure === 'false' %}{% endif %} + {% if doc.packageDeprecated or (not doc.isPrimaryPackage and doc.deprecated !== undefined) %}{% endif %} + {% if doc.security !== undefined %}{% endif %} + {% if doc.pipeOptions.pure === 'false' %}{% endif %}
{% endblock %} @@ -37,23 +37,22 @@ {% include "includes/see-also.html" %} {% if doc.isPrimaryPackage %} -

入口点

- {$ listItems([doc.packageInfo.primary], '主要', 'primary', '#primary-entry-point-exports') $} - {$ listItems(doc.packageInfo.secondary, '次要', 'secondary') $} +

Entry points

+ {$ listItems([doc.packageInfo.primary], 'Primary', '#primary-entry-point-exports') $} + {$ listItems(doc.packageInfo.secondary, 'Secondary') $} {% endif %} - {% if doc.isPrimaryPackage %} -

主入口点的导出

- {% else %} -

入口点的导出

+

{% if doc.isPrimaryPackage %}Primary entry{% else %}Entry{% endif %} point exports

+ {% if not doc.hasPublicExports %} +

No public exports.

{% endif %} {% include "includes/deprecation.html" %} - {$ listItems(doc.ngmodules, '模块', 'ngmodules') $} - {$ listItems(doc.classes, '类', 'classes') $} - {$ listItems(doc.decorators, '装饰器', 'decorators') $} - {$ listItems(doc.functions, '函数', 'functions') $} - {$ listItems(doc.structures, '结构', 'structures') $} - {$ listItems(doc.directives, '指令', 'directives') $} - {$ listItems(doc.pipes, '管道', 'pipes') $} - {$ listItems(doc.types, '类型', 'types') $} + {$ listItems(doc.ngmodules, 'NgModules') $} + {$ listItems(doc.classes, 'Classes') $} + {$ listItems(doc.decorators, 'Decorators') $} + {$ listItems(doc.functions, 'Functions') $} + {$ listItems(doc.structures, 'Structures') $} + {$ listItems(doc.directives, 'Directives') $} + {$ listItems(doc.pipes, 'Pipes') $} + {$ listItems(doc.types, 'Types') $} {%- endblock %} diff --git a/aio/tools/transforms/templates/cli/cli-command.template.html b/aio/tools/transforms/templates/cli/cli-command.template.html index 1f971fecf8..5de2178ff9 100644 --- a/aio/tools/transforms/templates/cli/cli-command.template.html +++ b/aio/tools/transforms/templates/cli/cli-command.template.html @@ -16,7 +16,7 @@ {% if doc.longDescription.length %}

{$ github.githubLinks(doc.longDescriptionDoc, cliVersionInfo) $} - 说明 + Description

{$ doc.longDescription | marked $} {% endif%} diff --git a/aio/tools/transforms/templates/cli/cli-container.template.html b/aio/tools/transforms/templates/cli/cli-container.template.html index 57a79acd47..40162b0451 100644 --- a/aio/tools/transforms/templates/cli/cli-container.template.html +++ b/aio/tools/transforms/templates/cli/cli-container.template.html @@ -5,13 +5,13 @@ {$ doc.description | marked $} -

命令概览

+

Command Overview

{$ item.name $} - {% if item.deprecated !== undefined %}
{$ ('**已废弃:** ' + item.deprecated) | marked $}
{% endif %} + {% if item.deprecated !== undefined %}{$ ('**Deprecated:** ' + item.deprecated) | marked $}{% endif %} {% if item.shortDescription %}{$ item.shortDescription | marked $}{% endif %}
- - - + + + diff --git a/aio/tools/transforms/templates/cli/lib/cli.html b/aio/tools/transforms/templates/cli/lib/cli.html index 10aeec1a8a..99dca56305 100644 --- a/aio/tools/transforms/templates/cli/lib/cli.html +++ b/aio/tools/transforms/templates/cli/lib/cli.html @@ -9,22 +9,23 @@ {% macro renderArguments(arguments, level = 2) %} {% if arguments.length %} -参数 +Arguments
命令别名说明CommandAliasDescription
- - + + + {% for option in arguments %} - + + {% endfor %} @@ -41,33 +43,35 @@ {% macro renderNamedOptions(options, level = 2) %} {% if options.length %} -选项 +Options
参数说明ArgumentDescriptionValue Type
<{$ option.name $}>{% if option.enum.length > 0 %}={$ renderValues(option.enum) $}{% endif %}<{$ option.name $}> {$ option.description | marked $} {% if option.subcommands.length -%} -

该选项可以接受下列子命令之一:

+

This option can take one of the following sub-commands:

{%- endif %}
{% if option.enum.length > 0 %}{$ renderValues(option.enum) $}{% else %}string{% endif %}
- - - + + + + + {% for option in options %} + + {% endfor %} @@ -79,26 +83,21 @@ {% if name.length > 1 %}-{% endif %}-{$ name $} {%- endmacro %} -{%- macro renderValues(values, default) -%} -{%- set valString = values.join('|') -%} -{%- if valString.length > 15 %}
{% endif %}{$ valString $} +{%- macro renderValues(values) -%} +{$ values.join('|') $} {%- endmacro -%} -{%- macro renderOption(name, type, default, values) -%} -{% set prefix = '--' if name.length > 1 else '-' %} -{%- if type === 'boolean' and not values.length %}{$ prefix $}{$ name $}={$ renderValues([true, false], default) $} -{%- elif values.length -%} -{$ prefix $}{$ name $}={$ renderValues(values, default) $} -{%- elif type === 'string' -%} -{$ prefix $}{$ name $}={% if name.length > 15 %}
{% endif %}{$ name $} +{%- macro renderOptionValues(type, values) -%} +{%- if values.length -%} +{$ renderValues(values) $} {%- else -%} -{$ prefix $}{$ name $} +{$ type $} {%- endif -%} {%- endmacro -%} {%- macro renderSubcommands(container) -%} {% for command in container.positionalOptions %}{% if command.subcommands.length %} -

{$ command.name | title $} 命令

+

{$ command.name | title $} commands

{% for subcommand in command.subcommands %}

{$ subcommand.name $}

{% for name in container.names %} @@ -110,4 +109,4 @@ {$ renderNamedOptions(subcommand.namedOptions, 4) $} {% endfor %} {% endif %}{% endfor %} -{%- endmacro -%} +{%- endmacro -%} \ No newline at end of file diff --git a/aio/tools/transforms/templates/content.template.html b/aio/tools/transforms/templates/content.template.html index 58d0baf52d..81c975493c 100644 --- a/aio/tools/transforms/templates/content.template.html +++ b/aio/tools/transforms/templates/content.template.html @@ -3,12 +3,13 @@ {% set relativePath = doc.fileInfo.relativePath %} {% if doc.title %}{$ ('# ' + doc.title.trim()) | marked $}{% endif %} {% if '/' in relativePath or 'docs.md' in relativePath %} - + {% endif %} {% block content %}
-{$ doc.description | marked $} + {$ doc.description | marked $} + {% if doc.reviewed %}
Last reviewed on {$ doc.reviewed.date.toDateString() $}
{% endif %}
{% endblock %} \ No newline at end of file diff --git a/aio/tools/transforms/templates/lib/githubLinks.html b/aio/tools/transforms/templates/lib/githubLinks.html index 659d7cdf0a..35d575ab4e 100644 --- a/aio/tools/transforms/templates/lib/githubLinks.html +++ b/aio/tools/transforms/templates/lib/githubLinks.html @@ -3,29 +3,29 @@ {%- endmacro %} {% macro githubViewHref(doc, versionInfo) -%} -{% set githubUrl = 'https://github.com/' + versionInfo.gitRepoInfo.owner + '/' + 'angular-cn' -%} +{% set githubUrl = 'https://github.com/' + versionInfo.gitRepoInfo.owner + '/' + versionInfo.gitRepoInfo.repo -%} {% set version = versionInfo.currentVersion.isSnapshot and versionInfo.currentVersion.SHA or versionInfo.currentVersion.raw -%} {% set lineInfo = doc.startingLine and ('#L' + (doc.startingLine + 1) + '-L' + (doc.endingLine + 1)) or '' -%} {$ githubUrl $}/tree/{$ version $}/{$ projectRelativePath(doc.fileInfo) $}{$ lineInfo $} {%- endmacro %} {% macro githubEditHref(doc, versionInfo, pathPrefix) -%} -{% set githubUrl = 'https://github.com/' + versionInfo.gitRepoInfo.owner + '/' + 'angular-cn' -%} +{% set githubUrl = 'https://github.com/' + versionInfo.gitRepoInfo.owner + '/' + versionInfo.gitRepoInfo.repo -%} {% set lineInfo = doc.startingLine and ('#L' + (doc.startingLine + 1) + '-L' + (doc.endingLine + 1)) or '' -%} -{$ githubUrl $}/edit/aio/{$ projectRelativePath(doc.fileInfo) $}?message=docs +{$ githubUrl $}/edit/master/{$ projectRelativePath(doc.fileInfo) $}?message=docs {%- if doc.moduleDoc %}({$ doc.moduleDoc.id.split('/')[0] $}) {%- elseif doc.docType === 'module' %}({$ doc.id.split('/')[0] $}) {%- elseif doc.docType === 'content' %} {%- else %}(...){%- endif -%} -%3A%20请简述你的修改...{$ lineInfo $} +%3A%20describe%20your%20change...{$ lineInfo $} {%- endmacro %} {% macro githubEditLink(doc, versionInfo) -%} - + {%- endmacro %} {% macro githubViewLink(doc, versionInfo) -%} - + {%- endmacro %} {% macro githubLinks(doc, versionInfo) -%} diff --git a/aio/tools/transforms/templates/overview-dump.template.html b/aio/tools/transforms/templates/overview-dump.template.html index 2caf5468dc..41b383efd4 100644 --- a/aio/tools/transforms/templates/overview-dump.template.html +++ b/aio/tools/transforms/templates/overview-dump.template.html @@ -1,9 +1,9 @@ {% import "lib/githubLinks.html" as github -%} {% import "api/lib/memberHelpers.html" as members -%} -{% macro goToCode(doc) %}{% endmacro %} +{% macro goToCode(doc) %}{% endmacro %} {% macro label(test, class, text) %}{% if test %}{% endif %}{% endmacro %} {% macro renderLabels(doc) -%} - {$ label(doc.notYetDocumented, 'no-doc', '尚无文档') $} + {$ label(doc.notYetDocumented, 'no-doc', 'UNDOCUMENTED') $} {%- for tag in doc.tags.tags %}{$ label(tag.tagDef.deprecated, 'deprecated', '@' + tag.tagDef.name + ' deprecated') $}{% endfor %} {% endmacro %} {% macro renderMember(member) -%} @@ -79,7 +79,7 @@ -

文档状态报告

+

Documentation Status Report

{% for module in doc.modules %}
diff --git a/aio/tools/transforms/templates/sitemap.template.xml b/aio/tools/transforms/templates/sitemap.template.xml index 1405850157..009dd5d66d 100644 --- a/aio/tools/transforms/templates/sitemap.template.xml +++ b/aio/tools/transforms/templates/sitemap.template.xml @@ -2,6 +2,6 @@ {%- for url in doc.urls %} - https://angular.cn/{$ url $} + https://angular.io/{$ url $} {% endfor %} - + \ No newline at end of file diff --git a/aio/tools/translator/bin/add-spaces-all.ts b/aio/tools/translator/bin/add-spaces-all.ts deleted file mode 100644 index 1f0e517298..0000000000 --- a/aio/tools/translator/bin/add-spaces-all.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as fs from 'fs'; -import { dirs } from '../dirs'; -import { listMarkdownFiles } from '../extractor'; - -export function addSpaces(sourceDir: string): void { - const files = listMarkdownFiles(sourceDir); - files.forEach(fileName => { - console.log('adding spaces ...', fileName); - const content = fs.readFileSync(fileName, 'utf8'); - const result = content - .replace(/([\u4e00-\u9fa5])([a-zA-Z0-9`])/g, '$1 $2') - .replace(/([a-zA-Z0-9`])([\u4e00-\u9fa5])/g, '$1 $2') - .replace(/([*-]+)(\w+)([*-]+)([\u4e00-\u9fa5])/g, '$1$2$3 $4') - .replace(/([\u4e00-\u9fa5])([*-]+)(\w+)([*-]+)/g, '$1 $2$3$4') - ; - fs.writeFileSync(fileName, result, 'utf8'); - console.log('added spaces ', fileName); - }); -} - -addSpaces(dirs.content); diff --git a/aio/tools/translator/bin/extract-all.ts b/aio/tools/translator/bin/extract-all.ts deleted file mode 100644 index 1117d2d18e..0000000000 --- a/aio/tools/translator/bin/extract-all.ts +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env ts-node -import { dirs } from '../dirs'; -import { gatherFromDirectory } from '../extractor'; - -gatherFromDirectory(dirs.content, dirs.here + 'dict-latest.json'); diff --git a/aio/tools/translator/bin/prerender.ts b/aio/tools/translator/bin/prerender.ts deleted file mode 100644 index 92be225362..0000000000 --- a/aio/tools/translator/bin/prerender.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { writeFileSync } from 'fs'; -import * as globby from 'globby'; -import { minify } from 'html-minifier'; -import { chunk, uniq } from 'lodash'; -import { sync as mkdirp } from 'mkdirp'; -import { dirname, join } from 'path'; -import { Browser, launch, Request } from 'puppeteer'; - -const minifyOptions = { - collapseWhitespace: true, - ignoreCustomFragments: [/[\s\S]*?<\/code>/], - minifyCSS: true, - minifyJS: true, - removeComments: true, - removeScriptTypeAttributes: true, - removeStyleLinkTypeAttributes: true, -}; - -function getAllUrls(): string[] { - return globby.sync('./dist/generated/docs/**/*.json') - .map(file => file.replace(/^.*generated\/docs\/(.*).json$/, '$1')); -} - -const urls = [...getAllUrls(), 'index.html']; - -async function filterResource(request: Request) { - const type = request.resourceType(); - if (['image', 'stylesheet', 'font'].indexOf(type) !== -1) { - return request.abort(); - } else { - return request.continue(); - } -} - -async function renderPage(browser: Browser, url: string) { - const page = await browser.newPage(); - await page.setRequestInterception(true); - page.on('request', filterResource); - await page.goto(`http://localhost:4200/${url}`, { waitUntil: 'domcontentloaded' }); - await page.waitForSelector('aio-doc-viewer>div'); - const content = await page.content(); - const filename = join('dist', `${url}.html`); - mkdirp(dirname(filename)); - writeFileSync(filename, minify(content, minifyOptions), 'utf-8'); - await page.close(); - console.log(`rendered ${url}.`); -} - -async function renderPageGroup(browser: Browser, urls: string[]) { - await Promise.all(urls.map(url => renderPage(browser, url))); -} - -async function prerender() { - const browser = await launch({ defaultViewport: { width: 1280, height: 768 } }); - const groups = chunk(uniq(urls), 4); - for (let i = 0; i < groups.length; ++i) { - await renderPageGroup(browser, groups[i]); - } - await browser.close(); -} - -prerender().then(() => { - process.exit(0); -}).catch((e) => { - console.error(e); - process.exit(-1); -}); diff --git a/aio/tools/translator/bin/translate-all.ts b/aio/tools/translator/bin/translate-all.ts deleted file mode 100644 index 3436397cab..0000000000 --- a/aio/tools/translator/bin/translate-all.ts +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env ts-node -import { dirs } from '../dirs'; -import { translateDirectory } from '../translate'; - -translateDirectory(__dirname + '/../../../../../content-en/', dirs.content); diff --git a/aio/tools/translator/bin/translate-one.ts b/aio/tools/translator/bin/translate-one.ts deleted file mode 100644 index 31edeef06c..0000000000 --- a/aio/tools/translator/bin/translate-one.ts +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env ts-node - -import { dirs } from '../dirs'; -import * as path from 'path'; -import { translateFile } from '../translate'; - -const filename = path.join(dirs.content, process.argv[2]); -translateFile(filename, filename); diff --git a/aio/tools/translator/checker.spec.ts b/aio/tools/translator/checker.spec.ts deleted file mode 100644 index f8f4a29ea2..0000000000 --- a/aio/tools/translator/checker.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { expect } from 'chai'; -import { dirs } from './dirs'; -import { gatherFromMarkdownFiles } from './extractor'; -import { - isHead, - isNotCheatSheet, - isNotCnPages, - isNotImg, - isNotMarketingDocs, - originalIsNotChinese, - originalIsNotSpecialDivTag, - translationHasNotCodeExample, -} from './utils'; - -describe('自动检查翻译结果', function () { - const entries = gatherFromMarkdownFiles(dirs.content) - .filter(isNotCheatSheet) - .filter(isNotMarketingDocs) - .filter(isNotCnPages); - - it('译文里不应该出现 ', function () { - const codeExamples = entries.filter(translationHasNotCodeExample); - expect(codeExamples).eql([]); - }); - - it('原文中不应该有汉语', function () { - const lines = entries.filter(originalIsNotChinese) - .filter(isNotImg); - expect(lines).eql([]); - }); - - it('原文和译文应该有相同的标题等级', function () { - const lines = entries - .filter(entry => isHead(entry.original) && isHead(entry.translation)) - .filter(entry => { - const originalLevel = entry.original.replace(/^(#+).*$/, '$1').length; - const translationLevel = entry.translation.replace(/^(#+).*$/, '$1').length; - return originalLevel !== translationLevel; - }); - - expect(lines).eql([]); - }); - - it('原文不应该是以
{ - it('空字符串应该拆分成空数组', function () { - expect(splitAndTrim()).eql([]); - }); - it('按照双行(忽略空格)拆分对照文本', function () { - const result = splitAndTrim(`a - - - - b - c - - d`); - expect(result[1]).eql(`b - c`); - }); - - it('生成原文和译文的对照表', () => { - const result = gatherTranslations(` - a - - 一 - - b - `); - expect(result).eql([{original: 'a', translation: '一'}]); - }); - - it('处理 html 标签包裹的翻译文本', () => { - const result = gatherTranslations(` -

- a -

- -

- 一 -

- - `); - expect(result).eql([{original: 'a', translation: '一'}]); - - }); - - it('处理 a 标签包裹的单行翻译文本', () => { - const result = gatherTranslations(` - a - - - - `); - expect(result).eql([{original: 'a', translation: ''}]); - - }); - - it('从真实的文件中采集(测试)', function () { - const fs = require('fs'); - const content = fs.readFileSync(dirs.content + 'guide/forms.md', 'utf-8'); - const result = gatherTranslations(content); - expect(result[0]).eql({original: '# Forms', translation: '# 表单'}); - }); - - it('递归查找所有 markdown 文件', function () { - expect(listMarkdownFiles(dirs.content).length).greaterThan(10); - }); -}); diff --git a/aio/tools/translator/extractor.ts b/aio/tools/translator/extractor.ts deleted file mode 100644 index 80a41b9772..0000000000 --- a/aio/tools/translator/extractor.ts +++ /dev/null @@ -1,106 +0,0 @@ -import * as globby from 'globby'; -import { DictEntry } from './dict-entry'; -import { - extractOriginalContent, - isNotCnPages, - isOnlyBeginTag, hasInlineText, kernelText, - normalizeLines, - originalIsNotChinese, - originalIsNotCodeExampleTag, - originalIsNotOnlyBeginTag, - originalIsNotPureCloseTag, - originalIsNotSpecialDivTag, -} from './utils'; - -export function splitAndTrim(text = ''): string[] { - return text.split(/\n+\s*\n+/).map(line => line.trim()).filter(line => !!line); -} - -// tslint:disable:max-line-length -const pattern = /[\u2E80-\u2EFF\u2F00-\u2FDF\u3000-\u303F\u31C0-\u31EF\u3200-\u32FF\u3300-\u33FF\u3400-\u4DBF\u4DC0-\u4DFF\u4E00-\u9FBF\uF900-\uFAFF\uFE30-\uFE4F\uFF00-\uFFEF]/; - -export function isTranslation(text) { - return text && pattern.test(text); -} - -export function gatherTranslations(text: string): DictEntry[] { - const lines = splitAndTrim(normalizeLines(text)); - - const result: any[] = []; - for (let i = 1; i < lines.length; ++i) { - const translation = purifyText(lines[i]); - if (hasInlineText(translation)) { - const originalContent = extractOriginalContent(translation); - result.push({ - key: kernelText(originalContent), - original: originalContent, - translation - }); - } else if (isTranslation(translation)) { - const original = purifyText(lines[i - 1]); - // 对于包裹在 html tag 中的翻译文本进行特殊处理 - if (isOnlyBeginTag(original)) { - const prevBeginTag = lines[i - 4].trim(); - const prevEndTag = lines[i - 2].trim(); - const thisEndTag = lines[i + 1].trim(); - if (original === prevBeginTag && prevEndTag === thisEndTag) { - result.push({ - key: kernelText(lines[i - 3]), - original: lines[i - 3], - translation: lines[i], - }); - } - } else { - result.push({key: kernelText(original), original, translation}); - } - } - } - return result - .filter(isNotCnPages) - .filter(originalIsNotChinese) - .filter(originalIsNotSpecialDivTag) - .filter(originalIsNotCodeExampleTag) - .filter(originalIsNotPureCloseTag) - .filter(originalIsNotOnlyBeginTag) - .map(purifyEntry); -} - -export function listMarkdownFiles(directory: string): string[] { - return globby.sync(directory + '**/*.md'); -} - -export function gatherFromMarkdownFile(fileName: string): DictEntry[] { - const fs = require('fs'); - const content = fs.readFileSync(fileName, 'utf-8'); - const entries = gatherTranslations(content); - entries.forEach(entry => entry.sourceFile = fileName); - return entries; -} - -export function gatherFromMarkdownFiles(directory: string): DictEntry[] { - const files = listMarkdownFiles(directory); - const entries = files.map(gatherFromMarkdownFile); - return entries.reduce((result, value) => result.concat(value), []); -} - -export function purifyText(text): string { - return text - .replace(/^(.*) { - it('查字典', () => { - expect(lookup('# Forms')[0].translation).eql('# 表单'); - }); - - it('翻译 header', () => { - expect(translate(`
`)).eql(``); - }); -}); diff --git a/aio/tools/translator/translate.ts b/aio/tools/translator/translate.ts deleted file mode 100644 index 163a0e33b3..0000000000 --- a/aio/tools/translator/translate.ts +++ /dev/null @@ -1,63 +0,0 @@ -import * as fs from 'fs'; -import * as _ from 'lodash'; -import { DictEntry } from './dict-entry'; -import { dirs } from './dirs'; -import { listMarkdownFiles } from './extractor'; -import { exactlyTest, extractOriginalContent, indentOf, hasInlineText, kernelText, normalizeLines, repeat } from './utils'; - -// TODO: 改用 markdown 解析器实现 - -export const dict = require('./dict-latest.json') as DictEntry[]; - -export function lookup(english: string): DictEntry[] { - const englishKernel = kernelText(extractOriginalContent(english)); - const entries = dict - .filter(entry => exactlyTest(entry.key, englishKernel)); - return _.uniqBy(entries, 'translation'); -} - -function isPureTag(line: string): boolean { - let content = line.trim(); - return /^<\/?\w+\b[^>]*>$/.test(content) || - /^<(\w+)\b[^>]*>\s*<\/\1>$/.test(content); -} - -export function translate(content: string): string[] { - const lines = normalizeLines(content) - .split(/\n{2,}/); - return lines - .map(line => { - if (!line.trim() || isPureTag(line)) { - return line; - } - const translations = lookup(line); - if (translations.length > 0 && hasInlineText(translations[0].translation)) { - return translations[0].translation; - } else { - const indent = indentOf(line); - const padding = repeat(indent); - if (translations.length === 0) { - return line; - } else if (translations.length === 1) { - return line + '\n\n' + padding + translations[0].translation; - } else { - return line + '\n\n' + padding + translations[translations.length - 1].translation; - } - } - }); -} - -export function translateFile(sourceFile: string, targetFile: string): void { - const content = fs.readFileSync(sourceFile, 'utf-8'); - const result = translate(content); - fs.writeFileSync(targetFile, result.join('\n\n') + '\n', 'utf-8'); -} - -export function translateDirectory(sourceDir: string, targetDir: string): void { - const files = listMarkdownFiles(sourceDir); - files.forEach(fileName => { - console.log('translating ...', fileName); - translateFile(fileName, fileName.replace(/^.*content-en\//, dirs.content)); - console.log('translated ', fileName); - }); -} diff --git a/aio/tools/translator/tsconfig.json b/aio/tools/translator/tsconfig.json deleted file mode 100644 index f5b6194e92..0000000000 --- a/aio/tools/translator/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "compileOnSave": true, - "compilerOptions": { - "strict": true, - "noImplicitAny": false, - "outDir": "dist", - "baseUrl": "src", - "module": "commonjs", - "sourceMap": true, - "declaration": true, - "moduleResolution": "node", - "noUnusedLocals": true, - "target": "es5", - "typeRoots": [ - "node_modules/@types" - ], - "types": [ - "node" - ], - "lib": [ - "es2017", - "dom" - ] - }, - "exclude": [ - "**/*.spec.ts" - ] -} diff --git a/aio/tools/translator/utils.spec.ts b/aio/tools/translator/utils.spec.ts deleted file mode 100644 index 2d814df923..0000000000 --- a/aio/tools/translator/utils.spec.ts +++ /dev/null @@ -1,370 +0,0 @@ -import { expect } from 'chai'; -import { fuzzyTest, hasInlineText, kernelText, normalizeLines, tokenize } from './utils'; - -describe(' 工具函数', () => { - it('把“1. ”列表处理成空行分隔的格式,以便处理', function () { - const lines = normalizeLines(`1. abc -11. def -`); - expect(lines).eql(`1. abc - -11. def`); - }); - - it('把“- ”列表处理成空行分隔的格式,以便处理', function () { - const lines = normalizeLines(`- abc -- def -`); - expect(lines).eql(`- abc - -- def`); - }); - - it('把“* ”列表处理成空行分隔的格式,以便处理', function () { - const lines = normalizeLines(`* abc -* def -`); - expect(lines).eql(`* abc - -* def`); - }); - - it('把“# ”标题处理成空行分隔的格式,以便处理', function () { - const lines = normalizeLines(`\n# abc -def`); - expect(lines).eql(`# abc - -def`); - }); - - it('拆解单行的成对 tag', function () { - const lines = normalizeLines(` - a -
DEF
- b -`); - expect(lines).eql(`a - -
DEF
- - b`); - }); - - it('拆解单行的自封闭 tag', function () { - const lines = normalizeLines(` - a -
- b -`); - expect(lines).eql(`a - -
- - b`); - }); - it('拆解单行的 h\\d 标签', function () { - const lines = normalizeLines(` - a -

line

- b -`); - expect(lines).eql(`a - -

line

- - b`); - }); - - it('把多行 hn 处理成单行', function () { - const lines = normalizeLines(` - -`); - expect(lines).eql(``); - }); - - it('拆解单行的 th 标签', function () { - const lines = normalizeLines(` - a -
- b -`); - expect(lines).eql(`a - - - - b`); - }); - - it('拆解单行注释', function () { - const lines = normalizeLines(` - a - - b -`); - expect(lines).eql(`a - - - - b`); - }); - - it('拆解多行注释', function () { - const lines = normalizeLines(` - a - - b -`); - expect(lines).eql(`a - - - - b`); - }); - - it('拆解单行br', function () { - const lines = normalizeLines(` - a -
- b -`); - expect(lines).eql(`a - -
- - b`); - }); - - it('拆解 code-example', function () { - const lines = normalizeLines(` -abc - -ng generate directive highlight - -def -`); - expect(lines).eql(`abc - - -ng generate directive highlight - - -def`); - }); - - it('不拆解引用的 code-example', function () { - const lines = normalizeLines(` -abc - -> abc - -def -`); - expect(lines).eql(`abc - -> abc - -def`); - }); - - it('为单行的 li 和 ul 前后添加空行', function () { - const lines = normalizeLines(` - a -
  • - b -
  • - c -
      - -
    - `); - expect(normalizeLines(lines)).eql(`a - -
  • - - b - -
  • - - c - -
      - -
    `); - }); - it('拆解 @a 标记', function () { - const lines = normalizeLines(` - a - {@a test} - b -`); - expect(lines).eql(`a - - {@a test} - - b`); - }); - - it('拆解多行代码', function () { - const lines = normalizeLines(` - a - \`\`\` - var a = 1 - \`\`\` - b -`); - expect(lines).eql(`a - - \`\`\` - - var a = 1 - - \`\`\` - - b`); - }); - - it('把多行的 p 元素合并成单行', function () { - const lines = normalizeLines(` -

    - a -

    -

    - 一 -

    - -`); - expect(lines).eq(`

    a

    - -

    `); - }); - - it('不要拆解 header', function () { - const lines = normalizeLines(` -
    Angular forms don't require a style library
    -`); - expect(lines).eq(`
    Angular forms don't require a style library
    `); - }); - - it('拆解独行的 th/td', function () { - expect(normalizeLines(` -
    -`)).eql(``); - }); - - it('拆解 pre', function () { - expect(normalizeLines(` - ABC -
    def
    - ghi -`)).eql(`ABC - -
    def
    - - ghi`); - }); - - it('拆解任意位置的
    ', function () { - expect(normalizeLines(` - -`)).eql(` - - - - `); - }); - it('拆解独行的 li', function () { - expect(normalizeLines(` - - -`)).eql(`
      - -
    • - - a - -
    • - -
    • - - b - -
    • - -
    • - - c - -
    • - -
    `); - }); - it('不要拆解行内的 html tag', function () { - expect(normalizeLines(` -a c - -`)).eql(`a c`); - }); - it('把连续的三行及以上空行简化为两个空行', function () { - const lines = normalizeLines(` - a - - - b`); - expect(lines).eql(`a - - b`); - }); - - it('拆分', function () { - expect(tokenize('abc def,abc.')).eql(['abc', 'def', 'abc']); - }); - - it('抽取核心字符', function () { - expect(kernelText(' # Forms ABC. ')).eql('#FORMSABC'); - }); - - it('删除非核心字符', function () { - expect(kernelText('Abc-132-@#!abc')).eql('ABC132#ABC'); - }); - - it('模糊匹配', function () { - expect(fuzzyTest(`a b c d e`, `a b c d e`)).is.false; - expect(fuzzyTest(`a b c d e f g`, `a b c d e`)).is.false; - expect(fuzzyTest(`Make that easy by encapsulating the _click-triggering_ process in a helper such as the \`click\` function below:`, - `Make that consistent and easy by encapsulating the _click-triggering_ process -in a helper such as the \`click()\` function below: -`)).is.true; - }); - - it('检测是否表格', function () { - expect(hasInlineText(` -abc | def -----|--- -gh | ij -`)).eql(true); - }); -}); diff --git a/aio/tools/translator/utils.ts b/aio/tools/translator/utils.ts deleted file mode 100644 index b252db6e71..0000000000 --- a/aio/tools/translator/utils.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { DictEntry } from './dict-entry'; -import { isTranslation } from './extractor'; -import * as _ from 'lodash'; - -export function translationHasNotCodeExample(entry: DictEntry): boolean { - return entry.translation.indexOf('Back to top\s+<\/a>/g; - text = text.replace(specialBackToTopPattern, 'Back to top'); - // 原文中有'); - // 原文中有的换行会干扰生成 html 的格式,替换一下 - // tslint:disable:max-line-length - text = text.replace(` -Get them now if they're not already installed on your machine. -`, `Get them now if they're not already installed on your machine. -`); - - // 为各种列表多加一个空行 - const listElementPattern = /(?=\n *(\d+\.|-|\*) )\n/g; - text = text.replace(listElementPattern, '\n\n'); - // 为标题增加空行 - const hxPattern = /^( *#+ .*)$/gm; - text = text.replace(hxPattern, '\n$1\n'); - // 把多行的 HTML 标题或 p 元素变成单行 - const hxMultilinePattern = /^( *)<(h\d|p|header)([^>]*)>\s*(.*)\s*<\/\2>$/gm; - text = text.replace(hxMultilinePattern, '\n$1<$2$3>$4\n'); - // 为单行的成对标签前后添加空行 - const oneLinePairedTagPattern = /^( *)<(p|div|h\d+|code-example|section)\b([^>]*)>([^\n]*?)<\/\2>( *)$/gm; - text = text.replace(oneLinePairedTagPattern, '\n$1<$2$3>$4$5\n'); - // 为单行的注释前后添加空行 - const oneLineCommentPattern = /^( * *)$/gm; - text = text.replace(oneLineCommentPattern, '\n$1\n'); - // 为单行的 back to top 前后添加空行 - const backToTopPattern = /^( *Back to top<\/a> *)$/gm; - text = text.replace(backToTopPattern, '\n$1\n'); - // 为单行的 {@ 语句前后添加空行 - const atTagCommentPattern = /^( *{@a.*} *)$/gm; - text = text.replace(atTagCommentPattern, '\n$1\n'); - // 为单行的自封闭标签前后添加空行 - const oneLineClosedTagPattern = /^( *
    ) *$/gm; - text = text.replace(oneLineClosedTagPattern, '\n$1\n'); - // 为单行的
    前后添加空行 - const oneLineBrTagPattern = /^( *
    *)$/gm; - text = text.replace(oneLineBrTagPattern, '\n$1\n'); - // 为单独的 div 前后添加空行 - const oneLineDivTagPattern = /^( *<\/?(div|li|ul|ol)\b([^>]*)> *)$/gm; - text = text.replace(oneLineDivTagPattern, '\n$1\n'); - // 在 pre 前后添加空行 - const preBeginTagPattern = /(^ * *)$/gm; - text = text.replace(preEndTagPattern, '$1\n'); - - // 为 ``` 前后添加空行 - const multiLineCodePattern = /^( *```\w* *)$/gm; - text = text.replace(multiLineCodePattern, '\n$1\n'); - - // 把单行的 tr 拆成多行,以便翻译 - const trTagPattern = /^( *)(]*>)(.*)(<\/tr>)$/gm; - text = text.replace(trTagPattern, '\n$1$2\n\n$1 $3\n\n$1$4\n'); - - // 把单行的 th/td/li 等拆成多行,以便翻译, - const oneLineThTdTagPattern = /^( *)<(th|td|li)\b([^>]*)>(.*?)<\/\2>$/gm; - text = text.replace(oneLineThTdTagPattern, '\n$1<$2$3>\n\n$1 $4\n\n$1\n'); - - // 把原本就是多行的 th/td 中间添加空行 - const thTdTagPattern = /^( *)<(th|td)\b( *[^>]*)>([\s\S]*?)<\/\2>$/gm; - text = text.replace(thTdTagPattern, '\n\n$1<$2$3>\n\n$1 $4\n\n$1\n\n'); - - // 在所有的起始标签前面加空行 - const blockBeginTagPattern = /^( *)<(code-example|code-tabs|pre|p)\b( *[^>]*)>( *)$/gm; - text = text.replace(blockBeginTagPattern, '\n$1<$2$3>$4'); - - // 在所有的结束标签前面加空行 - const blockEndTagPattern = /^( *)<\/(code-example|code-tabs|pre|p)>( *)$/gm; - text = text.replace(blockEndTagPattern, '$1$3\n'); - - // 把所有由空格组成的空行都去掉 - const blankLinePattern = /^[ \t]+$/gm; - text = text.replace(blankLinePattern, ''); - // 把中间的多个回车都变成两个回车 - const multipleBlankLinePattern = /\n{2,}/g; - text = text.replace(multipleBlankLinePattern, '\n\n'); - - // 去掉全文头尾的空白 - text = text.trim(); - return text; -} - -export function indentOf(line): number { - let pattern = /^( *)[\s\S]*/; - if (!pattern.test(line)) { - return 0; - } - const leadSpaces = line.replace(pattern, '$1').length; - if (/^ *(\d+\.|-|\*) /.test(line)) { - return leadSpaces + 3; - } else { - return leadSpaces; - } -} - -export function repeat(indent: number): string { - let result = ''; - for (let i = 0; i < indent; ++i) { - result = result + ' '; - } - return result; -} - -// 目前还不能正常工作 -export function fuzzyTest(text1: string, text2: string): boolean { - const tokens1 = tokenize(text1); - const tokens2 = tokenize(text2); - const sameTokens = _.intersection(tokens1, tokens2); - const maxTokens = Math.max(tokens1.length, tokens2.length); - return sameTokens.length > 5 && sameTokens.length / maxTokens >= 0.8; -} - -export function exactlyTest(key: string, text: string): boolean { - return !!key && key === text; -} - -export function kernelText(text: string): string { - return text - .replace(/([^a-zA-Z0-9#:]|\s|\.$)/g, '') - .toUpperCase() - .trim(); -} - -export function tokenize(text: string): string[] { - return text.split(/\W/) - .map(token => token.trim()) - .filter(token => !!token); -} - -export function hasInlineText(text: string): boolean { - return /(.*?)<\/t> *.*?<\/t>/g.test(text); -} - -export function extractOriginalContent(text: string): string { - if (!hasInlineText(text)) { - return text; - } - return text.replace(/(.*?)<\/t> *.*?<\/t>/gi, '$1') - .replace(/ +/g, ' '); -} diff --git a/aio/tsconfig.json b/aio/tsconfig.json index c4edb3ba2e..fc3d847571 100644 --- a/aio/tsconfig.json +++ b/aio/tsconfig.json @@ -36,6 +36,7 @@ "tools" ], "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, "disableTypeScriptVersionCheck": true, "strictInjectionParameters": true, "strictInputAccessModifiers": true, diff --git a/aio/tslint.json b/aio/tslint.json index 26e5eaf064..1fd7816557 100644 --- a/aio/tslint.json +++ b/aio/tslint.json @@ -61,8 +61,7 @@ true, "ignore-params" ], - // TODO(gkalpak): Fix the code and enable this to align with CLI. (Failures: 59) - // "no-non-null-assertion": true, + "no-non-null-assertion": true, "no-redundant-jsdoc": true, "no-switch-case-fall-through": true, "no-var-requires": false, diff --git a/aio/yarn.lock b/aio/yarn.lock index 8de7730341..8885ad74bb 100644 --- a/aio/yarn.lock +++ b/aio/yarn.lock @@ -2,113 +2,116 @@ # yarn lockfile v1 -"@angular-devkit/architect@0.1100.1": - version "0.1100.1" - resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1100.1.tgz#0451fd261a1afc69af2e41f80f4002b93035f548" - integrity sha512-DIAvTRRY+k7T2xHf4RVV06P16D0V7wSf1MSpGSDWVpfWcA3HNOSGfsk1F+COMlbFehXuKztwieXarv5rXbBCng== +"@angular-devkit/architect@0.1102.2": + version "0.1102.2" + resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1102.2.tgz#3b3eb654ae7c8c204b248bba76982ce8de2f7b6c" + integrity sha512-FE7DeT13elqDlELF23QqvEFnT2BkxeC5t31/QW85IN/OR5Tf/q7XEpj7giJXyzKFQ60M3ZzbznZyRz0EqtfaBQ== dependencies: - "@angular-devkit/core" "11.0.1" + "@angular-devkit/core" "11.2.2" rxjs "6.6.3" -"@angular-devkit/build-angular@0.1100.1": - version "0.1100.1" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1100.1.tgz#194d9600093798766164d3a5952af239a9709ed5" - integrity sha512-w8NcoXuruOHio0D/JbX47iDl9FVH8X9k/OlZ/rSNVQ3uEpV6uxIaTm3fZ1ZSrKffi+97rKEwpHOf2N0DXl4XGQ== +"@angular-devkit/build-angular@0.1102.2": + version "0.1102.2" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1102.2.tgz#c850818fd8bb4dd4fda6288390868475c4b3236e" + integrity sha512-AjnvHrzkYTzDGzp0r5RmGoP9fyZXtaVFo0598PRusi1oWp1sW6B5FKPWw896iREOlotRXw3dsjqrGwbMcz0qyg== dependencies: - "@angular-devkit/architect" "0.1100.1" - "@angular-devkit/build-optimizer" "0.1100.1" - "@angular-devkit/build-webpack" "0.1100.1" - "@angular-devkit/core" "11.0.1" - "@babel/core" "7.12.3" - "@babel/generator" "7.12.1" - "@babel/plugin-transform-runtime" "7.12.1" - "@babel/preset-env" "7.12.1" - "@babel/runtime" "7.12.1" - "@babel/template" "7.10.4" + "@angular-devkit/architect" "0.1102.2" + "@angular-devkit/build-optimizer" "0.1102.2" + "@angular-devkit/build-webpack" "0.1102.2" + "@angular-devkit/core" "11.2.2" + "@babel/core" "7.12.10" + "@babel/generator" "7.12.11" + "@babel/plugin-transform-async-to-generator" "7.12.1" + "@babel/plugin-transform-runtime" "7.12.10" + "@babel/preset-env" "7.12.11" + "@babel/runtime" "7.12.5" + "@babel/template" "7.12.7" "@jsdevtools/coverage-istanbul-loader" "3.0.5" - "@ngtools/webpack" "11.0.1" + "@ngtools/webpack" "11.2.2" ansi-colors "4.1.1" - autoprefixer "9.8.6" - babel-loader "8.1.0" + autoprefixer "10.2.4" + babel-loader "8.2.2" browserslist "^4.9.1" cacache "15.0.5" caniuse-lite "^1.0.30001032" - circular-dependency-plugin "5.2.0" - copy-webpack-plugin "6.2.1" - core-js "3.6.5" - css-loader "5.0.0" + circular-dependency-plugin "5.2.2" + copy-webpack-plugin "6.3.2" + core-js "3.8.3" + critters "0.0.7" + css-loader "5.0.1" cssnano "4.1.10" - file-loader "6.1.1" + file-loader "6.2.0" find-cache-dir "3.3.1" glob "7.1.6" + https-proxy-agent "5.0.0" inquirer "7.3.3" - jest-worker "26.5.0" + jest-worker "26.6.2" karma-source-map-support "1.4.0" - less "3.12.2" - less-loader "7.0.2" - license-webpack-plugin "2.3.1" + less "4.1.1" + less-loader "7.3.0" + license-webpack-plugin "2.3.11" loader-utils "2.0.0" - mini-css-extract-plugin "1.2.1" + mini-css-extract-plugin "1.3.5" minimatch "3.0.4" - open "7.3.0" - ora "5.1.0" + open "7.4.0" + ora "5.3.0" parse5-html-rewriting-stream "6.0.1" pnp-webpack-plugin "1.6.4" - postcss "7.0.32" - postcss-import "12.0.1" - postcss-loader "4.0.4" + postcss "8.2.4" + postcss-import "14.0.0" + postcss-loader "4.2.0" raw-loader "4.0.2" regenerator-runtime "0.13.7" resolve-url-loader "3.1.2" rimraf "3.0.2" - rollup "2.32.1" + rollup "2.38.4" rxjs "6.6.3" - sass "1.27.0" - sass-loader "10.0.5" - semver "7.3.2" + sass "1.32.6" + sass-loader "10.1.1" + semver "7.3.4" source-map "0.7.3" - source-map-loader "1.1.2" + source-map-loader "1.1.3" source-map-support "0.5.19" - speed-measure-webpack-plugin "1.3.3" + speed-measure-webpack-plugin "1.4.2" style-loader "2.0.0" stylus "0.54.8" - stylus-loader "4.1.1" - terser "5.3.7" + stylus-loader "4.3.3" + terser "5.5.1" terser-webpack-plugin "4.2.3" text-table "0.2.0" tree-kill "1.2.2" webpack "4.44.2" webpack-dev-middleware "3.7.2" - webpack-dev-server "3.11.0" - webpack-merge "5.2.0" - webpack-sources "2.0.1" - webpack-subresource-integrity "1.5.1" + webpack-dev-server "3.11.2" + webpack-merge "5.7.3" + webpack-sources "2.2.0" + webpack-subresource-integrity "1.5.2" worker-plugin "5.0.0" -"@angular-devkit/build-optimizer@0.1100.1": - version "0.1100.1" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1100.1.tgz#a51ecf54e3098035d9a13ecc48fa5cadf49b79a6" - integrity sha512-PpqBmDd+/cmaMj9MURe5pSSudo+Qz6BrGdzvYB16ekSp8bSDYLUriv5NvE/bm+ODKwo3jHgFrwWLiwK65vQcxQ== +"@angular-devkit/build-optimizer@0.1102.2": + version "0.1102.2" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1102.2.tgz#a306fee0bc648983405320953f05ad1fc60b6b84" + integrity sha512-TCWWqAe+pWZzLp/g2gG8Z5NC8JSgDNfyEuMBWxEUfo1Sm3BluXoz0BbmnietuhXJZ+fPAp9rLLzEGZlHvOlmOA== dependencies: loader-utils "2.0.0" source-map "0.7.3" - tslib "2.0.3" - typescript "4.0.5" - webpack-sources "2.0.1" + tslib "2.1.0" + typescript "4.1.3" + webpack-sources "2.2.0" -"@angular-devkit/build-webpack@0.1100.1": - version "0.1100.1" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1100.1.tgz#e80990ecd44dd5e30f6508c136ec1e02c20452ae" - integrity sha512-nfgsUfP6WyZ35rxgjqDYydB552Si/JdYLMtwy/kAFybW/6yTpw0sBOgCQoppyQ4mvVwyX9X0ZTQsMNhPOzy3sA== +"@angular-devkit/build-webpack@0.1102.2": + version "0.1102.2" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1102.2.tgz#f48501426a5d01b0610dafce33b4eb84d07181e6" + integrity sha512-59CBbwbdN8lI5/whuNeAZHRJxPlOmDc5ux8aJJNwWI9w54fz0ut/MLT3iuPk+WZuKlGdpS1sGkObfZwWen5kIQ== dependencies: - "@angular-devkit/architect" "0.1100.1" - "@angular-devkit/core" "11.0.1" + "@angular-devkit/architect" "0.1102.2" + "@angular-devkit/core" "11.2.2" rxjs "6.6.3" -"@angular-devkit/core@11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-11.0.1.tgz#7125b07ac7d66a8fdaaf612a3b757817d23b8107" - integrity sha512-ui3g7w/0SpU9oq8uwN9upR8Y1eOXZ+P2p3NyDydBrR7ZEfEkRLS1mhozN/ib8farrwK5N3kIIJxMb5t3187Hng== +"@angular-devkit/core@11.2.2": + version "11.2.2" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-11.2.2.tgz#c6b40f941b24d2af447831fc958b744316cd7d87" + integrity sha512-LUDO1AdIjereiMh0j5p9xJcdr9ifhbWCPxlZqfu5wHzUfhCx9gO2Lvjp6rZXQ3OedXg5IZUnyxHlzkszQOsgiw== dependencies: ajv "6.12.6" fast-json-stable-stringify "2.1.0" @@ -116,68 +119,70 @@ rxjs "6.6.3" source-map "0.7.3" -"@angular-devkit/schematics@11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-11.0.1.tgz#d0826cde52a2c015b24a7c6f3b4df31665eb67ed" - integrity sha512-rAOnAndcybEH398xf5wzmcUPCoCi0dKiOo/+1dkKU5aTxynw1OUnANt5K6A+ZZTGnJmfjtP0ovkZGYun9IUDxQ== +"@angular-devkit/schematics@11.2.2": + version "11.2.2" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-11.2.2.tgz#0c8c4b98a30f00649dcbb7794d3783b9a067209f" + integrity sha512-6bIxMwafz/+lwdtcshwOuFfhxTMU4RLma1uxBS34DXupMauPGl0IIXAy5cK9dXPlHLxuGsjeBiOM6eq033RLgw== dependencies: - "@angular-devkit/core" "11.0.1" - ora "5.1.0" + "@angular-devkit/core" "11.2.2" + ora "5.3.0" rxjs "6.6.3" -"@angular/animations@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-11.0.0.tgz#6f567930dca8eb8ab1320f1f48feb981493b86c6" - integrity sha512-RGaAnZOI73bPnNWrJq/p8sc+hpUBhScq139M6r4qQjQPsPahazL6v6hHAgRhZNemqw164d1oE4K/22O/i0E3Tw== +"@angular/animations@11.2.3": + version "11.2.3" + resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-11.2.3.tgz#518183e5f7b8c3b304020ea86d12cc3216142cc9" + integrity sha512-Z6sHIeTeeZrRAW83NI7FO7THF50cPCFkkuvVah3qmCqopY6FuoHKUBEENyGzQGH69LbGFYhEppY8KM/6JtVF6Q== dependencies: tslib "^2.0.0" -"@angular/cdk@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-11.0.0.tgz#7ec0fc8b5051c54f4a3910e61f1ee7d30796c2d2" - integrity sha512-kfgEE/LDYMZB4NICChzWn/nTEeoZ3wxrYsDDcy2Qj+57zUmJcxBLL1h+tawYCy3a1g7ypDLYX7yrbPEBE7/EXQ== +"@angular/cdk@11.2.2": + version "11.2.2" + resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-11.2.2.tgz#f541069db3f5705d8c064138f6cd94568fe1b658" + integrity sha512-p3lRDPlnOuJtLWEd020QOyn0ERyc1LF7OLi90hTdzMMxe9fT3v6sQJVRs8jIY3NTmpIm/pNDGi77+1/vKerLPQ== dependencies: tslib "^2.0.0" optionalDependencies: parse5 "^5.0.0" -"@angular/cli@11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-11.0.1.tgz#70417fb910d7e19a71b5b0926c3aeb51af3f7a5d" - integrity sha512-zB20jTLQxLpkJjhbYelhRyMcgGsjwbD8pSYYAO6QX656Tx1tCtJ2UskEtf4ePQvgzu0Ds2dnAEfco+tekIMRTA== +"@angular/cli@11.2.2": + version "11.2.2" + resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-11.2.2.tgz#ca56894f1a4d1f4e411408b8185b711614c3195a" + integrity sha512-rOVBzDzrMuOgJY43O46/7yYbncx0egGfr+DMJDQdazePGH1H3INN/eA9gkVcVK53ztCYb9X1sbZKOs9TUhF6nw== dependencies: - "@angular-devkit/architect" "0.1100.1" - "@angular-devkit/core" "11.0.1" - "@angular-devkit/schematics" "11.0.1" - "@schematics/angular" "11.0.1" - "@schematics/update" "0.1100.1" + "@angular-devkit/architect" "0.1102.2" + "@angular-devkit/core" "11.2.2" + "@angular-devkit/schematics" "11.2.2" + "@schematics/angular" "11.2.2" + "@schematics/update" "0.1102.2" "@yarnpkg/lockfile" "1.1.0" ansi-colors "4.1.1" - debug "4.2.0" - ini "1.3.5" + debug "4.3.1" + ini "2.0.0" inquirer "7.3.3" + jsonc-parser "3.0.0" npm-package-arg "8.1.0" npm-pick-manifest "6.1.0" - open "7.3.0" - pacote "9.5.12" - resolve "1.18.1" + open "7.4.0" + ora "5.3.0" + pacote "11.2.4" + resolve "1.19.0" rimraf "3.0.2" - semver "7.3.2" - symbol-observable "2.0.3" + semver "7.3.4" + symbol-observable "3.0.0" universal-analytics "0.4.23" - uuid "8.3.1" + uuid "8.3.2" -"@angular/common@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@angular/common/-/common-11.0.0.tgz#cc2a14b36c56f6c4d93427c2f8c17f55e4b464c9" - integrity sha512-chlbtxR7jpPs3Rc1ymdp3UfUzqEr57OFIxVMG6hROODclPQQk/7oOHdQB4hpUObaF9y4ZTLeKHKWiR/twi21Pg== +"@angular/common@11.2.3": + version "11.2.3" + resolved "https://registry.yarnpkg.com/@angular/common/-/common-11.2.3.tgz#e71d645fb6bdef9463f23a551cc072ef276c1d84" + integrity sha512-51gVmr942SZtAFmhVfp7/3fcTQ+Tia7UxWjv6iUtYF3oCvTWbo/J1zki2VNSfmMNKJV8MaMq6XUw8UWbHA0sgQ== dependencies: tslib "^2.0.0" -"@angular/compiler-cli@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-11.0.0.tgz#ff4c2c16284a31a4f8ff1d224f593f64a1458234" - integrity sha512-zrd/cU9syZ8XuQ3ItfIGaKDn1ZBCWyiqdLVRH9VDmyNqQFiCc/VWQ9Th9z8qpLptgdpzE9+lKFgeZJTDtbcveQ== +"@angular/compiler-cli@11.2.3": + version "11.2.3" + resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-11.2.3.tgz#5307215b9aa6e32d772906fd3b2960ba03a7565d" + integrity sha512-ObQVI6q2c0VTWbsDnWJDdUZv2Jz/u1jiQNcrdtu/rjtJARaldEno9dMakN838Q6Nw4FzKUO6uYZXmnvKCUjfxQ== dependencies: "@babel/core" "^7.8.6" "@babel/types" "^7.8.6" @@ -193,12 +198,12 @@ source-map "^0.6.1" sourcemap-codec "^1.4.8" tslib "^2.0.0" - yargs "15.3.0" + yargs "^16.1.1" -"@angular/compiler@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-11.0.0.tgz#b49997d0130e7c8cfe84fa73e5610892f4a772af" - integrity sha512-I7wVhdqvhtBTQTtW61z0lwPb1LiQQ0NOwjsbfN5sAc7/uwxw7em+Kyb/XJgBwgaTKtAL8bZEzdoQGLdsSKQF2g== +"@angular/compiler@11.2.3": + version "11.2.3" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-11.2.3.tgz#72427d57b992bf6840fb7268357a466095caf8eb" + integrity sha512-De8BwtSwPVYGdvQa6CDq2C1SLmB78YjS0t/KNlvfp85cl4Gb3BdjTDsKMkJXkm/3ubnIXi1BaRIsFNVTCCF70Q== dependencies: tslib "^2.0.0" @@ -207,10 +212,10 @@ resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-9.0.0.tgz#87e0bef4c369b6cadae07e3a4295778fc93799d5" integrity sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ== -"@angular/core@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@angular/core/-/core-11.0.0.tgz#cdb89f3877f6e5487a0e5f18d234447ec41e8184" - integrity sha512-FNewyMwYy+kGdw1xWfrtaPD2cSQs3kDVFbl8mNMSzp933W5yMsHDvjXb0+nPFqEb8ywEIdm3MsBMK0y3iBWZQw== +"@angular/core@11.2.3": + version "11.2.3" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-11.2.3.tgz#7dd59f35e0b2410543a61be6048c474c18a43f40" + integrity sha512-+G7rZj21Mcmf6nWjQ79EwomwEOVQ1WLqw6YvCXWzgJ9ZlVjLi/Sti0/jIzUpgK0E0Fn86yuXw/vgYq5kjGeOcQ== dependencies: tslib "^2.0.0" @@ -219,52 +224,52 @@ resolved "https://registry.yarnpkg.com/@angular/core/-/core-9.0.0.tgz#227dc53e1ac81824f998c6e76000b7efc522641e" integrity sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w== -"@angular/elements@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@angular/elements/-/elements-11.0.0.tgz#67a7f4a8b51a060005eb3c7a3e55d04d3afc79c2" - integrity sha512-Xe1azn17DJos7NfqaUzVu8NNy8mixHTuNQS4dAssBHP3EtgpPwQXI9oZlcPjFRCn4+pPNvhFaWxifyxDlZhfpg== +"@angular/elements@11.2.3": + version "11.2.3" + resolved "https://registry.yarnpkg.com/@angular/elements/-/elements-11.2.3.tgz#9ea9ab194a961b8d1074eabbff3102927e86e10e" + integrity sha512-cfEZSzpm++0kgbEWomWx+tFwlRp4HcVi+RpJtkRWZSuWTIy5qZZg9yBzgDzrOrhXH9iXUlJ2+4ujmdPe7wKJ9w== dependencies: tslib "^2.0.0" -"@angular/forms@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-11.0.0.tgz#fd9e167024e92df17ff98714ccae322ac4fbc1ab" - integrity sha512-hP6GF1ZkxKQp7Y+EVbEe9PPDQPrUQNdfVxphCWQYwu3tm8+tn1r91KVXkp2MA3M4Fh6Xo2HQEU2d+VXv4w0iNQ== +"@angular/forms@11.2.3": + version "11.2.3" + resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-11.2.3.tgz#57460a110e6601b50362f878fc0f67701c76dc24" + integrity sha512-VfyKV8IxHTclcHQmt5gjGFmKC1kGz7sdNLYsEM+M0y88Bsufh3VIhK4kspfO4nhJxVfh6HFOt1JVQ5bvo6PDlQ== dependencies: tslib "^2.0.0" -"@angular/material@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@angular/material/-/material-11.0.0.tgz#6ef7048763e7415dabda54d32847b2821bf6413d" - integrity sha512-pTwYmBrRXbEzF5J/oayZF0ApA0tLN+CUl/2MaYFNLzvE/Kn6hIdDb7TonWAEBgeSHIzqzyTV8IUQuXwGaPds9A== +"@angular/material@11.2.2": + version "11.2.2" + resolved "https://registry.yarnpkg.com/@angular/material/-/material-11.2.2.tgz#718f97ab82a2da2b549aa773416e6a4ac46089ec" + integrity sha512-mPCGmWlDJ1HvHWDcvzOqoOYepW/8S1JHi+RD4jG7hn77+cD78BR+Ze8bQgJWADHvrtbElTmjB1ExB+gB+7gP8g== dependencies: tslib "^2.0.0" -"@angular/platform-browser-dynamic@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.0.0.tgz#630d77a0c853bcc2c80c30dfe6c101d6c7fe4ac1" - integrity sha512-NAmKGhHK+tl7dr/Hcqxvr/813Opec3Mv0IRwIgmKdlpZd7qAwT/mw4RnO4YPSEoDOM6hqGt7GdlWrSDX802duQ== +"@angular/platform-browser-dynamic@11.2.3": + version "11.2.3" + resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.2.3.tgz#3d7eb15ba4bcc9e227f68f13bf20258fa16efad1" + integrity sha512-QUPCvack7De6u5AqWcW8O6FzczwqoL858R1NlnqojnNbcnN/dCtXtKvvETEEgp/9VMwLfcuLd1BWdBJSah7f6A== dependencies: tslib "^2.0.0" -"@angular/platform-browser@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-11.0.0.tgz#314a0362e63ac7eef80adebfc5fbe4e7f2aa2a73" - integrity sha512-p8sF6JfaBI+YyLpp5OSg6UcCqjtLKRR+Otq1P/tro5SuxrsrBNRVU8j0tl/crkScsMwAvgmJ1joRyUKdI2mUGQ== +"@angular/platform-browser@11.2.3": + version "11.2.3" + resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-11.2.3.tgz#0c6b537500a1c6304829fab19cf8c12daa2b48b9" + integrity sha512-S0IP/kGinIH18+gfnX0gLFLbP0Euw1RBceDt/WipYhUeFZZryQHvot/6KFLFtO+8rVunfrg+UyBiaK65/TT9Og== dependencies: tslib "^2.0.0" -"@angular/router@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@angular/router/-/router-11.0.0.tgz#59e855b0d34c4578e0556e181f2f28048fb0d5a8" - integrity sha512-10ZeobfK3HqVeWS6zjdKU16ccxFtdCHkxT11bnFg3Jwq9vKt+LI5KitAkCI5rYTY3DRfVzasRkqBzZfZMkbftw== +"@angular/router@11.2.3": + version "11.2.3" + resolved "https://registry.yarnpkg.com/@angular/router/-/router-11.2.3.tgz#407a0797845c1cac963663537b30872e39e4b229" + integrity sha512-lRuEIlNj2BcBZ17mt5SZY7v80PsvlS4J6EbKSOFeSYhALM/AQnaaCdrrMlQ1WyEa5bBUabxGT9/zvahBosy2yA== dependencies: tslib "^2.0.0" -"@angular/service-worker@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-11.0.0.tgz#16818850529856f5a6812634b10294e99b54585e" - integrity sha512-elGlO2CxYZs0/p9I/gj4c7IPh+P4bpvMiF1BVMXU1Mj09Obvn0hHk9thhekdfKcYOQbvdZhn0wmrbYyxDifAqw== +"@angular/service-worker@11.2.3": + version "11.2.3" + resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-11.2.3.tgz#316bfc07ccebdc5af1a9cbc825082880c551c0b9" + integrity sha512-/JgA4rCH2SyIK/v0+sCqNgiBEV/pXQUcUoqfm//2zfc3VwerehvF3RtRBfabtLBpdwdO5a9DZ4nX+djvTJypvw== dependencies: tslib "^2.0.0" @@ -277,43 +282,6 @@ call-me-maybe "^1.0.1" js-yaml "^3.13.1" -"@awesome-fe/translate@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@awesome-fe/translate/-/translate-1.0.0.tgz#4bf7ea96ff12256c9f4d3b2824f2813a395fa991" - integrity sha512-wt7Zmmz8cnQqdw0RWzvKAFGtG42IbfdC15/xUSq6lQJsJE+i/nzGwTrelS378RAN6ZbiZNw9FUFcERuwcG5ixA== - dependencies: - "@google-cloud/translate" "^5.3.0" - "@types/js-yaml" "^3.12.5" - "@vitalets/google-translate-api" "^4.0.0" - better-sqlite3 "^7.1.2" - common-dir "^3.0.0" - core-js "^3.8.1" - github-slugger "^1.3.0" - globby "^8.0.2" - js-yaml "^3.14.0" - lodash "^4.17.20" - mkdirp "^1.0.4" - parse5 "^6.0.1" - reflect-metadata "^0.1.13" - rehype-parse "^6.0.2" - rehype-remark "^5.0.2" - rehype-stringify "^5.0.0" - remark-frontmatter "^1.3.3" - remark-html "^9.0.1" - remark-parse "^6.0.3" - remark-stringify "^6.0.4" - request "^2.88.2" - request-promise-native "^1.0.9" - string-width "^3.1.0" - ts-morph "^9.1.0" - typeorm "^0.2.29" - unified "^7.1.0" - unist-util-flatmap "^1.0.0" - unist-util-remove "^1.0.3" - unist-util-visit "^1.4.1" - uuid "^3.4.0" - yargs "^12.0.5" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" @@ -328,30 +296,36 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/compat-data@^7.12.1", "@babel/compat-data@^7.12.5": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.12.5.tgz#f56db0c4bb1bbbf221b4e81345aab4141e7cb0e9" - integrity sha512-DTsS7cxrsH3by8nqQSpFSyjSfSYl57D6Cf4q8dW3LK83tBKBDCkfcay1nYkXq1nIHXnpX8WMMb/O25HOy3h1zg== +"@babel/code-frame@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" + integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== + dependencies: + "@babel/highlight" "^7.12.13" -"@babel/core@7.12.3", "@babel/core@^7.8.6": - version "7.12.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz#1b436884e1e3bff6fb1328dc02b208759de92ad8" - integrity sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g== +"@babel/compat-data@^7.12.7", "@babel/compat-data@^7.13.0": + version "7.13.6" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.6.tgz#11972d07db4c2317afdbf41d6feb3a730301ef4e" + integrity sha512-VhgqKOWYVm7lQXlvbJnWOzwfAQATd2nV52koT0HZ/LdDH0m4DUDwkKYsH+IwpXb+bKPyBJzawA4I6nBKqZcpQw== + +"@babel/core@7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.10.tgz#b79a2e1b9f70ed3d84bbfb6d8c4ef825f606bccd" + integrity sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w== dependencies: "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.12.1" + "@babel/generator" "^7.12.10" "@babel/helper-module-transforms" "^7.12.1" - "@babel/helpers" "^7.12.1" - "@babel/parser" "^7.12.3" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.12.1" - "@babel/types" "^7.12.1" + "@babel/helpers" "^7.12.5" + "@babel/parser" "^7.12.10" + "@babel/template" "^7.12.7" + "@babel/traverse" "^7.12.10" + "@babel/types" "^7.12.10" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.1" json5 "^2.1.2" lodash "^4.17.19" - resolve "^1.3.2" semver "^5.4.1" source-map "^0.5.0" @@ -376,12 +350,34 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.1.tgz#0d70be32bdaa03d7c51c8597dda76e0df1f15468" - integrity sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg== +"@babel/core@^7.8.6": + version "7.12.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz#1b436884e1e3bff6fb1328dc02b208759de92ad8" + integrity sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g== dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.1" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.1" + "@babel/parser" "^7.12.3" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.1" "@babel/types" "^7.12.1" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.11.tgz#98a7df7b8c358c9a37ab07a24056853016aba3af" + integrity sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA== + dependencies: + "@babel/types" "^7.12.11" jsesc "^2.5.1" source-map "^0.5.0" @@ -403,6 +399,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.12.10", "@babel/generator@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.0.tgz#bd00d4394ca22f220390c56a0b5b85568ec1ec0c" + integrity sha512-zBZfgvBB/ywjx0Rgc2+BwoH/3H+lDtlgD4hBOpEv5LxRnYsm/753iRuLepqnYlynpjC3AdQxtxsoeHJoEEwOAw== + dependencies: + "@babel/types" "^7.13.0" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/generator@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.4.tgz#35bbc74486956fe4251829f9f6c48330e8d0985e" @@ -435,15 +440,15 @@ "@babel/helper-explode-assignable-expression" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/helper-compilation-targets@^7.12.1": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz#cb470c76198db6a24e9dbc8987275631e5d29831" - integrity sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw== +"@babel/helper-compilation-targets@^7.12.5": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.0.tgz#c9cf29b82a76fd637f0faa35544c4ace60a155a1" + integrity sha512-SOWD0JK9+MMIhTQiUVd4ng8f3NXhPVQvTv7D3UN4wbp/6cAHnB2EmMaU1zZA2Hh1gwme+THBrVSqTFxHczTh0Q== dependencies: - "@babel/compat-data" "^7.12.5" - "@babel/helper-validator-option" "^7.12.1" + "@babel/compat-data" "^7.13.0" + "@babel/helper-validator-option" "^7.12.17" browserslist "^4.14.5" - semver "^5.5.0" + semver "7.0.0" "@babel/helper-create-class-features-plugin@^7.12.1": version "7.12.1" @@ -507,6 +512,15 @@ "@babel/template" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-function-name@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz#93ad656db3c3c2232559fd7b2c3dbdcbe0eb377a" + integrity sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA== + dependencies: + "@babel/helper-get-function-arity" "^7.12.13" + "@babel/template" "^7.12.13" + "@babel/types" "^7.12.13" + "@babel/helper-function-name@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca" @@ -523,6 +537,13 @@ dependencies: "@babel/types" "^7.10.4" +"@babel/helper-get-function-arity@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" + integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg== + dependencies: + "@babel/types" "^7.12.13" + "@babel/helper-get-function-arity@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" @@ -551,6 +572,13 @@ dependencies: "@babel/types" "^7.12.5" +"@babel/helper-module-imports@^7.12.5": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz#ec67e4404f41750463e455cc3203f6a32e93fcb0" + integrity sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g== + dependencies: + "@babel/types" "^7.12.13" + "@babel/helper-module-transforms@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz#7954fec71f5b32c48e4b303b437c34453fd7247c" @@ -583,6 +611,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== +"@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" + integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== + "@babel/helper-regex@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0" @@ -637,6 +670,13 @@ dependencies: "@babel/types" "^7.11.0" +"@babel/helper-split-export-declaration@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05" + integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== + dependencies: + "@babel/types" "^7.12.13" + "@babel/helper-split-export-declaration@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" @@ -649,15 +689,20 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== +"@babel/helper-validator-identifier@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== + "@babel/helper-validator-identifier@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== -"@babel/helper-validator-option@^7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz#175567380c3e77d60ff98a54bb015fe78f2178d9" - integrity sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A== +"@babel/helper-validator-option@^7.12.11", "@babel/helper-validator-option@^7.12.17": + version "7.12.17" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" + integrity sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw== "@babel/helper-wrap-function@^7.10.4": version "7.10.4" @@ -678,6 +723,15 @@ "@babel/traverse" "^7.12.5" "@babel/types" "^7.12.5" +"@babel/helpers@^7.12.5": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.13.0.tgz#7647ae57377b4f0408bf4f8a7af01c42e41badc0" + integrity sha512-aan1MeFPxFacZeSz6Ld7YZo5aPuqnKlD7+HZY75xQsueczFccP9A7V05+oe0XpLwHK3oLorPe9eaAUljL7WEaQ== + dependencies: + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.0" + "@babel/helpers@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73" @@ -696,6 +750,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.12.13.tgz#8ab538393e00370b26271b01fa08f7f27f2e795c" + integrity sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/highlight@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" @@ -710,6 +773,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== +"@babel/parser@^7.12.10", "@babel/parser@^7.12.13", "@babel/parser@^7.12.7", "@babel/parser@^7.13.0": + version "7.13.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.4.tgz#340211b0da94a351a6f10e63671fa727333d13ab" + integrity sha512-uvoOulWHhI+0+1f9L4BoozY7U5cIkZ9PgJqvb041d6vypgUmtVPG4vmGm4pSggjl8BELzvHyUeJSUyEMY6b+qA== + "@babel/parser@^7.12.3", "@babel/parser@^7.12.5": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.5.tgz#b4af32ddd473c0bfa643bd7ff0728b8e71b81ea0" @@ -777,12 +845,12 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" -"@babel/plugin-proposal-numeric-separator@^7.12.1": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.5.tgz#b1ce757156d40ed79d59d467cb2b154a5c4149ba" - integrity sha512-UiAnkKuOrCyjZ3sYNHlRlfuZJbBHknMQ9VMwVeX97Ofwx7RpD6gS2HfqTCh8KNUQgcOm8IKt103oR4KIjh7Q8g== +"@babel/plugin-proposal-numeric-separator@^7.12.7": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz#bd9da3188e787b5120b4f9d465a8261ce67ed1db" + integrity sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-numeric-separator" "^7.10.4" "@babel/plugin-proposal-object-rest-spread@^7.12.1": @@ -802,12 +870,12 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" -"@babel/plugin-proposal-optional-chaining@^7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.1.tgz#cce122203fc8a32794296fc377c6dedaf4363797" - integrity sha512-c2uRpY6WzaVDzynVY9liyykS+kVU+WRZPMPYpkelXH8KBt1oXoI89kPbZKKG/jDT5UK92FTW2fZkZaJhdiBabw== +"@babel/plugin-proposal-optional-chaining@^7.12.7": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.0.tgz#75b41ce0d883d19e8fe635fc3f846be3b1664f4d" + integrity sha512-OVRQOZEBP2luZrvEbNSX5FfWDousthhdEoAOpej+Tpe58HFLvqRClT89RauIvBuCDFEip7GW1eT86/5lMy2RNA== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.13.0" "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" "@babel/plugin-syntax-optional-chaining" "^7.8.0" @@ -926,7 +994,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-async-to-generator@^7.12.1": +"@babel/plugin-transform-async-to-generator@7.12.1", "@babel/plugin-transform-async-to-generator@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz#3849a49cc2a22e9743cbd6b52926d30337229af1" integrity sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A== @@ -942,12 +1010,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-block-scoping@^7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz#f0ee727874b42a208a48a586b84c3d222c2bbef1" - integrity sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w== +"@babel/plugin-transform-block-scoping@^7.12.11": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.13.tgz#f36e55076d06f41dfd78557ea039c1b581642e61" + integrity sha512-Pxwe0iqWJX4fOOM2kEZeUuAxHMWb9nK+9oh5d11bsLoB0xMg+mkDpt0eYuDZB7ETrY9bbcVlKUGTOGWy7BHsMQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-transform-classes@^7.12.1": version "7.12.1" @@ -1125,14 +1193,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-runtime@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.1.tgz#04b792057eb460389ff6a4198e377614ea1e7ba5" - integrity sha512-Ac/H6G9FEIkS2tXsZjL4RAdS3L3WHxci0usAnz7laPWUmFiGtj7tIASChqKZMHTSQTQY6xDbOq+V1/vIq3QrWg== +"@babel/plugin-transform-runtime@7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.10.tgz#af0fded4e846c4b37078e8e5d06deac6cd848562" + integrity sha512-xOrUfzPxw7+WDm9igMgQCbO3cJKymX7dFdsgRr1eu9n3KjjyU4pptIXbXPseQDquw+W+RuJEJMHKHNsPNNm3CA== dependencies: - "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-module-imports" "^7.12.5" "@babel/helper-plugin-utils" "^7.10.4" - resolve "^1.8.1" semver "^5.5.1" "@babel/plugin-transform-shorthand-properties@^7.12.1": @@ -1150,13 +1217,12 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" -"@babel/plugin-transform-sticky-regex@^7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.1.tgz#5c24cf50de396d30e99afc8d1c700e8bce0f5caf" - integrity sha512-CiUgKQ3AGVk7kveIaPEET1jNDhZZEl1RPMWdTBE1799bdz++SwqDHStmxfCtDfBhQgCl38YRiSnrMuUMZIWSUQ== +"@babel/plugin-transform-sticky-regex@^7.12.7": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz#760ffd936face73f860ae646fb86ee82f3d06d1f" + integrity sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-regex" "^7.10.4" + "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-transform-template-literals@^7.12.1": version "7.12.1" @@ -1165,12 +1231,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-typeof-symbol@^7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.1.tgz#9ca6be343d42512fbc2e68236a82ae64bc7af78a" - integrity sha512-EPGgpGy+O5Kg5pJFNDKuxt9RdmTgj5sgrus2XVeMp/ZIbOESadgILUbm50SNpghOh3/6yrbsH+NB5+WJTmsA7Q== +"@babel/plugin-transform-typeof-symbol@^7.12.10": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz#785dd67a1f2ea579d9c2be722de8c84cb85f5a7f" + integrity sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-transform-unicode-escapes@^7.12.1": version "7.12.1" @@ -1187,16 +1253,16 @@ "@babel/helper-create-regexp-features-plugin" "^7.12.1" "@babel/helper-plugin-utils" "^7.10.4" -"@babel/preset-env@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.12.1.tgz#9c7e5ca82a19efc865384bb4989148d2ee5d7ac2" - integrity sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg== +"@babel/preset-env@7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.12.11.tgz#55d5f7981487365c93dbbc84507b1c7215e857f9" + integrity sha512-j8Tb+KKIXKYlDBQyIOy4BLxzv1NUOwlHfZ74rvW+Z0Gp4/cI2IMDPBWAgWceGcE7aep9oL/0K9mlzlMGxA8yNw== dependencies: - "@babel/compat-data" "^7.12.1" - "@babel/helper-compilation-targets" "^7.12.1" - "@babel/helper-module-imports" "^7.12.1" + "@babel/compat-data" "^7.12.7" + "@babel/helper-compilation-targets" "^7.12.5" + "@babel/helper-module-imports" "^7.12.5" "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-validator-option" "^7.12.1" + "@babel/helper-validator-option" "^7.12.11" "@babel/plugin-proposal-async-generator-functions" "^7.12.1" "@babel/plugin-proposal-class-properties" "^7.12.1" "@babel/plugin-proposal-dynamic-import" "^7.12.1" @@ -1204,10 +1270,10 @@ "@babel/plugin-proposal-json-strings" "^7.12.1" "@babel/plugin-proposal-logical-assignment-operators" "^7.12.1" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.12.1" - "@babel/plugin-proposal-numeric-separator" "^7.12.1" + "@babel/plugin-proposal-numeric-separator" "^7.12.7" "@babel/plugin-proposal-object-rest-spread" "^7.12.1" "@babel/plugin-proposal-optional-catch-binding" "^7.12.1" - "@babel/plugin-proposal-optional-chaining" "^7.12.1" + "@babel/plugin-proposal-optional-chaining" "^7.12.7" "@babel/plugin-proposal-private-methods" "^7.12.1" "@babel/plugin-proposal-unicode-property-regex" "^7.12.1" "@babel/plugin-syntax-async-generators" "^7.8.0" @@ -1225,7 +1291,7 @@ "@babel/plugin-transform-arrow-functions" "^7.12.1" "@babel/plugin-transform-async-to-generator" "^7.12.1" "@babel/plugin-transform-block-scoped-functions" "^7.12.1" - "@babel/plugin-transform-block-scoping" "^7.12.1" + "@babel/plugin-transform-block-scoping" "^7.12.11" "@babel/plugin-transform-classes" "^7.12.1" "@babel/plugin-transform-computed-properties" "^7.12.1" "@babel/plugin-transform-destructuring" "^7.12.1" @@ -1249,14 +1315,14 @@ "@babel/plugin-transform-reserved-words" "^7.12.1" "@babel/plugin-transform-shorthand-properties" "^7.12.1" "@babel/plugin-transform-spread" "^7.12.1" - "@babel/plugin-transform-sticky-regex" "^7.12.1" + "@babel/plugin-transform-sticky-regex" "^7.12.7" "@babel/plugin-transform-template-literals" "^7.12.1" - "@babel/plugin-transform-typeof-symbol" "^7.12.1" + "@babel/plugin-transform-typeof-symbol" "^7.12.10" "@babel/plugin-transform-unicode-escapes" "^7.12.1" "@babel/plugin-transform-unicode-regex" "^7.12.1" "@babel/preset-modules" "^0.1.3" - "@babel/types" "^7.12.1" - core-js-compat "^3.6.2" + "@babel/types" "^7.12.11" + core-js-compat "^3.8.0" semver "^5.5.0" "@babel/preset-modules@^0.1.3": @@ -1278,10 +1344,10 @@ core-js-pure "^3.0.0" regenerator-runtime "^0.13.2" -"@babel/runtime@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.1.tgz#b4116a6b6711d010b2dad3b7b6e43bf1b9954740" - integrity sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA== +"@babel/runtime@7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" + integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== dependencies: regenerator-runtime "^0.13.4" @@ -1292,7 +1358,16 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/template@7.10.4", "@babel/template@^7.10.4": +"@babel/template@7.12.7": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.7.tgz#c817233696018e39fbb6c491d2fb684e05ed43bc" + integrity sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.12.7" + "@babel/types" "^7.12.7" + +"@babel/template@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== @@ -1301,6 +1376,15 @@ "@babel/parser" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/template@^7.12.13", "@babel/template@^7.12.7": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" + integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/parser" "^7.12.13" + "@babel/types" "^7.12.13" + "@babel/template@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8" @@ -1340,6 +1424,21 @@ globals "^11.1.0" lodash "^4.17.19" +"@babel/traverse@^7.12.10", "@babel/traverse@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.0.tgz#6d95752475f86ee7ded06536de309a65fc8966cc" + integrity sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.13.0" + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/parser" "^7.13.0" + "@babel/types" "^7.13.0" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + "@babel/traverse@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.4.tgz#f0845822365f9d5b0e312ed3959d3f827f869e3c" @@ -1373,6 +1472,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.12.10", "@babel/types@^7.12.11", "@babel/types@^7.12.13", "@babel/types@^7.12.7", "@babel/types@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80" + integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@babel/types@^7.4.4": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7" @@ -1391,6 +1499,11 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@bazel/bazelisk@^1.7.5": + version "1.7.5" + resolved "https://registry.yarnpkg.com/@bazel/bazelisk/-/bazelisk-1.7.5.tgz#dd1a52e3d23464f72de55aa3dc4777847fa85373" + integrity sha512-JHwP9JhfZUSoj4sku471Bjw4uE773U2Agujnx0CdPkeRk25khy1l3VyjaPaHB+z1fmMnM6ED3M7tetQUsovUQg== + "@dabh/diagnostics@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31" @@ -1400,29 +1513,6 @@ enabled "2.0.x" kuler "^2.0.0" -"@dsherret/to-absolute-glob@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@dsherret/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1f6475dc8bd974cea07a2daf3864b317b1dd332c" - integrity sha1-H2R13IvZdM6gei2vOGSzF7HdMyw= - dependencies: - is-absolute "^1.0.0" - is-negated-glob "^1.0.0" - -"@google-cloud/common@^2.0.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@google-cloud/common/-/common-2.4.0.tgz#2783b7de8435024a31453510f2dab5a6a91a4c82" - integrity sha512-zWFjBS35eI9leAHhjfeOYlK5Plcuj/77EzstnrJIZbKgF/nkqjcQuGiMCpzCwOfPyUbz8ZaEOYgbHa759AKbjg== - dependencies: - "@google-cloud/projectify" "^1.0.0" - "@google-cloud/promisify" "^1.0.0" - arrify "^2.0.0" - duplexify "^3.6.0" - ent "^2.2.0" - extend "^3.0.2" - google-auth-library "^5.5.0" - retry-request "^4.0.0" - teeny-request "^6.0.0" - "@google-cloud/paginator@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-2.0.3.tgz#c7987ad05d1c3ebcef554381be80e9e8da4e4882" @@ -1467,20 +1557,6 @@ p-defer "^3.0.0" protobufjs "^6.8.1" -"@google-cloud/translate@^5.3.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@google-cloud/translate/-/translate-5.3.0.tgz#478c9918a78b5dec066dd6fd0429adc8a86516fb" - integrity sha512-bQmPe7LYiPNM7F6m0IzoDxG1FqQJdjq5PboOpXt+OBOUR7UKNmAfn8nMVOdKNZTlA7nQdQwX/YKd2wQT2h1d9g== - dependencies: - "@google-cloud/common" "^2.0.0" - "@google-cloud/promisify" "^1.0.0" - arrify "^2.0.0" - extend "^3.0.2" - google-gax "^1.11.1" - is "^3.2.1" - is-html "^2.0.0" - protobufjs "^6.8.8" - "@grpc/grpc-js@^0.6.12": version "0.6.16" resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-0.6.16.tgz#74dda9eb3deba9f3d8741e91b21faa1d8405fdd3" @@ -1532,14 +1608,27 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@ngtools/webpack@11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-11.0.1.tgz#029aaff16d76fbfdf06422dde4c83f3b79c34215" - integrity sha512-z62qQ4J5LhDxW68HjYYCRo+sDK/5yHwX4fCCY2iXngyTtA5cQbGI5WXr3+9B4foX64ft5WvV0WJkx8mjE/VR6w== +"@napi-rs/triples@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@napi-rs/triples/-/triples-1.0.2.tgz#2ce4c6a78568358772008f564ee5009093d20a19" + integrity sha512-EL3SiX43m9poFSnhDx4d4fn9SSaqyO2rHsCNhETi9bWPmjXK3uPJ0QpPFtx39FEdHcz1vJmsiW41kqc0AgvtzQ== + +"@ngtools/webpack@11.2.2": + version "11.2.2" + resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-11.2.2.tgz#647862ed19761796c7f84d5fb3305661d2a3af67" + integrity sha512-X1M/Xs0kLi9FrOIU6yJ74q3pCzhgwPQowO1XjJ68KLOoMbj/DM6Qm0Hi9N0Ay8h0s7BIdjKEu/C3pCdGu1Q54w== dependencies: - "@angular-devkit/core" "11.0.1" - enhanced-resolve "5.3.1" - webpack-sources "2.0.1" + "@angular-devkit/core" "11.2.2" + enhanced-resolve "5.7.0" + webpack-sources "2.2.0" + +"@node-rs/helper@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@node-rs/helper/-/helper-1.1.0.tgz#4fcbbebae3b81932d1ff3e431c7cd3886b504742" + integrity sha512-r43YnnrY5JNzDuXJdW3sBJrKzvejvFmFWbiItUEoBJsaPzOIWFMhXB7i5j4c9EMXcFfxveF4l7hT+rLmwtjrVQ== + dependencies: + "@napi-rs/triples" "^1.0.2" + tslib "^2.1.0" "@nodelib/fs.scandir@2.1.3": version "2.1.3" @@ -1567,6 +1656,34 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" +"@npmcli/ci-detect@^1.0.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@npmcli/ci-detect/-/ci-detect-1.3.0.tgz#6c1d2c625fb6ef1b9dea85ad0a5afcbef85ef22a" + integrity sha512-oN3y7FAROHhrAt7Rr7PnTSwrHrZVRTS2ZbyxeQwSSYD0ifwM3YNgQqbaRmjcWoPyq77MjchusjJDspbzMmip1Q== + +"@npmcli/git@^2.0.1": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-2.0.6.tgz#47b97e96b2eede3f38379262fa3bdfa6eae57bf2" + integrity sha512-a1MnTfeRPBaKbFY07fd+6HugY1WAkKJzdiJvlRub/9o5xz2F/JtPacZZapx5zRJUQFIzSL677vmTSxEcDMrDbg== + dependencies: + "@npmcli/promise-spawn" "^1.1.0" + lru-cache "^6.0.0" + mkdirp "^1.0.3" + npm-pick-manifest "^6.0.0" + promise-inflight "^1.0.1" + promise-retry "^2.0.1" + semver "^7.3.2" + unique-filename "^1.1.1" + which "^2.0.2" + +"@npmcli/installed-package-contents@^1.0.5": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" + integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw== + dependencies: + npm-bundled "^1.1.1" + npm-normalize-package-bin "^1.0.1" + "@npmcli/move-file@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.0.1.tgz#de103070dac0f48ce49cf6693c23af59c0f70464" @@ -1574,6 +1691,30 @@ dependencies: mkdirp "^1.0.4" +"@npmcli/node-gyp@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.2.tgz#3cdc1f30e9736dbc417373ed803b42b1a0a29ede" + integrity sha512-yrJUe6reVMpktcvagumoqD9r08fH1iRo01gn1u0zoCApa9lnZGEigVKUd2hzsCId4gdtkZZIVscLhNxMECKgRg== + +"@npmcli/promise-spawn@^1.1.0", "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" + integrity sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg== + dependencies: + infer-owner "^1.0.4" + +"@npmcli/run-script@^1.3.0": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.3.tgz#07f440ed492400bb1114369bc37315eeaaae2bb3" + integrity sha512-ELPGWAVU/xyU+A+H3pEPj0QOvYwLTX71RArXcClFzeiyJ/b/McsZ+d0QxpznvfFtZzxGN/gz/1cvlqICR4/suQ== + dependencies: + "@npmcli/node-gyp" "^1.0.2" + "@npmcli/promise-spawn" "^1.3.2" + infer-owner "^1.0.4" + node-gyp "^7.1.0" + puka "^1.0.1" + read-package-json-fast "^2.0.1" + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -1627,27 +1768,27 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= -"@schematics/angular@11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-11.0.1.tgz#33617ae6df12b4cc883d9d4ab4601762be12a983" - integrity sha512-cYq3NhFn4DLSXXtbYYU2w0sginkMfN1w7pXjZLT/+etXXbtANQAXSPrPrDQql004ZNMbuDKuC0aoXjv8hgXOfw== +"@schematics/angular@11.2.2": + version "11.2.2" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-11.2.2.tgz#ff69a66b6e1acf5aa36ed0795973f3f57d893d0b" + integrity sha512-TcxPy58adUnkirGXyZVVSMuKkA0eIz2PWSQWEgB9l7kO+5LvDOn+RMoc6AVx0s/bU9nH+eozBUJ1XAD/E8QnYQ== dependencies: - "@angular-devkit/core" "11.0.1" - "@angular-devkit/schematics" "11.0.1" - jsonc-parser "2.3.1" + "@angular-devkit/core" "11.2.2" + "@angular-devkit/schematics" "11.2.2" + jsonc-parser "3.0.0" -"@schematics/update@0.1100.1": - version "0.1100.1" - resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1100.1.tgz#18a3befe9faece0fe784843f2f6a774ed09843ce" - integrity sha512-EVcqdM/d5rC5L1UYnwhFMk/TjHlNgL5LGfroE13C38A+WpKKJquAjgOQLj4nPvJ5csdEZqn3Sui9yeEWc3hklQ== +"@schematics/update@0.1102.2": + version "0.1102.2" + resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1102.2.tgz#f8aed68bbcefdc8633c7804e47ff891ef06bd5ef" + integrity sha512-Nz8kjeixzDnOw00bnZznq3qrbIv8yWEWNb9eDkRBqgOUXQwlhKJY/sYBK58JF2D+conaRVuEqMsBlX08GlFtIA== dependencies: - "@angular-devkit/core" "11.0.1" - "@angular-devkit/schematics" "11.0.1" + "@angular-devkit/core" "11.2.2" + "@angular-devkit/schematics" "11.2.2" "@yarnpkg/lockfile" "1.1.0" - ini "1.3.5" + ini "2.0.0" npm-package-arg "^8.0.0" - pacote "9.5.12" - semver "7.3.2" + pacote "11.2.4" + semver "7.3.4" semver-intersect "1.4.0" "@sindresorhus/is@^0.14.0": @@ -1655,10 +1796,79 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@sqltools/formatter@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.2.tgz#9390a8127c0dcba61ebd7fdcc748655e191bdd68" - integrity sha512-/5O7Fq6Vnv8L6ucmPjaWbVG1XkP4FO+w5glqfkIsq3Xw4oyNAdJddbnYodNDAfjVUvo/rrSCTom4kAND7T1o5Q== +"@swc/cli@^0.1.35": + version "0.1.35" + resolved "https://registry.yarnpkg.com/@swc/cli/-/cli-0.1.35.tgz#e939fd374e889e3329ebdc48988e9e489d06f002" + integrity sha512-jjgfm8s+ueNAS8QaStpEXctn0O0AVfRR1vAocsFQyLysQkmbsnp8OyajaKgaLSUsdf4ZWlF8pKATPUPp869UEQ== + dependencies: + commander "^7.1.0" + convert-source-map "^1.6.0" + glob "^7.1.3" + lodash "^4.17.21" + slash "3.0.0" + source-map "^0.7.3" + +"@swc/core-android-arm64@^1.2.50": + version "1.2.50" + resolved "https://registry.yarnpkg.com/@swc/core-android-arm64/-/core-android-arm64-1.2.50.tgz#763e5ad1a899e2ff0a914c8ac8134e573d8dc733" + integrity sha512-aPJGhHqRkNncZnG5fCRhdX9JHGQ7SJBykm7ER/F6nNuvuSTYbuv4wGLYzNVi2UmeSMQQH/9j1D5xcX8STGIgsw== + +"@swc/core-darwin-arm64@^1.2.50": + version "1.2.50" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.50.tgz#1ed630edef1b8864d3793e4133988b7281511239" + integrity sha512-ginmLsDGswvaENSDxhevBpq63iKAxKtVS0P53b9KvfOhRDtQpnXMIxc3iYeyII8D2QDR0HOQu/c1n2b6sUIuBQ== + +"@swc/core-darwin-x64@^1.2.50": + version "1.2.50" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.2.50.tgz#2e794feb5071cf4e7e20cedde872f4dfb77cd031" + integrity sha512-HJ2CFuzxIXwoo22jHr1ntuveqoHN0hbY7mUTXaRLWh9468TMeGmD20rm7l9FnOsBVH9m9psHGpOVNOUSEXg6tw== + +"@swc/core-linux-arm-gnueabihf@^1.2.50": + version "1.2.50" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.50.tgz#c7b9e2b35c7d9997004d6138c1c8115e14744cb7" + integrity sha512-y1rsaqmbO/rezCifX30ilqTKaeN4yp0pDpHri6hAMKqHOzNGZTrY7p3nmiCd425gFVpM72gcGNrDqbt9IqeOPA== + +"@swc/core-linux-arm64-gnu@^1.2.50": + version "1.2.50" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.50.tgz#1f41ac1fae0438641773098f254c9e1d72563d1f" + integrity sha512-eh1xpvPNoXiZBrkKLoM1JDXAH5U7zK+kebC7lXsPjPuPVGENfknAoT3oQvyohWnE7pf1nOPBiDX7wOT4T2e1Pw== + +"@swc/core-linux-x64-gnu@^1.2.50": + version "1.2.50" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.50.tgz#57ca45b5b5e67a12ff90b5f50a23559be65840e6" + integrity sha512-2I5OX66FQejeiJ4fuPS18AQieVk6H8Z2LfOgCuDMK2ZRQdc3gBe1rYVhZyCGdYXQ76mUlo6+phSy5qI63d172Q== + +"@swc/core-linux-x64-musl@^1.2.50": + version "1.2.50" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.50.tgz#c9225ec94618e75a5260264db7cd3122930e68f1" + integrity sha512-370yPM7+8tzEdiV3Y+RzkzukRfYeDiOS6oRCtpTqGmfh9RUhxyKQFM/gYRi77Qpgq6LnvY1eUQh+WrioCAztXg== + +"@swc/core-win32-ia32-msvc@^1.2.50": + version "1.2.50" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.50.tgz#8aca2e009d49f2cff44fe924115104565ce59d5c" + integrity sha512-sQOXq79ge8Bv07rh1bHZwiH5qXg8do28HdzhI8/VaFAh3K+8zTqOdF9DuOlf0Z/sE23znbbQcp3Hz30+5jhgWg== + +"@swc/core-win32-x64-msvc@^1.2.50": + version "1.2.50" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.50.tgz#383ac624bdf0d7865013c6482392319afe2e8462" + integrity sha512-NmxSnkj7PYkN+e060tf0LR7knrdCzUobSmQrMwWlaC5dACHREFlo00mocL8AwB3WOZKvNp4MQv1cH3EaGbIpKw== + +"@swc/core@^1.2.50": + version "1.2.50" + resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.2.50.tgz#d70daed948c9bb0ba8084e804e7fb2b1fcf0e19c" + integrity sha512-v4geRnqPqNBOAhWIT0ntcbw09mOxNy5XaGp4Nulxefpdpr8QUxPa1/9fLRPAbYjojT8yiqDtcLdx+yk+stFhpA== + dependencies: + "@node-rs/helper" "^1.0.0" + optionalDependencies: + "@swc/core-android-arm64" "^1.2.50" + "@swc/core-darwin-arm64" "^1.2.50" + "@swc/core-darwin-x64" "^1.2.50" + "@swc/core-linux-arm-gnueabihf" "^1.2.50" + "@swc/core-linux-arm64-gnu" "^1.2.50" + "@swc/core-linux-x64-gnu" "^1.2.50" + "@swc/core-linux-x64-musl" "^1.2.50" + "@swc/core-win32-ia32-msvc" "^1.2.50" + "@swc/core-win32-x64-msvc" "^1.2.50" "@szmarczak/http-timer@^1.1.2": version "1.1.2" @@ -1672,30 +1882,26 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== -"@ts-morph/common@~0.7.0": - version "0.7.3" - resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.7.3.tgz#380020c278e4aa6cecedf362a1157591d1003267" - integrity sha512-M6Tcu0EZDLL8Ht7WAYz7yJfDZ9eArhqR8XZ9Mk3q8jwU6MKFAttrw3JtW4JhneqTz7pZMv4XaimEdXI0E4K4rg== - dependencies: - "@dsherret/to-absolute-glob" "^2.0.2" - fast-glob "^3.2.4" - is-negated-glob "^1.0.0" - mkdirp "^1.0.4" - multimatch "^5.0.0" - typescript "~4.1.2" - -"@types/clean-css@*": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@types/clean-css/-/clean-css-4.2.2.tgz#99fd79f6939c2b325938a1c569712e07dd97d709" - integrity sha512-xiTJn3bmDh1lA8c6iVJs4ZhHw+pcmxXlJQXOB6G1oULaak8rmarIeFKI4aTJ7849dEhaO612wgIualZfbxTJwA== - dependencies: - "@types/node" "*" - "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/component-emitter@^1.2.10": + version "1.2.10" + resolved "https://registry.yarnpkg.com/@types/component-emitter/-/component-emitter-1.2.10.tgz#ef5b1589b9f16544642e473db5ea5639107ef3ea" + integrity sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg== + +"@types/cookie@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.0.tgz#14f854c0f93d326e39da6e3b6f34f7d37513d108" + integrity sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg== + +"@types/cors@^2.8.8": + version "2.8.10" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.10.tgz#61cc8469849e5bcdd0c7044122265c39cec10cf4" + integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ== + "@types/duplexify@^3.6.0": version "3.6.0" resolved "https://registry.yarnpkg.com/@types/duplexify/-/duplexify-3.6.0.tgz#dfc82b64bd3a2168f5bd26444af165bf0237dcd8" @@ -1724,25 +1930,11 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/html-minifier@^3.5.3": - version "3.5.3" - resolved "https://registry.yarnpkg.com/@types/html-minifier/-/html-minifier-3.5.3.tgz#5276845138db2cebc54c789e0aaf87621a21e84f" - integrity sha512-j1P/4PcWVVCPEy5lofcHnQ6BtXz9tHGiFPWzqm7TtGuWZEfCHEP446HlkSNc9fQgNJaJZ6ewPtp2aaFla/Uerg== - dependencies: - "@types/clean-css" "*" - "@types/relateurl" "*" - "@types/uglify-js" "*" - "@types/jasmine@~3.6.0": version "3.6.1" resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.6.1.tgz#f8b95def0016411c58c7adb4791dff29bc62992c" integrity sha512-eeSCVhBsgwHNS1FmaMu4zrLxfykCTWJMLFZv7lmyrZQjw7foUUXoPu4GukSN9v7JvUw7X+/aDH3kCaymirBSTg== -"@types/js-yaml@^3.12.5": - version "3.12.6" - resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.6.tgz#7f10c926aa41e189a2755c4c7fcf8e4573bd7ac1" - integrity sha512-cK4XqrLvP17X6c0C8n4iTbT59EixqyXL3Fk8/Rsk4dF3oX4dg70gYUXrXVUUHpnsGMPNlTQMqf+TVmNPX6FmSQ== - "@types/json-schema@^7.0.4": version "7.0.5" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" @@ -1753,18 +1945,6 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== -"@types/klaw-sync@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@types/klaw-sync/-/klaw-sync-6.0.0.tgz#ff0b36601efaaa109d513c4ced109311fd06ba36" - integrity sha512-Ibfb2jgpjYUxnl7RSVvUzOrv/vhkTVKEfPwQf9ZlDDsSyWVDp/2JtTBxO4eRrKBYtxc3cZQabdR38i8R0o1uww== - dependencies: - "@types/node" "*" - -"@types/lodash@^4.14.133": - version "4.14.165" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.165.tgz#74d55d947452e2de0742bad65270433b63a8c30f" - integrity sha512-tjSSOTHhI5mCHTy/OOXYIhi2Wt1qcbHmuXD1Ha7q70CgI/I71afO4XtLb/cVexki1oVYchpul/TOuu3Arcdxrg== - "@types/long@^4.0.0", "@types/long@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" @@ -1775,18 +1955,11 @@ resolved "https://registry.yarnpkg.com/@types/lunr/-/lunr-2.3.2.tgz#d4a51703315ed0e53c43257216f1014ce6491562" integrity sha512-zcUZYquYDUEegRRPQtkZ068U9CoIjW6pJMYCVDRK25r76FEWvMm1oHqZQUfQh4ayIZ42lipXOpXEiAtGXc1XUg== -"@types/minimatch@*", "@types/minimatch@^3.0.3": +"@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== -"@types/mkdirp@^0.5.2": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-0.5.2.tgz#503aacfe5cc2703d5484326b1b27efa67a339c1f" - integrity sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg== - dependencies: - "@types/node" "*" - "@types/node@*": version "13.7.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.1.tgz#238eb34a66431b71d2aaddeaa7db166f25971a0d" @@ -1807,18 +1980,16 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.30.tgz#1ed6e01e4ca576d5aec9cc802cc3bcf94c274192" integrity sha512-HmqFpNzp3TSELxU/bUuRK+xzarVOAsR00hzcvM0TXrMlt/+wcSLa5q6YhTb6/cA6wqDCZLDcfd8fSL95x5h7AA== +"@types/node@^14.14.10": + version "14.14.31" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055" + integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/puppeteer@^1.12.4": - version "1.20.7" - resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-1.20.7.tgz#31fb4274f0c6ec2e90ed8473616243f15a808017" - integrity sha512-LCfP/Zf/y4I/hG8ARR8htPYa1wpLpUkysJo9TffmQssVz8c1b9uDNU4benDHSldiz7HVAMek1DCWz7KbqEUg3w== - dependencies: - "@types/node" "*" - "@types/q@^0.0.32": version "0.0.32" resolved "https://registry.yarnpkg.com/@types/q/-/q-0.0.32.tgz#bd284e57c84f1325da702babfc82a5328190c0c5" @@ -1829,11 +2000,6 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== -"@types/relateurl@*": - version "0.2.28" - resolved "https://registry.yarnpkg.com/@types/relateurl/-/relateurl-0.2.28.tgz#6bda7db8653fa62643f5ee69e9f69c11a392e3a6" - integrity sha1-a9p9uGU/piZD9e5p6facEaOS46Y= - "@types/selenium-webdriver@^3.0.0": version "3.0.16" resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.16.tgz#50a4755f8e33edacd9c406729e9b930d2451902a" @@ -1844,12 +2010,10 @@ resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== -"@types/uglify-js@*": - version "3.11.1" - resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.11.1.tgz#97ff30e61a0aa6876c270b5f538737e2d6ab8ceb" - integrity sha512-7npvPKV+jINLu1SpSYVWG8KvyJBhBa8tmzMMdDoVc2pWUYHN8KIXlPJhjJ4LT97c4dXJA2SHL/q6ADbDriZN+Q== - dependencies: - source-map "^0.6.1" +"@types/stemmer@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/stemmer/-/stemmer-1.0.2.tgz#bd8354f50b3c9b87c351d169240e45cf1fa1f5e8" + integrity sha512-2gWEIFqVZjjZxo8/TcugCAl7nW9Jd9ArEDpTAc5nH7d+ZUkreHA7GzuFcLZ0sflLrA5b1PZ+2yDyHJcuP9KWWw== "@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2": version "2.0.3" @@ -1886,22 +2050,12 @@ resolved "https://registry.yarnpkg.com/@types/xregexp/-/xregexp-3.0.30.tgz#333d550467dd27ef989f375629f8f279a97cee39" integrity sha512-u1dpabg81Rd660bYebOqMXO0+E63H1hxunPAWGebNb7TpxqZYe9YaVLgkkj6ZnzLs3yLumtVB956o8u8OZdhXw== -"@vitalets/google-translate-api@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@vitalets/google-translate-api/-/google-translate-api-4.0.0.tgz#e33d4e44ef75f2595a9f7179a6a0baed9b61cd3d" - integrity sha512-qFGQos1frg5u1JuIDI+Qbkau2a0COANbBvTJjmPjbKxQZEvO010fd/4x0ruMEeEMxf+096GiGd4d4hdYw0NLfw== +"@types/yauzl@^2.9.1": + version "2.9.1" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af" + integrity sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA== dependencies: - "@vitalets/google-translate-token" "^1.2.0" - configstore "^5.0.1" - got "^9.6.0" - -"@vitalets/google-translate-token@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@vitalets/google-translate-token/-/google-translate-token-1.2.0.tgz#a7581d15644e2a60027850c60e45efdc15646dda" - integrity sha512-7MXRjMIWTH8XdIWRWj9IUwgYnd+VB5/herOgxNemSRzIJ6oaIeAxOSe0htw5S+OSm/gvCcUz2gq3SNBO0TufDA== - dependencies: - configstore "^5.0.1" - got "^6.3.0" + "@types/node" "*" "@webassemblyjs/ast@1.9.0": version "1.9.0" @@ -2068,7 +2222,7 @@ resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== -JSONStream@^1.2.1, JSONStream@^1.3.4: +JSONStream@^1.2.1: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== @@ -2086,7 +2240,12 @@ a-sync-waterfall@^1.0.0: resolved "https://registry.yarnpkg.com/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz#75b6b6aa72598b497a125e7a2770f14f4c8a1fa7" integrity sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA== -abab@^2.0.0, abab@^2.0.5: +abab@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e" + integrity sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4= + +abab@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== @@ -2111,13 +2270,12 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" -acorn-globals@^4.3.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" - integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== +acorn-globals@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf" + integrity sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8= dependencies: - acorn "^6.0.1" - acorn-walk "^6.0.1" + acorn "^4.0.4" acorn-jsx@^3.0.0: version "3.0.1" @@ -2126,26 +2284,21 @@ acorn-jsx@^3.0.0: dependencies: acorn "^3.0.4" -acorn-walk@^6.0.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" - integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== - acorn@^3.0.4: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= +acorn@^4.0.4: + version "4.0.13" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" + integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c= + acorn@^5.5.0: version "5.7.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== -acorn@^6.0.1, acorn@^6.0.4: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== - acorn@^6.4.1: version "6.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" @@ -2164,18 +2317,6 @@ adm-zip@^0.4.9: resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.14.tgz#2cf312bcc9f8875df835b0f6040bd89be0a727a9" integrity sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g== -after@0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" - integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= - -agent-base@4, agent-base@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" - integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== - dependencies: - es6-promisify "^5.0.0" - agent-base@5: version "5.1.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" @@ -2188,18 +2329,20 @@ agent-base@6: dependencies: debug "4" -agent-base@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" - integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== +agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== dependencies: es6-promisify "^5.0.0" -agentkeepalive@^3.4.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67" - integrity sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ== +agentkeepalive@^4.1.3: + version "4.1.4" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.1.4.tgz#d928028a4862cb11718e55227872e842a44c945b" + integrity sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ== dependencies: + debug "^4.1.0" + depd "^1.1.2" humanize-ms "^1.2.1" aggregate-error@^3.0.0: @@ -2299,13 +2442,6 @@ ambi@^2.2.0: editions "^1.1.1" typechecker "^4.3.0" -ansi-align@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" - integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38= - dependencies: - string-width "^2.0.0" - ansi-align@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" @@ -2328,7 +2464,7 @@ ansi-escapes@^1.1.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= -ansi-escapes@^3.0.0, ansi-escapes@^3.1.0, ansi-escapes@^3.2.0: +ansi-escapes@^3.1.0, ansi-escapes@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== @@ -2390,11 +2526,6 @@ ansicolors@~0.3.2: resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk= -any-promise@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" - integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= - anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -2525,11 +2656,6 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= -array-differ@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" - integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== - array-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" @@ -2550,11 +2676,6 @@ array-flatten@^2.1.0: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== -array-iterate@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/array-iterate/-/array-iterate-1.1.4.tgz#add1522e9dd9749bb41152d08b845bd08d6af8b7" - integrity sha512-sNRaPGh9nnmdC8Zf+pT3UqP8rnWj5Hf9wiFGsX3wUQ2yVSIhO2ShFwCoceIPpB41QF6i2OEmrHmCo36xronCVA== - array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -2577,17 +2698,12 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -arraybuffer.slice@~0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" - integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== - -arrify@^1.0.0, arrify@^1.0.1: +arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= -arrify@^2.0.0, arrify@^2.0.1: +arrify@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== @@ -2640,11 +2756,6 @@ assert@^1.1.1: object-assign "^4.1.1" util "0.10.3" -assertion-error@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" - integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== - assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -2697,17 +2808,16 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@9.8.6: - version "9.8.6" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" - integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== +autoprefixer@10.2.4: + version "10.2.4" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.2.4.tgz#c0e7cf24fcc6a1ae5d6250c623f0cb8beef2f7e1" + integrity sha512-DCCdUQiMD+P/as8m3XkeTUkUKuuRqLGcwD0nll7wevhqoJfMRpJlkFd1+MQh1pvupjiQuip42lc/VFvfUTMSKw== dependencies: - browserslist "^4.12.0" - caniuse-lite "^1.0.30001109" + browserslist "^4.16.1" + caniuse-lite "^1.0.30001181" colorette "^1.2.1" + fraction.js "^4.0.13" normalize-range "^0.1.2" - num2fraction "^1.2.2" - postcss "^7.0.32" postcss-value-parser "^4.1.0" aws-sign2@~0.7.0: @@ -2720,10 +2830,10 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== -axe-core@3.5.5: - version "3.5.5" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.5.5.tgz#84315073b53fa3c0c51676c588d59da09a192227" - integrity sha512-5P0QZ6J5xGikH780pghEdbEKijCTrruK9KxtPZCFWUpef0f6GipO+xEZ5GKCb020mmqgbiNO6TcA55CriL784Q== +axe-core@4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.1.2.tgz#7cf783331320098bfbef620df3b3c770147bc224" + integrity sha512-V+Nq70NxKhYt89ArVcaNL9FDryB3vQOd+BFXZIfO3RP6rwtj+2yqqqdHEkacutglPaZLkJeuXKCjCJDMGPtPqg== axobject-query@2.0.2: version "2.0.2" @@ -2741,15 +2851,14 @@ babel-code-frame@^6.16.0: esutils "^2.0.2" js-tokens "^3.0.2" -babel-loader@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" - integrity sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw== +babel-loader@8.2.2: + version "8.2.2" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81" + integrity sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g== dependencies: - find-cache-dir "^2.1.0" + find-cache-dir "^3.3.1" loader-utils "^1.4.0" - mkdirp "^0.5.3" - pify "^4.0.1" + make-dir "^3.1.0" schema-utils "^2.6.5" babel-plugin-dynamic-import-node@^2.3.3: @@ -2759,11 +2868,6 @@ babel-plugin-dynamic-import-node@^2.3.3: dependencies: object.assign "^4.1.0" -backo2@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" - integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= - bail@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" @@ -2779,22 +2883,12 @@ base64-arraybuffer@0.1.4: resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812" integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI= -base64-arraybuffer@0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" - integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= - base64-js@^1.0.2, base64-js@^1.2.3, base64-js@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -base64id@2.0.0: +base64id@2.0.0, base64id@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== @@ -2836,22 +2930,6 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -better-assert@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" - integrity sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI= - dependencies: - callsite "1.0.0" - -better-sqlite3@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-7.1.2.tgz#95565757a834093f1ecae0d4457f60820ed5dd2a" - integrity sha512-8FWYnJ6Bx94MBX03J5Ka7sTRlvXXMEm4FW2Op7nM8ErQZeyALYLmSlbMBnfr4cMpS0tj0aYZv0a+26G2YJuIjg== - dependencies: - bindings "^1.5.0" - prebuild-install "^5.3.3" - tar "^6.0.5" - big-integer@^1.6.17: version "1.6.48" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e" @@ -2907,10 +2985,19 @@ bl@^3.0.0: dependencies: readable-stream "^3.0.1" +bl@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.2.tgz#52b71e9088515d0606d9dd9cc7aa48dc1f98e73a" + integrity sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + bl@^4.0.3: - version "4.0.4" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.4.tgz#f4fda39f81a811d0df6368c1ed91dae499d1c900" - integrity sha512-7tdr4EpSd7jJ6tuQ21vu2ke8w7pNEstzj1O8wwq6sNNzO3UDi5MA8Gny/gquCj7r2C6fHudg8tKRGyjRgmvNxQ== + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== dependencies: buffer "^5.5.0" inherits "^2.0.4" @@ -2921,11 +3008,6 @@ blakejs@^1.1.0: resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5" integrity sha1-ad+S75U6qIylGjLfarHFShVfx6U= -blob@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" - integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== - blocking-proxy@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/blocking-proxy/-/blocking-proxy-1.0.1.tgz#81d6fd1fe13a4c0d6957df7f91b75e98dac40cb2" @@ -2933,7 +3015,7 @@ blocking-proxy@^1.0.0: dependencies: minimist "^1.2.0" -bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5: +bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -2981,19 +3063,6 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= -boxen@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" - integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw== - dependencies: - ansi-align "^2.0.0" - camelcase "^4.0.0" - chalk "^2.0.1" - cli-boxes "^1.0.0" - string-width "^2.0.0" - term-size "^1.2.0" - widest-line "^2.0.0" - boxen@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" @@ -3044,16 +3113,6 @@ brorand@^1.0.1: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - -browser-stdout@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" @@ -3113,7 +3172,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.0.0, browserslist@^4.8.3: +browserslist@^4.0.0: version "4.8.6" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.6.tgz#96406f3f5f0755d272e27a66f4163ca821590a7e" integrity sha512-ZHao85gf0eZ0ESxLfCp73GG9O/VTytYDIkIiZDlURppLTI9wErSM/5yAKEq6rcUdxBLjMELmrYUJGg5sxGKMHg== @@ -3122,16 +3181,6 @@ browserslist@^4.0.0, browserslist@^4.8.3: electron-to-chromium "^1.3.341" node-releases "^1.1.47" -browserslist@^4.12.0: - version "4.12.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.12.0.tgz#06c6d5715a1ede6c51fc39ff67fd647f740b656d" - integrity sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg== - dependencies: - caniuse-lite "^1.0.30001043" - electron-to-chromium "^1.3.413" - node-releases "^1.1.53" - pkg-up "^2.0.0" - browserslist@^4.14.5: version "4.14.6" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.6.tgz#97702a9c212e0c6b6afefad913d3a1538e348457" @@ -3142,6 +3191,17 @@ browserslist@^4.14.5: escalade "^3.1.1" node-releases "^1.1.65" +browserslist@^4.16.1, browserslist@^4.16.3: + version "4.16.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" + integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== + dependencies: + caniuse-lite "^1.0.30001181" + colorette "^1.2.1" + electron-to-chromium "^1.3.649" + escalade "^3.1.1" + node-releases "^1.1.70" + browserslist@^4.9.1: version "4.10.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.10.0.tgz#f179737913eaf0d2b98e4926ac1ca6a15cbcc6a9" @@ -3224,13 +3284,13 @@ buffer@^5.1.0: base64-js "^1.0.2" ieee754 "^1.1.4" -buffer@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== +buffer@^5.2.1, buffer@^5.5.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" + base64-js "^1.0.2" + ieee754 "^1.1.4" buffers@~0.1.1: version "0.1.1" @@ -3285,27 +3345,6 @@ cacache@15.0.5, cacache@^15.0.5: tar "^6.0.2" unique-filename "^1.1.1" -cacache@^12.0.0: - version "12.0.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" - integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== - dependencies: - bluebird "^3.5.5" - chownr "^1.1.1" - figgy-pudding "^3.5.1" - glob "^7.1.4" - graceful-fs "^4.1.15" - infer-owner "^1.0.3" - lru-cache "^5.1.1" - mississippi "^3.0.0" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - promise-inflight "^1.0.1" - rimraf "^2.6.3" - ssri "^6.0.1" - unique-filename "^1.1.1" - y18n "^4.0.0" - cacache@^12.0.2: version "12.0.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390" @@ -3381,11 +3420,6 @@ caller-path@^2.0.0: dependencies: caller-callsite "^2.0.0" -callsite@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" - integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA= - callsites@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" @@ -3401,7 +3435,7 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camel-case@3.0.x, camel-case@^3.0.0: +camel-case@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= @@ -3419,12 +3453,7 @@ camelcase@^2.0.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= -camelcase@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= - -camelcase@^6.1.0: +camelcase@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== @@ -3449,21 +3478,16 @@ caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001035: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001035.tgz#2bb53b8aa4716b2ed08e088d4dc816a5fe089a1e" integrity sha512-C1ZxgkuA4/bUEdMbU5WrGY4+UhMFFiXrgNAfxiMIqWgFTWfv/xsZCS2xEHT2LMq7xAZfuAnu6mcqyDl0ZR6wLQ== -caniuse-lite@^1.0.30001043: - version "1.0.30001051" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001051.tgz#8e944abf9c796bc7ea0bec3c3688a250561fc9ac" - integrity sha512-sw8UUnTlRevawTMZKN7vpfwSjCBVoiMPlYd8oT2VwNylyPCBdMAUmLGUApnYYTtIm5JXsQegUAY7GPHqgfDzjw== - -caniuse-lite@^1.0.30001109: - version "1.0.30001137" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001137.tgz#6f0127b1d3788742561a25af3607a17fc778b803" - integrity sha512-54xKQZTqZrKVHmVz0+UvdZR6kQc7pJDgfhsMYDG19ID1BWoNnDMFm5Q3uSBSU401pBvKYMsHAt9qhEDcxmk8aw== - caniuse-lite@^1.0.30001154: version "1.0.30001156" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001156.tgz#75c20937b6012fe2b02ab58b30d475bf0718de97" integrity sha512-z7qztybA2eFZTB6Z3yvaQBIoJpQtsewRD74adw2UbRWwsRq3jIPvgrQGawBMbfafekQaD21FWuXNcywtTDGGCw== +caniuse-lite@^1.0.30001181: + version "1.0.30001192" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001192.tgz#b848ebc0ab230cf313d194a4775a30155d50ae40" + integrity sha512-63OrUnwJj5T1rUmoyqYTdRWBqFFxZFlyZnRRjDR8NSUQFB6A+j/uBORU/SyJ5WzDLg4SPiZH40hQCBNdZ/jmAw== + canonical-path@1.0.0, canonical-path@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-1.0.0.tgz#fcb470c23958def85081856be7a86e904f180d1d" @@ -3479,11 +3503,6 @@ canonicalize@^1.0.1: resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-1.0.1.tgz#657b4f3fa38a6ecb97a9e5b7b26d7a19cc6e0da9" integrity sha512-N3cmB3QLhS5TJ5smKFf1w42rJXWe6C1qP01z4dxJiI5v269buii4fLHWETDyf7yEd0azGLNC63VxNMiPd2u0Cg== -capture-stack-trace@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d" - integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw== - cardinal@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-2.1.1.tgz#7cc1055d822d212954d07b085dea251cc7bc5505" @@ -3509,18 +3528,6 @@ ccount@^1.0.0, ccount@^1.0.3: resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.5.tgz#ac82a944905a65ce204eb03023157edf29425c17" integrity sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw== -chai@^4.1.2: - version "4.2.0" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" - integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw== - dependencies: - assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^3.0.1" - get-func-name "^2.0.0" - pathval "^1.1.0" - type-detect "^4.0.5" - chainsaw@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" @@ -3613,11 +3620,6 @@ character-reference-invalid@^1.0.0: resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== -chardet@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" - integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= - chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -3628,11 +3630,6 @@ charenc@~0.0.1: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= - "chokidar@>=2.0.0 <4.0.0", chokidar@^3.0.0, chokidar@^3.0.2: version "3.3.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450" @@ -3682,7 +3679,22 @@ chokidar@^3.4.1: optionalDependencies: fsevents "~2.1.2" -chownr@^1.1.1, chownr@^1.1.2: +chokidar@^3.4.2: + version "3.5.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" + integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.3.1" + +chownr@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== @@ -3692,10 +3704,10 @@ chownr@^2.0.0: resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== -chrome-launcher@^0.13.3: - version "0.13.3" - resolved "https://registry.yarnpkg.com/chrome-launcher/-/chrome-launcher-0.13.3.tgz#5dd72ae4e9b3de19ce3fe1941f89551c0ceb1d30" - integrity sha512-ovrDuFXgXS96lzeDqFPQRsczkxla+6QMvzsF+1u0mKlD1KE8EuhjdLwiDfIFedb0FSLz18RK3y6IbKu8oqA0qw== +chrome-launcher@^0.13.4: + version "0.13.4" + resolved "https://registry.yarnpkg.com/chrome-launcher/-/chrome-launcher-0.13.4.tgz#4c7d81333c98282899c4e38256da23e00ed32f73" + integrity sha512-nnzXiDbGKjDSK6t2I+35OAPBy5Pw/39bgkb/ZAFwMhwJbdYBp6aH+vW28ZgtjdU890Q7D+3wN/tB8N66q5Gi2A== dependencies: "@types/node" "*" escape-string-regexp "^1.0.5" @@ -3711,11 +3723,6 @@ chrome-trace-event@^1.0.2: dependencies: tslib "^1.9.0" -ci-info@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" - integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== - ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -3729,10 +3736,10 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: inherits "^2.0.1" safe-buffer "^5.0.1" -circular-dependency-plugin@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz#e09dbc2dd3e2928442403e2d45b41cea06bc0a93" - integrity sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw== +circular-dependency-plugin@5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz#39e836079db1d3cf2f988dc48c5188a44058b600" + integrity sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ== circular-json@^0.3.1: version "0.3.3" @@ -3763,23 +3770,11 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -clean-css@4.2.x: - version "4.2.3" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" - integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA== - dependencies: - source-map "~0.6.0" - clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -cli-boxes@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" - integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM= - cli-boxes@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" @@ -3818,27 +3813,15 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-highlight@^2.1.10: - version "2.1.10" - resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.10.tgz#26a087da9209dce4fcb8cf5427dc97cd96ac173a" - integrity sha512-CcPFD3JwdQ2oSzy+AMG6j3LRTkNjM82kzcSKzoVw6cLanDCJNlsLjeqVTOTfOfucnWv5F0rmBemVf1m9JiIasw== - dependencies: - chalk "^4.0.0" - highlight.js "^10.0.0" - mz "^2.4.0" - parse5 "^5.1.1" - parse5-htmlparser2-tree-adapter "^6.0.0" - yargs "^16.0.0" - cli-spinners@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77" integrity sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ== -cli-spinners@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.4.0.tgz#c6256db216b878cfba4720e719cec7cf72685d7f" - integrity sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA== +cli-spinners@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.5.0.tgz#12763e47251bf951cb75c201dfa58ff1bcb2d047" + integrity sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ== cli-table@^0.3.1: version "0.3.1" @@ -3866,15 +3849,6 @@ cliui@^3.0.3: strip-ansi "^3.0.1" wrap-ansi "^2.0.0" -cliui@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" - integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== - dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi "^2.0.0" - cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -3923,6 +3897,11 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= +clonedeep@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/clonedeep/-/clonedeep-2.0.0.tgz#8ceca0777f477bbf31fe8c871aaf63a390bbc272" + integrity sha1-jOygd39He78x/oyHGq9jo5C7wnI= + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -3937,11 +3916,6 @@ coa@^2.0.2: chalk "^2.4.1" q "^1.1.2" -code-block-writer@^10.1.1: - version "10.1.1" - resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-10.1.1.tgz#ad5684ed4bfb2b0783c8b131281ae84ee640a42f" - integrity sha512-67ueh2IRGst/51p0n6FvPrnRjAGHY5F8xdjkgrYE7DDzpJe6qA07RYQ9VcoUeo5ATOjSOiWpSL3SWBRRbempMw== - code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" @@ -4063,16 +4037,6 @@ comma-separated-tokens@^1.0.0, comma-separated-tokens@^1.0.1: resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== -commander@2.15.1: - version "2.15.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" - integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== - -commander@2.17.x: - version "2.17.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" - integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== - commander@^2.11.0, commander@^2.12.1, commander@^2.20.0, commander@~2.20.3: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -4083,22 +4047,10 @@ commander@^4.0.1: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== -commander@~2.19.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" - integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== - -common-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/common-dir/-/common-dir-3.0.0.tgz#37d92f4eb998ec95f67216f1a2059f40aae71736" - integrity sha512-DxMoyyrXEEGpzpLHs5UvcuXHYyUOe+weMVXgmM0cQerf/NbLzOgZuNpBh7mKbt69pgtSNjjZnEQh3CEnIIxhKQ== - dependencies: - common-sequence "^2.0.0" - -common-sequence@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/common-sequence/-/common-sequence-2.0.0.tgz#a4f01aaf5aebd0ac1ce43653e8c8fe6f0ef3a987" - integrity sha512-f0QqPLpRTgMQn/pQIynf+SdE73Lw5Q1jn4hjirHLgH/NJ71TiHjXusV16BmOyuK5rRQ1W2f++II+TFZbQOh4hA== +commander@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.1.0.tgz#f2eaecf131f10e36e07d894698226e36ae0eb5ff" + integrity sha512-pRxBna3MJe6HKnBGsDyMv8ETbptw3axEdYHoqNh7gu5oDcew8fs0xnivZGm06Ogk8zGAJ9VX+OPEr2GXEQK4dg== commondir@^1.0.1: version "1.0.1" @@ -4112,26 +4064,11 @@ compare-semver@^1.0.0: dependencies: semver "^5.0.1" -component-bind@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" - integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= - -component-emitter@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" - integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= - component-emitter@^1.2.1, component-emitter@~1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== -component-inherit@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" - integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= - compose-function@3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f" @@ -4184,7 +4121,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.5.2, concat-stream@^1.6.2: +concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.5.2: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -4194,18 +4131,6 @@ concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.5.2, concat-stream@ readable-stream "^2.2.2" typedarray "^0.0.6" -configstore@^3.0.0, configstore@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.2.tgz#c6f25defaeef26df12dd33414b001fe81a543f8f" - integrity sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw== - dependencies: - dot-prop "^4.1.0" - graceful-fs "^4.1.2" - make-dir "^1.0.0" - unique-string "^1.0.0" - write-file-atomic "^2.0.0" - xdg-basedir "^3.0.0" - configstore@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" @@ -4280,12 +4205,17 @@ content-disposition@0.5.3: dependencies: safe-buffer "5.1.2" +content-type-parser@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7" + integrity sha512-lM4l4CnMEwOLHAHr/P6MEZwZFPJFtAAKgL6pogbXmVZggIqXhdB6RbBtPOTsw2FcXwYhehRGERJmRrjOiIB8pQ== + content-type@^1.0.4, content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== -convert-source-map@1.7.0, convert-source-map@^1.5.1, convert-source-map@^1.7.0: +convert-source-map@1.7.0, convert-source-map@^1.5.1, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -4312,6 +4242,18 @@ cookie@0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== +cookie@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" + integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== + +copy-anything@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-2.0.3.tgz#842407ba02466b0df844819bbe3baebbe5d45d87" + integrity sha512-GK6QUtisv4fNS+XcI7shX0Gx9ORg7QqIznyfho79JTnX1XhLiyZHfftvGiziqzRiEi/Bjhgpi+D2o7HxJFPnDQ== + dependencies: + is-what "^3.12.0" + copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -4329,10 +4271,10 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -copy-webpack-plugin@6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.2.1.tgz#8015e4d5c5e637ab7b39c76daa9e03c7a4bf1ae5" - integrity sha512-VH2ZTMIBsx4p++Lmpg77adZ0KUyM5gFR/9cuTrbneNnJlcQXUFvsNariPqq2dq2kV3F2skHiDGPQCyKWy1+U0Q== +copy-webpack-plugin@6.3.2: + version "6.3.2" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.3.2.tgz#0e920a6c181a5052aa6e2861b164bda03f83afeb" + integrity sha512-MgJ1uouLIbDg4ST1GzqrGQyKoXY5iPqi6fghFqarijam7FQcBa/r6Rg0VkoIuzx75Xq8iAMghyOueMkWUQ5OaA== dependencies: cacache "^15.0.5" fast-glob "^3.2.4" @@ -4346,12 +4288,12 @@ copy-webpack-plugin@6.2.1: serialize-javascript "^5.0.1" webpack-sources "^1.4.3" -core-js-compat@^3.6.2: - version "3.6.4" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.4.tgz#938476569ebb6cda80d339bcf199fae4f16fff17" - integrity sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA== +core-js-compat@^3.8.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.9.0.tgz#29da39385f16b71e1915565aa0385c4e0963ad56" + integrity sha512-YK6fwFjCOKWwGnjFUR3c544YsnA/7DoLL0ysncuOJ4pwbriAtOpvM2bygdlcXbvQCQZ7bBU9CL4t7tGl7ETRpQ== dependencies: - browserslist "^4.8.3" + browserslist "^4.16.3" semver "7.0.0" core-js-pure@^3.0.0: @@ -4359,12 +4301,7 @@ core-js-pure@^3.0.0: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a" integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw== -core-js@3.6.5: - version "3.6.5" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" - integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== - -core-js@^3.8.1: +core-js@3.8.3: version "3.8.3" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.8.3.tgz#c21906e1f14f3689f93abcc6e26883550dd92dd0" integrity sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q== @@ -4374,10 +4311,13 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -corser@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87" - integrity sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c= +cors@~2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" cosmiconfig@^5.0.0: version "5.2.1" @@ -4431,13 +4371,6 @@ create-ecdh@^4.0.0: bn.js "^4.1.0" elliptic "^6.0.0" -create-error-class@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" - integrity sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y= - dependencies: - capture-stack-trace "^1.0.0" - create-hash@^1.1.0, create-hash@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" @@ -4461,6 +4394,17 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" +critters@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/critters/-/critters-0.0.7.tgz#548b470360f4f3c51e622de3b7aa733c8f0b17bf" + integrity sha512-qUF2SaAWFYjNPdCcPpu68p2DnHiosia84yx5mPTlUMQjkjChR+n6sO1/I7yn2U2qNDgSPTd2SoaTIDQcUL+EwQ== + dependencies: + chalk "^4.1.0" + css "^3.0.0" + parse5 "^6.0.1" + parse5-htmlparser2-tree-adapter "^6.0.1" + pretty-bytes "^5.3.0" + cross-env@^5.1.3: version "5.2.1" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.2.1.tgz#b2c76c1ca7add66dc874d11798466094f551b34d" @@ -4468,7 +4412,7 @@ cross-env@^5.1.3: dependencies: cross-spawn "^6.0.5" -cross-spawn@^5.0.1, cross-spawn@^5.1.0: +cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= @@ -4519,11 +4463,6 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" -crypto-random-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" - integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= - crypto-random-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" @@ -4547,16 +4486,16 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" -css-loader@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.0.0.tgz#f0a48dfacc3ab9936a05ee16a09e7f313872e117" - integrity sha512-9g35eXRBgjvswyJWoqq/seWp+BOxvUl8IinVNTsUBFFxtwfEYvlmEn6ciyn0liXGbGh5HyJjPGCuobDSfqMIVg== +css-loader@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.0.1.tgz#9e4de0d6636a6266a585bd0900b422c85539d25f" + integrity sha512-cXc2ti9V234cq7rJzFKhirb2L2iPy8ZjALeVJAozXYz9te3r4eqLSixNAbMDJSgJEQywqXzs8gonxaboeKqwiw== dependencies: - camelcase "^6.1.0" + camelcase "^6.2.0" cssesc "^3.0.0" icss-utils "^5.0.0" loader-utils "^2.0.0" - postcss "^8.1.1" + postcss "^8.1.4" postcss-modules-extract-imports "^3.0.0" postcss-modules-local-by-default "^4.0.0" postcss-modules-scope "^3.0.0" @@ -4629,6 +4568,15 @@ css@^2.0.0: source-map-resolve "^0.5.2" urix "^0.1.0" +css@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d" + integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ== + dependencies: + inherits "^2.0.4" + source-map "^0.6.1" + source-map-resolve "^0.6.0" + cssauron@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/cssauron/-/cssauron-1.4.0.tgz#a6602dff7e04a8306dc0db9a551e92e8b5662ad8" @@ -4726,7 +4674,7 @@ csso@^4.0.2: dependencies: css-tree "1.0.0-alpha.37" -cssom@0.3.x, cssom@^0.3.4: +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": version "0.3.8" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== @@ -4738,10 +4686,10 @@ cssstyle@1.2.1: dependencies: cssom "0.3.x" -cssstyle@^1.1.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" - integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== +"cssstyle@>= 0.2.37 < 0.3.0": + version "0.2.37" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54" + integrity sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ= dependencies: cssom "0.3.x" @@ -4787,15 +4735,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-urls@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" - integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== - dependencies: - abab "^2.0.0" - whatwg-mimetype "^2.2.0" - whatwg-url "^7.0.0" - date-format@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf" @@ -4806,21 +4745,14 @@ date-format@^3.0.0: resolved "https://registry.yarnpkg.com/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95" integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w== -debug@2.6.9, debug@^2.0.0, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: +debug@2.6.9, debug@^2.0.0, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@3.1.0, debug@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - -debug@4, debug@^4.1.0, debug@^4.1.1, debug@~4.1.0: +debug@4, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -4834,20 +4766,34 @@ debug@4.1.0: dependencies: ms "^2.1.1" -debug@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" - integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== +debug@4.3.1, debug@~4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" -debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5: +debug@^3.0.0, debug@^3.1.0, debug@^3.1.1: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== dependencies: ms "^2.1.1" +debug@^3.2.6: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + decamelize@^1.1.1, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -4865,20 +4811,6 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" -decompress-response@^4.2.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" - integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== - dependencies: - mimic-response "^2.0.0" - -deep-eql@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" - integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== - dependencies: - type-detect "^4.0.0" - deep-equal@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" @@ -4991,7 +4923,7 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -depd@~1.1.2: +depd@^1.1.2, depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= @@ -5031,16 +4963,16 @@ details-element-polyfill@^2.4.0: resolved "https://registry.yarnpkg.com/details-element-polyfill/-/details-element-polyfill-2.4.0.tgz#e0622adef7902662faf27b4ab8acba5dc4e3a6e6" integrity sha512-jnZ/m0+b1gz3EcooitqL7oDEkKHEro659dt8bWB/T/HjiILucoQhHvvi5MEOAIFJXxxO+rIYJ/t3qCgfUOSU5g== -detect-libc@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - detect-node@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== +devtools-protocol@0.0.809251: + version "0.0.809251" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.809251.tgz#300b3366be107d5c46114ecb85274173e3999518" + integrity sha512-pf+2OY6ghMDPjKkzSWxHMq+McD+9Ojmq5XVRYpv/kPd9sTMQxzEt21592a31API8qRjro0iYYOc3ag46qF/1FA== + dgeni-packages@^0.28.4: version "0.28.4" resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.28.4.tgz#53a3e6700b8d8f6be168cadcc9fdb36e1d7011d3" @@ -5068,7 +5000,22 @@ dgeni-packages@^0.28.4: typescript "^3.2.2" urlencode "^1.1.0" -dgeni@^0.4.11, dgeni@^0.4.9: +dgeni@^0.4.13: + version "0.4.13" + resolved "https://registry.yarnpkg.com/dgeni/-/dgeni-0.4.13.tgz#274ad9edfe17bd6c2c1c51e9f30cb20faccdabc0" + integrity sha512-gryF1gNdzbQdgAeQxjaFbbKgR0qzyjq93Rc6/3CsBIN88NJ5yvsT4x26BvCKVI6jh2WatgvuGj+akmWvGndTyQ== + dependencies: + canonical-path "~0.0.2" + clonedeep "^2.0.0" + dependency-graph "^0.7.0" + di "0.0.1" + fast-deep-equal "^3.1.3" + objectdiff "^1.1.0" + optimist "~0.6.1" + validate.js "^0.12.0" + winston "^2.1.1" + +dgeni@^0.4.9: version "0.4.12" resolved "https://registry.yarnpkg.com/dgeni/-/dgeni-0.4.12.tgz#574a91de25dab0018a9832da83fdb7c2b8c7507b" integrity sha512-AhlRuwduzZhHIpf8DYZ0wWHLvV+9IQ8dQRqzx3ryYEg9trU/gWc2xz3wnJrRpDnS6Hu6F73saj6m1xbckW3k9Q== @@ -5088,11 +5035,6 @@ di@0.0.1, di@^0.0.1: resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" integrity sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw= -diff@3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" - integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== - diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -5107,14 +5049,6 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" -dir-glob@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" - integrity sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag== - dependencies: - arrify "^1.0.1" - path-type "^3.0.0" - dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -5182,13 +5116,6 @@ domelementtype@^2.0.1: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== -domexception@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" - integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== - dependencies: - webidl-conversions "^4.0.2" - domhandler@^2.3.0: version "2.4.2" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" @@ -5211,7 +5138,7 @@ dot-case@^2.1.0: dependencies: no-case "^2.2.0" -dot-prop@^4.1.0, dot-prop@^4.1.1: +dot-prop@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ== @@ -5230,11 +5157,6 @@ dotenv@^6.1.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064" integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w== -dotenv@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" - integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== - duplexer2@~0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -5280,16 +5202,6 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" -ecstatic@^3.0.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/ecstatic/-/ecstatic-3.3.2.tgz#6d1dd49814d00594682c652adb66076a69d46c48" - integrity sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog== - dependencies: - he "^1.1.1" - mime "^1.6.0" - minimist "^1.1.0" - url-join "^2.0.5" - editions@^1.1.1, editions@^1.3.1, editions@^1.3.3: version "1.3.4" resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b" @@ -5318,16 +5230,16 @@ electron-to-chromium@^1.3.378: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.379.tgz#81dc5e82a3e72bbb830d93e15bc35eda2bbc910e" integrity sha512-NK9DBBYEBb5f9D7zXI0hiE941gq3wkBeQmXs1ingigA/jnTg5mhwY2Z5egwA+ZI8OLGKCx0h1Cl8/xeuIBuLlg== -electron-to-chromium@^1.3.413: - version "1.3.428" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.428.tgz#9afec8766dbe3cab825817f77e3ed0e63467b71a" - integrity sha512-u3+5jEfgLKq/hGO96YfAoOAM1tgFnRDTCD5mLuev44tttcXix+INtVegAkmGzUcfDsnzkPt51XXurXZLLwXt0w== - electron-to-chromium@^1.3.585: version "1.3.591" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.591.tgz#a18892bf1acb93f7b6e4da402705d564bc235017" integrity sha512-ol/0WzjL4NS4Kqy9VD6xXQON91xIihDT36sYCew/G/bnd1v0/4D+kahp26JauQhgFUjrdva3kRSo7URcUmQ+qw== +electron-to-chromium@^1.3.649: + version "1.3.673" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.673.tgz#b4f81c930b388f962b7eba20d0483299aaa40913" + integrity sha512-ms+QR2ckfrrpEAjXweLx6kNCbpAl66DcW//3BZD4BV5KhUgr0RZRce1ON/9J3QyA3JO28nzgb5Xv8DnPr05ILg== + elliptic@^6.0.0: version "6.5.2" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762" @@ -5376,12 +5288,12 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -encoding@^0.1.11: - version "0.1.12" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" - integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s= +encoding@^0.1.12: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== dependencies: - iconv-lite "~0.4.13" + iconv-lite "^0.6.2" end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" @@ -5390,53 +5302,33 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -engine.io-client@~3.4.0: - version "3.4.4" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.4.4.tgz#77d8003f502b0782dd792b073a4d2cf7ca5ab967" - integrity sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ== +engine.io-parser@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-4.0.2.tgz#e41d0b3fb66f7bf4a3671d2038a154024edb501e" + integrity sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg== dependencies: - component-emitter "~1.3.0" - component-inherit "0.0.3" - debug "~3.1.0" - engine.io-parser "~2.2.0" - has-cors "1.1.0" - indexof "0.0.1" - parseqs "0.0.6" - parseuri "0.0.6" - ws "~6.1.0" - xmlhttprequest-ssl "~1.5.4" - yeast "0.1.2" - -engine.io-parser@~2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.1.tgz#57ce5611d9370ee94f99641b589f94c97e4f5da7" - integrity sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg== - dependencies: - after "0.8.2" - arraybuffer.slice "~0.0.7" base64-arraybuffer "0.1.4" - blob "0.0.5" - has-binary2 "~1.0.2" -engine.io@~3.4.0: - version "3.4.2" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.4.2.tgz#8fc84ee00388e3e228645e0a7d3dfaeed5bd122c" - integrity sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg== +engine.io@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-4.1.1.tgz#9a8f8a5ac5a5ea316183c489bf7f5b6cf91ace5b" + integrity sha512-t2E9wLlssQjGw0nluF6aYyfX8LwYU8Jj0xct+pAhfWfv/YrBn6TSNtEYsgxHIfaMqfrLx07czcMg9bMN6di+3w== dependencies: accepts "~1.3.4" base64id "2.0.0" - cookie "0.3.1" - debug "~4.1.0" - engine.io-parser "~2.2.0" - ws "^7.1.2" + cookie "~0.4.1" + cors "~2.8.5" + debug "~4.3.1" + engine.io-parser "~4.0.0" + ws "~7.4.2" -enhanced-resolve@5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.3.1.tgz#3f988d0d7775bdc2d96ede321dc81f8249492f57" - integrity sha512-G1XD3MRGrGfNcf6Hg0LVZG7GIKcYkbfHa5QMxt1HDUTdYoXH0JR1xXyg+MaKLF73E9A27uWNVxvFivNRYeUB6w== +enhanced-resolve@5.7.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz#525c5d856680fbd5052de453ac83e32049958b5c" + integrity sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw== dependencies: graceful-fs "^4.2.4" - tapable "^2.0.0" + tapable "^2.2.0" enhanced-resolve@^4.3.0: version "4.3.0" @@ -5447,7 +5339,7 @@ enhanced-resolve@^4.3.0: memory-fs "^0.5.0" tapable "^1.0.0" -ent@^2.2.0, ent@~2.2.0: +ent@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= @@ -5472,6 +5364,11 @@ err-code@^1.0.0: resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" integrity sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA= +err-code@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + errlop@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/errlop/-/errlop-2.0.0.tgz#52b97d35da1b0795e2647b5d2d3a46d17776f55a" @@ -5596,6 +5493,11 @@ es6-weak-map@^2.0.1, es6-weak-map@^2.0.2: es6-iterator "^2.0.3" es6-symbol "^3.1.1" +esbuild@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.9.0.tgz#b8320df85048ed1637c6b59ee52abba248936d3c" + integrity sha512-IqYFO7ZKHf0y4uJpJfGqInmSRn8jMPMbyI1W0Y2PSjSjJcVP538tC8TleJAS4Y8QeqwajqBTwFKayWVzYlMIgg== + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -5611,15 +5513,15 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escodegen@^1.11.0: - version "1.14.3" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" - integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== +escodegen@^1.6.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" + integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== dependencies: esprima "^4.0.1" estraverse "^4.2.0" @@ -5782,19 +5684,6 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -5865,11 +5754,6 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expand-template@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" - integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== - express@^4.16.4, express@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" @@ -5941,15 +5825,6 @@ extendr@^3.2.2, extendr@^3.5.0: editions "^2.2.0" typechecker "^4.7.0" -external-editor@^2.0.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" - integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A== - dependencies: - chardet "^0.4.0" - iconv-lite "^0.4.17" - tmp "^0.0.33" - external-editor@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" @@ -5982,15 +5857,16 @@ extract-opts@^3.3.1: editions "^2.2.0" typechecker "^4.9.0" -extract-zip@^1.6.6: - version "1.7.0" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" - integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== +extract-zip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.0.tgz#f53b71d44f4ff5a4527a2259ade000fb8b303492" + integrity sha512-i42GQ498yibjdvIhivUsRslx608whtGoFIhF26Z7O4MYncBxp8CwalOs1lnHy21A9sIohWO2+uiE4SRtC9JXDg== dependencies: - concat-stream "^1.6.2" - debug "^2.6.9" - mkdirp "^0.5.4" + debug "^4.1.1" + get-stream "^5.1.0" yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" extsprintf@1.3.0: version "1.3.0" @@ -6012,7 +5888,12 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== -fast-glob@^2.0.2, fast-glob@^2.2.6: +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^2.2.6: version "2.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== @@ -6075,21 +5956,7 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" -fault@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.4.tgz#eafcfc0a6d214fc94601e170df29954a4f842f13" - integrity sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA== - dependencies: - format "^0.2.0" - -faye-websocket@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" - integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= - dependencies: - websocket-driver ">=0.5.1" - -faye-websocket@~0.11.1: +faye-websocket@^0.11.3: version "0.11.3" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== @@ -6108,21 +5975,11 @@ fecha@^4.2.0: resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41" integrity sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg== -figgy-pudding@^3.4.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" - integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== - figgy-pudding@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== -figlet@^1.1.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.5.0.tgz#2db4d00a584e5155a96080632db919213c3e003c" - integrity sha512-ZQJM4aifMpz6H19AW1VqvZ7l4pOE9p7i/3LyxgO2kp+PO/VcDYNqIHEMtkccqIhTXMKci4kjueJr/iCQEaT/Ww== - figures@^1.3.5: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" @@ -6153,10 +6010,10 @@ file-entry-cache@^2.0.0: flat-cache "^1.2.1" object-assign "^4.0.1" -file-loader@6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.1.1.tgz#a6f29dfb3f5933a1c350b2dbaa20ac5be0539baa" - integrity sha512-Klt8C4BjWSXYQAfhpYYkG4qHNTna4toMHEbWrI5IuVoxbU6uiDKeKAP99R8mmbJi3lvewn/jQBOgU4+NS3tDQw== +file-loader@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" + integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== dependencies: loader-utils "^2.0.0" schema-utils "^3.0.0" @@ -6224,13 +6081,6 @@ find-free-port@^2.0.0: resolved "https://registry.yarnpkg.com/find-free-port/-/find-free-port-2.0.0.tgz#4b22e5f6579eb1a38c41ac6bcb3efed1b6da9b1b" integrity sha1-SyLl9leesaOMQaxryz7+0bbamxs= -find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -6329,7 +6179,7 @@ flatmap@0.0.3: resolved "https://registry.yarnpkg.com/flatmap/-/flatmap-0.0.3.tgz#1f18a4d938152d495965f9c958d923ab2dd669b4" integrity sha1-Hxik2TgVLUlZZfnJWNkjqy3WabQ= -flatted@^2.0.1, flatted@^2.0.2: +flatted@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== @@ -6373,16 +6223,16 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" -format@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" - integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs= - forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= +fraction.js@^4.0.13: + version "4.0.13" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.0.13.tgz#3c1c315fa16b35c85fffa95725a36fa729c69dfe" + integrity sha512-E1fz2Xs9ltlUp+qbiyx9wmt2n9dRzPsS11Jtdb8D2o+cC7wr9xkkKsVKJuBX0ST+LVS+LhLO+SbLJNtfWcJvXA== + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -6462,7 +6312,7 @@ fs-minipass@^1.2.5: dependencies: minipass "^2.6.0" -fs-minipass@^2.0.0: +fs-minipass@^2.0.0, fs-minipass@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== @@ -6497,6 +6347,11 @@ fsevents@~2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== +fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + fstream@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" @@ -6566,36 +6421,16 @@ generate-object-property@^1.1.0: dependencies: is-property "^1.0.0" -genfun@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537" - integrity sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA== - gensync@^1.0.0-beta.1: version "1.0.0-beta.1" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== - get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= - -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= - get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -6622,11 +6457,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -github-from-package@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" - integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= - github-slugger@^1.1.1: version "1.2.1" resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.2.1.tgz#47e904e70bf2dccd0014748142d31126cfd49508" @@ -6634,13 +6464,6 @@ github-slugger@^1.1.1: dependencies: emoji-regex ">=6.0.0 <=6.1.1" -github-slugger@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.3.0.tgz#9bd0a95c5efdfc46005e82a906ef8e2a059124c9" - integrity sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q== - dependencies: - emoji-regex ">=6.0.0 <=6.1.1" - glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" @@ -6682,18 +6505,6 @@ glob-to-regexp@^0.3.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob@7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" @@ -6706,13 +6517,6 @@ glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glo once "^1.3.0" path-is-absolute "^1.0.0" -global-dirs@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" - integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU= - dependencies: - ini "^1.3.4" - global-dirs@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201" @@ -6742,18 +6546,6 @@ globby@^11.0.1: merge2 "^1.3.0" slash "^3.0.0" -globby@^11.0.2: - version "11.0.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.2.tgz#1af538b766a3b540ebfb58a32b2e2d5897321d83" - integrity sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.1.1" - ignore "^5.1.4" - merge2 "^1.3.0" - slash "^3.0.0" - globby@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" @@ -6777,19 +6569,6 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -globby@^8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" - integrity sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w== - dependencies: - array-union "^1.0.1" - dir-glob "2.0.0" - fast-glob "^2.0.2" - glob "^7.1.2" - ignore "^3.3.5" - pify "^3.0.0" - slash "^1.0.0" - globule@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.0.tgz#41d0e9fb44afd4b80d93a23263714f90b3dec904" @@ -6813,7 +6592,7 @@ google-auth-library@^5.0.0, google-auth-library@^5.5.0: jws "^4.0.0" lru-cache "^5.0.0" -google-gax@^1.11.1, google-gax@^1.14.2: +google-gax@^1.14.2: version "1.15.3" resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-1.15.3.tgz#e88cdcbbd19c7d88cc5fd7d7b932c4d1979a5aca" integrity sha512-3JKJCRumNm3x2EksUTw4P1Rad43FTpqrtW9jzpf3xSMYXx+ogaqTM1vGo7VixHB4xkAyATXVIa3OcNSh8H9zsQ== @@ -6861,23 +6640,6 @@ google-p12-pem@^2.0.0: dependencies: node-forge "^0.9.0" -got@^6.3.0, got@^6.7.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" - integrity sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA= - dependencies: - create-error-class "^3.0.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-redirect "^1.0.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - lowercase-keys "^1.0.0" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - unzip-response "^2.0.1" - url-parse-lax "^1.0.0" - got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -6905,11 +6667,6 @@ graceful-fs@^4.2.4: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - gtoken@^4.1.0: version "4.1.4" resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-4.1.4.tgz#925ff1e7df3aaada06611d30ea2d2abf60fcd6a7" @@ -6945,18 +6702,6 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" -has-binary2@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" - integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== - dependencies: - isarray "2.0.1" - -has-cors@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" - integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= - has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" @@ -7041,13 +6786,6 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" -hast-util-embedded@^1.0.0: - version "1.0.6" - resolved "https://registry.yarnpkg.com/hast-util-embedded/-/hast-util-embedded-1.0.6.tgz#ea7007323351cc43e19e1d6256b7cde66ad1aa03" - integrity sha512-JQMW+TJe0UAIXZMjCJ4Wf6ayDV9Yv3PBDPsHD4ExBpAspJ6MOcCX+nzVF+UJVv7OqPcg852WEMSHQPoRA+FVSw== - dependencies: - hast-util-is-element "^1.1.0" - hast-util-from-parse5@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-4.0.2.tgz#b7164a7ffc88da4f751dc7c2f801ff8d7c143bab" @@ -7075,24 +6813,11 @@ hast-util-has-property@^1.0.0: resolved "https://registry.yarnpkg.com/hast-util-has-property/-/hast-util-has-property-1.0.3.tgz#568bb8d3048483797b21d5d409eae43d89135a0f" integrity sha512-tT3ffSnrBu38RKnjn27n9vi+h/CUEXCQP5O2mriji4NNI2QNnhAqefjOg5ORAyvVfJItn0SC2Sx4CHReZSYh3g== -hast-util-is-body-ok-link@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-1.0.4.tgz#49ab2fad52ef04fe70adcbc95c9fc3a6358c32be" - integrity sha512-mFblNpLvFbD8dG2Nw5dJBYZkxIHeph1JAh5yr4huI7T5m8cV0zaXNiqzKPX/JdjA+tIDF7c33u9cxK132KRjyQ== - dependencies: - hast-util-has-property "^1.0.0" - hast-util-is-element "^1.0.0" - hast-util-is-element@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-1.0.3.tgz#423b4b26fe8bf1f25950fe052e9ce8f83fd5f6a4" integrity sha512-C62CVn7jbjp89yOhhy7vrkSaB7Vk906Gtcw/Ihd+Iufnq+2pwOZjdPmpzpKLWJXPJBMDX3wXg4FqmdOayPcewA== -hast-util-is-element@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz#3b3ed5159a2707c6137b48637fbfe068e175a425" - integrity sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ== - hast-util-parse-selector@^2.0.0, hast-util-parse-selector@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.3.tgz#57edd449103900c7f63fd9e6f694ffd7e4634719" @@ -7121,22 +6846,6 @@ hast-util-to-html@^4.0.0: unist-util-is "^2.0.0" xtend "^4.0.1" -hast-util-to-html@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-5.0.1.tgz#74cb518b3612b9f1aeb92fd29478c7a092f33953" - integrity sha512-zPuZuclFMJAP50wi9FoP3fcjOWmP3A9CqiPiR1nqOoweYDB4Urc2tg4ZITeDOrOjUQG5zArGopeGDuu3+vkGQg== - dependencies: - ccount "^1.0.0" - comma-separated-tokens "^1.0.1" - hast-util-is-element "^1.0.0" - hast-util-whitespace "^1.0.0" - html-void-elements "^1.0.0" - property-information "^5.0.0" - space-separated-tokens "^1.0.0" - stringify-entities "^1.0.1" - unist-util-is "^2.0.0" - xtend "^4.0.1" - hast-util-to-html@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-6.1.0.tgz#86bcd19c3bd46af456984f8f34db16298c2b10b0" @@ -7153,21 +6862,6 @@ hast-util-to-html@^6.0.0: unist-util-is "^3.0.0" xtend "^4.0.1" -hast-util-to-mdast@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/hast-util-to-mdast/-/hast-util-to-mdast-4.0.0.tgz#835b0d75febc34d95d83cd0c72b349385768db8f" - integrity sha512-3mHEoA5UF0imbdSFR6hhKKzDLF4m5G4nlNziAYS7wdN0ySetgAZT/5moFjWaYfnxPnY/3Ml5li7/ika2cVL+qg== - dependencies: - hast-util-has-property "^1.0.0" - hast-util-is-element "^1.0.0" - hast-util-to-string "^1.0.0" - mdast-util-phrasing "^1.0.0" - mdast-util-to-string "^1.0.4" - rehype-minify-whitespace "^2.0.3" - trim-trailing-lines "^1.1.0" - unist-util-visit "^1.1.1" - xtend "^4.0.1" - hast-util-to-string@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/hast-util-to-string/-/hast-util-to-string-1.0.2.tgz#fcf6d46bde2a50a1fbcaf6ed238971a51b622eac" @@ -7198,16 +6892,6 @@ hastscript@^5.0.0: property-information "^5.0.0" space-separated-tokens "^1.0.0" -he@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" - integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= - -he@1.2.x, he@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - header-case@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/header-case/-/header-case-1.0.1.tgz#9535973197c144b09613cd65d317ef19963bd02d" @@ -7221,11 +6905,6 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== -highlight.js@^10.0.0: - version "10.5.0" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.5.0.tgz#3f09fede6a865757378f2d9ebdcbc15ba268f98f" - integrity sha512-xTmvd9HiIHR6L53TMC7TKolEj65zG1XU+Onr8oi86mYa+nLcIbxTTWkpW7CsEwv/vK7u1zb8alZIMLDqqN6KTw== - hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -7245,11 +6924,6 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== -hosted-git-info@^2.7.1: - version "2.8.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" - integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== - hosted-git-info@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.2.tgz#8b7e3bd114b59b51786f8bade0f39ddc80275a97" @@ -7289,7 +6963,7 @@ html-comment-regex@^1.1.0: resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== -html-encoding-sniffer@^1.0.2: +html-encoding-sniffer@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== @@ -7306,34 +6980,11 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491" integrity sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig== -html-minifier@^3.5.21: - version "3.5.21" - resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c" - integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA== - dependencies: - camel-case "3.0.x" - clean-css "4.2.x" - commander "2.17.x" - he "1.2.x" - param-case "2.1.x" - relateurl "0.2.x" - uglify-js "3.4.x" - -html-tags@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" - integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== - html-void-elements@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w== -html-whitespace-sensitive-tag-names@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-1.0.3.tgz#60325c5bd331048d14ced6bac419c89d76cc9dd8" - integrity sha512-GX1UguduCBEAEo1hjFxc2Bz04/sDq0ACNyT7LsuoDcPfXYI3nS0NRPp3dyazLJyVUMp3GPBB56i/0Zr6CqD2PQ== - html@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/html/-/html-1.0.0.tgz#a544fa9ea5492bfb3a2cca8210a10be7b5af1f61" @@ -7358,12 +7009,7 @@ htmlparser2@^3.7.3: inherits "^2.0.1" readable-stream "^3.1.1" -http-cache-semantics@^3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" - integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== - -http-cache-semantics@^4.0.0: +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== @@ -7415,15 +7061,12 @@ http-link-header@^0.8.0: resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q= -http-proxy-agent@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" - integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== - dependencies: - agent-base "4" - debug "3.1.0" +http-parser-js@>=0.5.1: + version "0.5.3" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9" + integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg== -http-proxy-agent@^4.0.0: +http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== @@ -7451,7 +7094,7 @@ http-proxy@^1.17.0: follow-redirects "^1.0.0" requires-port "^1.0.0" -http-proxy@^1.18.1, http-proxy@^1.8.1: +http-proxy@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== @@ -7460,27 +7103,6 @@ http-proxy@^1.18.1, http-proxy@^1.8.1: follow-redirects "^1.0.0" requires-port "^1.0.0" -http-server-spa@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/http-server-spa/-/http-server-spa-1.3.0.tgz#2892c0ade750e1c3826b3589744e1c17c46aa6a5" - integrity sha512-NfXBksDzoiBOo1IrMDtxpKJ8FOHLqy0YdijYjqMoRcS7AWPf6MzhRvKe2KiXxENlqTRqkOH418SvbxC6GzG2TA== - dependencies: - mime "^1.3.4" - -http-server@^0.11.1: - version "0.11.1" - resolved "https://registry.yarnpkg.com/http-server/-/http-server-0.11.1.tgz#2302a56a6ffef7f9abea0147d838a5e9b6b6a79b" - integrity sha512-6JeGDGoujJLmhjiRGlt8yK8Z9Kl0vnl/dQoQZlc4oeqaUoAKQg94NILLfrY3oWzSyFaQCVNTcKE5PZ3cH8VP9w== - dependencies: - colors "1.0.3" - corser "~2.0.0" - ecstatic "^3.0.0" - http-proxy "^1.8.1" - opener "~1.4.0" - optimist "0.6.x" - portfinder "^1.0.13" - union "~0.4.3" - http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -7495,7 +7117,15 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -https-proxy-agent@^2.2.1, https-proxy-agent@^2.2.3: +https-proxy-agent@5.0.0, https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +https-proxy-agent@^2.2.1: version "2.2.4" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== @@ -7511,14 +7141,6 @@ https-proxy-agent@^4.0.0: agent-base "5" debug "4" -https-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== - dependencies: - agent-base "6" - debug "4" - humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -7526,7 +7148,7 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@~0.4.11, iconv-lite@~0.4.13: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.11: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -7545,11 +7167,6 @@ icss-utils@^5.0.0: resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.0.0.tgz#03ed56c3accd32f9caaf1752ebf64ef12347bb84" integrity sha512-aF2Cf/CkEZrI/vsu5WI/I+akFgdbwQHVE9YRZxATrhH4PVIe6a3BIjwjEcW+z+jP/hNh+YvM3lAAn1wJQ6opSg== -ieee754@^1.1.13: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - ieee754@^1.1.4: version "1.1.13" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" @@ -7560,14 +7177,14 @@ iferr@^0.1.5: resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= -ignore-walk@^3.0.1: +ignore-walk@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== dependencies: minimatch "^3.0.4" -ignore@^3.2.0, ignore@^3.3.3, ignore@^3.3.5: +ignore@^3.2.0, ignore@^3.3.3: version "3.3.10" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== @@ -7651,11 +7268,6 @@ indexes-of@^1.0.1: resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" - integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= - infer-owner@^1.0.3, infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -7684,12 +7296,17 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@1.3.5, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + +ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== -inquirer@7.3.3: +inquirer@7.3.3, inquirer@^7.3.3: version "7.3.3" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== @@ -7727,26 +7344,6 @@ inquirer@^0.12.0: strip-ansi "^3.0.0" through "^2.3.6" -inquirer@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" - integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ== - dependencies: - ansi-escapes "^3.0.0" - chalk "^2.0.0" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^2.0.4" - figures "^2.0.0" - lodash "^4.3.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rx-lite "^4.0.8" - rx-lite-aggregates "^4.0.8" - string-width "^2.1.0" - strip-ansi "^4.0.0" - through "^2.3.6" - inquirer@~6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.3.1.tgz#7a413b5e7950811013a3db491c61d1f3b776e8e7" @@ -7813,17 +7410,12 @@ invert-kv@^1.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= -invert-kv@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" - integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== - ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= -ip@1.1.5, ip@^1.1.0, ip@^1.1.5: +ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= @@ -7848,14 +7440,6 @@ is-absolute-url@^3.0.3: resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== -is-absolute@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" - integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== - dependencies: - is-relative "^1.0.0" - is-windows "^1.0.1" - is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -7932,13 +7516,6 @@ is-callable@^1.1.4, is-callable@^1.1.5: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== -is-ci@^1.0.10: - version "1.2.1" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" - integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg== - dependencies: - ci-info "^1.5.0" - is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -7958,10 +7535,10 @@ is-color-stop@^1.0.0: rgb-regex "^1.0.1" rgba-regex "^1.0.0" -is-core-module@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.1.0.tgz#a4cc031d9b1aca63eecbd18a650e13cb4eeab946" - integrity sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA== +is-core-module@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" + integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== dependencies: has "^1.0.3" @@ -8070,21 +7647,6 @@ is-hexadecimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== -is-html@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-html/-/is-html-2.0.0.tgz#b3ab2e27ccb7a12235448f51f115a6690f435fc8" - integrity sha512-S+OpgB5i7wzIue/YSE5hg0e5ZYfG3hhpNh9KGl6ayJ38p7ED6wxQLd1TV91xHpcTvw90KMJ9EwN3F/iNflHBVg== - dependencies: - html-tags "^3.0.0" - -is-installed-globally@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" - integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA= - dependencies: - global-dirs "^0.1.0" - is-path-inside "^1.0.0" - is-installed-globally@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" @@ -8098,6 +7660,11 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-lambda@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= + is-lower-case@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393" @@ -8121,16 +7688,6 @@ is-my-json-valid@^2.10.0: jsonpointer "^4.0.0" xtend "^4.0.0" -is-negated-glob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" - integrity sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI= - -is-npm@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" - integrity sha1-8vtjpl5JBbQGyGBydloaTceTufQ= - is-npm@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" @@ -8223,11 +7780,6 @@ is-property@^1.0.0, is-property@^1.0.2: resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= -is-redirect@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" - integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= - is-regex@^1.0.4, is-regex@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" @@ -8235,29 +7787,17 @@ is-regex@^1.0.4, is-regex@^1.0.5: dependencies: has "^1.0.3" -is-relative@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" - integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== - dependencies: - is-unc-path "^1.0.0" - is-resolvable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== -is-retry-allowed@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" - integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== - is-stream-ended@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-stream-ended/-/is-stream-ended-0.1.4.tgz#f50224e95e06bce0e356d440a4827cd35b267eda" integrity sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw== -is-stream@^1.0.0, is-stream@^1.1.0: +is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -8286,13 +7826,6 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-unc-path@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" - integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== - dependencies: - unc-path-regex "^0.1.2" - is-upper-case@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f" @@ -8305,12 +7838,17 @@ is-url@^1.2.2: resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== +is-what@^3.12.0: + version "3.14.1" + resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1" + integrity sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA== + is-whitespace-character@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== -is-windows@^1.0.1, is-windows@^1.0.2: +is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== @@ -8351,11 +7889,6 @@ is2@2.0.1: ip-regex "^2.1.0" is-url "^1.2.2" -is@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/is/-/is-3.3.0.tgz#61cff6dd3c4193db94a3d62582072b44e5645d79" - integrity sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg== - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -8366,11 +7899,6 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= -isarray@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" - integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= - isbinaryfile@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.6.tgz#edcb62b224e2b4710830b67498c8e4e5a4d2610b" @@ -8478,16 +8006,7 @@ jasminewd2@^2.1.0: resolved "https://registry.yarnpkg.com/jasminewd2/-/jasminewd2-2.2.0.tgz#e37cf0b17f199cce23bea71b2039395246b4ec4e" integrity sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4= -jest-worker@26.5.0: - version "26.5.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.5.0.tgz#87deee86dbbc5f98d9919e0dadf2c40e3152fa30" - integrity sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^7.0.0" - -jest-worker@^26.5.0: +jest-worker@26.6.2, jest-worker@^26.5.0: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== @@ -8510,15 +8029,15 @@ join-path@^1.1.1: url-join "0.0.1" valid-url "^1" -jpeg-js@0.1.2, jpeg-js@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.1.2.tgz#135b992c0575c985cfa0f494a3227ed238583ece" - integrity sha1-E1uZLAV1yYXPoPSUoyJ+0jhYPs4= +jpeg-js@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.2.tgz#8b345b1ae4abde64c2da2fe67ea216a114ac279d" + integrity sha512-+az2gi/hvex7eLTMTlbRLOhH6P6WFdk2ITI8HJsaH2VqYO0I594zXSYEP+tf4FW+8Cy68ScDXoAsQdyQanv3sw== -js-library-detector@^5.7.0: - version "5.9.0" - resolved "https://registry.yarnpkg.com/js-library-detector/-/js-library-detector-5.9.0.tgz#ad0add7f4991424326895b273e774876555d0e1b" - integrity sha512-0wYHRVJv8uVsylJhfQQaH2vOBYGehyZyJbtaHuchoTP3Mb6hqYvrA0hoMQ1ZhARLHzHJMbMc/nCr4D3pTNSCgw== +js-library-detector@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/js-library-detector/-/js-library-detector-6.4.0.tgz#63e165cb84a4a0a7f7bbf1e97d60623921baae14" + integrity sha512-NB2sYpmgqiTd7PNNhgp6bnEZmjvTUdAbzxABvYXWLpTL/t158T6mPnD8uYNd0FDP73YWyMrTYDvPxqdvCTbv2g== js-tokens@^3.0.2: version "3.0.2" @@ -8538,50 +8057,35 @@ js-yaml@^3.13.1, js-yaml@^3.5.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^3.14.0: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdom@^13.1.0: - version "13.2.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-13.2.0.tgz#b1a0dbdadc255435262be8ea3723d2dba0d7eb3a" - integrity sha512-cG1NtMWO9hWpqRNRR3dSvEQa8bFI6iLlqU2x4kwX51FQjp0qus8T9aBaAO6iGp3DeBrhdwuKxckknohkmfvsFw== +jsdom@^9.12.0: + version "9.12.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.12.0.tgz#e8c546fffcb06c00d4833ca84410fed7f8a097d4" + integrity sha1-6MVG//ywbADUgzyoRBD+1/igl9Q= dependencies: - abab "^2.0.0" - acorn "^6.0.4" - acorn-globals "^4.3.0" + abab "^1.0.3" + acorn "^4.0.4" + acorn-globals "^3.1.0" array-equal "^1.0.0" - cssom "^0.3.4" - cssstyle "^1.1.1" - data-urls "^1.1.0" - domexception "^1.0.1" - escodegen "^1.11.0" - html-encoding-sniffer "^1.0.2" - nwsapi "^2.0.9" - parse5 "5.1.0" - pn "^1.1.0" - request "^2.88.0" - request-promise-native "^1.0.5" - saxes "^3.1.5" - symbol-tree "^3.2.2" - tough-cookie "^2.5.0" - w3c-hr-time "^1.0.1" - w3c-xmlserializer "^1.0.1" - webidl-conversions "^4.0.2" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^7.0.0" - ws "^6.1.2" - xml-name-validator "^3.0.0" + content-type-parser "^1.0.1" + cssom ">= 0.3.2 < 0.4.0" + cssstyle ">= 0.2.37 < 0.3.0" + escodegen "^1.6.1" + html-encoding-sniffer "^1.0.1" + nwmatcher ">= 1.3.9 < 2.0.0" + parse5 "^1.5.1" + request "^2.79.0" + sax "^1.2.1" + symbol-tree "^3.2.1" + tough-cookie "^2.3.2" + webidl-conversions "^4.0.0" + whatwg-encoding "^1.0.1" + whatwg-url "^4.3.0" + xml-name-validator "^2.0.1" jsesc@^2.5.1: version "2.5.2" @@ -8605,7 +8109,7 @@ json-buffer@3.0.0: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= -json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: +json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== @@ -8651,7 +8155,7 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json3@^3.3.2: +json3@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== @@ -8677,10 +8181,10 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" -jsonc-parser@2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" - integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== +jsonc-parser@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22" + integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA== jsonfile@^2.1.0: version "2.4.0" @@ -8721,7 +8225,7 @@ jsonlint-mod@^1.7.5: chalk "^2.4.2" underscore "^1.9.1" -jsonparse@^1.2.0: +jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= @@ -8844,35 +8348,34 @@ karma-source-map-support@1.4.0: dependencies: source-map-support "^0.5.5" -karma@~5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/karma/-/karma-5.1.1.tgz#4e472c1e5352d73edbd2090726afdb01d7869d72" - integrity sha512-xAlOr5PMqUbiKXSv5PCniHWV3aiwj6wIZ0gUVcwpTCPVQm/qH2WAMFWxtnpM6KJqhkRWrIpovR4Rb0rn8GtJzQ== +karma@~6.1.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/karma/-/karma-6.1.1.tgz#a7539618cca0f2cbb26d5497120ec31fe340c2a1" + integrity sha512-vVDFxFGAsclgmFjZA/qGw5xqWdZIWxVD7xLyCukYUYd5xs/uGzYbXGOT5zOruVBQleKEmXIr4H2hzGCTn+M9Cg== dependencies: body-parser "^1.19.0" braces "^3.0.2" - chokidar "^3.0.0" + chokidar "^3.4.2" colors "^1.4.0" connect "^3.7.0" di "^0.0.1" dom-serialize "^2.2.1" - flatted "^2.0.2" glob "^7.1.6" graceful-fs "^4.2.4" http-proxy "^1.18.1" isbinaryfile "^4.0.6" - lodash "^4.17.15" + lodash "^4.17.19" log4js "^6.2.1" mime "^2.4.5" minimatch "^3.0.4" qjobs "^1.2.0" range-parser "^1.2.1" rimraf "^3.0.2" - socket.io "^2.3.0" + socket.io "^3.1.0" source-map "^0.6.1" tmp "0.2.1" - ua-parser-js "0.7.21" - yargs "^15.3.1" + ua-parser-js "^0.7.23" + yargs "^16.1.1" keyv@^3.0.0: version "3.1.0" @@ -8927,13 +8430,6 @@ kuler@^2.0.0: resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== -latest-version@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" - integrity sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU= - dependencies: - package-json "^4.0.0" - latest-version@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" @@ -8955,27 +8451,22 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" -lcid@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" - integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== - dependencies: - invert-kv "^2.0.0" - -less-loader@7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-7.0.2.tgz#0d73a49ec32a9d3ff12614598e6e2b47fb2a35c4" - integrity sha512-7MKlgjnkCf63E3Lv6w2FvAEgLMx3d/tNBExITcanAq7ys5U8VPWT3F6xcRjYmdNfkoQ9udoVFb1r2azSiTnD6w== +less-loader@7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-7.3.0.tgz#f9d6d36d18739d642067a05fb5bd70c8c61317e5" + integrity sha512-Mi8915g7NMaLlgi77mgTTQvK022xKRQBIVDSyfl3ErTuBhmZBQab0mjeJjNNqGbdR+qrfTleKXqbGI4uEFavxg== dependencies: klona "^2.0.4" loader-utils "^2.0.0" schema-utils "^3.0.0" -less@3.12.2: - version "3.12.2" - resolved "https://registry.yarnpkg.com/less/-/less-3.12.2.tgz#157e6dd32a68869df8859314ad38e70211af3ab4" - integrity sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q== +less@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/less/-/less-4.1.1.tgz#15bf253a9939791dc690888c3ff424f3e6c7edba" + integrity sha512-w09o8tZFPThBscl5d0Ggp3RcrKIouBoQscnOMgFH3n5V3kN/CXGHNfCkRPtxJk6nKryDXaV9aHLK55RXuH4sAw== dependencies: + copy-anything "^2.0.1" + parse-node-version "^1.0.1" tslib "^1.10.0" optionalDependencies: errno "^0.1.1" @@ -8983,7 +8474,7 @@ less@3.12.2: image-size "~0.5.0" make-dir "^2.1.0" mime "^1.4.1" - native-request "^1.0.5" + needle "^2.5.2" source-map "~0.6.0" leven@^3.1.0: @@ -8999,10 +8490,10 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -license-webpack-plugin@2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.1.tgz#08eddb2f776c7c64c02f308a00e017d6e824d0b6" - integrity sha512-yhqTmlYIEpZWA122lf6E0G8+rkn0AzoQ1OpzUKKs/lXUqG1plmGnwmkuuPlfggzJR5y6DLOdot/Tv00CC51CeQ== +license-webpack-plugin@2.3.11: + version "2.3.11" + resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.11.tgz#0d93188a31fce350a44c86212badbaf33dcd29d8" + integrity sha512-0iVGoX5vx0WDy8dmwTTpOOMYiGqILyUbDeVMFH52AjgBlS58lHwOlFMSoqg5nY8Kxl6+FRKyUZY/UdlQaOyqDw== dependencies: "@types/webpack-sources" "^0.1.5" webpack-sources "^1.2.0" @@ -9042,50 +8533,50 @@ lighthouse-logger@^1.0.0, lighthouse-logger@^1.2.0: debug "^2.6.8" marky "^1.2.0" -lighthouse@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/lighthouse/-/lighthouse-6.1.0.tgz#1c238049821e62eddd34c7371ab9c3222e2dfdef" - integrity sha512-j/tPSbxSkQRSwyIAjYUNIdarieR1cw8qDYIASsmtIcegCnMrhn5FM7hggcOTFlenDCEIOfv7JlQYV6UVfE4wZg== +lighthouse-stack-packs@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/lighthouse-stack-packs/-/lighthouse-stack-packs-1.4.0.tgz#bf98e0fb04a091ec2d73648842698b41070968ef" + integrity sha512-wdv94WUjaqUwtW8DOapng45Yah62c5O5geNVeoSQlnoagfbTO/YbiwNlfzDIF1xNKRkPlsfr/oWHhXsaHXDivg== + +lighthouse@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/lighthouse/-/lighthouse-7.2.0.tgz#3368e94804b751586a48e95f39a71c68bee9c4d9" + integrity sha512-f2FLaJIHVACPUHK13Wl0EAvc+8vjSNf3+zjHM9h/WM+qZKpP2FkPOVpWnWs+TjAfsUG3C1alYtG2bzdD6qGmxA== dependencies: - axe-core "3.5.5" - chrome-launcher "^0.13.3" - configstore "^3.1.1" + axe-core "4.1.2" + chrome-launcher "^0.13.4" + configstore "^5.0.1" cssstyle "1.2.1" details-element-polyfill "^2.4.0" http-link-header "^0.8.0" - inquirer "^3.3.0" + inquirer "^7.3.3" intl "^1.2.5" intl-messageformat "^4.4.0" intl-pluralrules "^1.0.3" - jpeg-js "0.1.2" - js-library-detector "^5.7.0" + jpeg-js "^0.4.1" + js-library-detector "^6.4.0" jsonld "^1.5.0" jsonlint-mod "^1.7.5" lighthouse-logger "^1.2.0" + lighthouse-stack-packs "^1.4.0" + lodash.get "^4.4.2" lodash.isequal "^4.5.0" lodash.set "^4.3.2" lookup-closest-locale "6.0.4" metaviewport-parser "0.2.0" open "^6.4.0" parse-cache-control "1.0.1" + ps-list "^7.2.0" raven "^2.2.1" rimraf "^2.6.1" robots-parser "^2.0.1" semver "^5.3.0" - speedline-core "1.4.2" - third-party-web "^0.11.1" - update-notifier "^2.5.0" + speedline-core "^1.4.3" + third-party-web "^0.12.2" + update-notifier "^4.1.0" ws "3.3.2" - yargs "3.32.0" - yargs-parser "^18.1.3" - -line-column@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/line-column/-/line-column-1.0.2.tgz#d25af2936b6f4849172b312e4792d1d987bc34a2" - integrity sha1-0lryk2tvSEkXKzEuR5LR2Ye8NKI= - dependencies: - isarray "^1.0.0" - isobject "^2.0.0" + yargs "^16.1.1" + yargs-parser "^20.2.4" lines-and-columns@^1.1.6: version "1.1.6" @@ -9139,14 +8630,6 @@ loader-utils@^1.4.0: emojis-list "^3.0.0" json5 "^1.0.1" -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -9204,6 +8687,11 @@ lodash.flatten@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= + lodash.has@^4.5.2: version "4.5.2" resolved "https://registry.yarnpkg.com/lodash.has/-/lodash.has-4.5.2.tgz#d19f4dc1095058cccbe2b0cdf4ee0fe4aa37c862" @@ -9295,11 +8783,6 @@ lodash.snakecase@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" integrity sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40= -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= - lodash.toarray@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" @@ -9327,11 +8810,16 @@ lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.1 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20: +lodash@^4.17.19: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" @@ -9458,13 +8946,6 @@ magic-string@^0.25.0: dependencies: sourcemap-codec "^1.4.4" -make-dir@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" - integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== - dependencies: - pify "^3.0.0" - make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -9473,7 +8954,7 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -make-dir@^3.0.0: +make-dir@^3.0.0, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -9492,35 +8973,32 @@ make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== -make-fetch-happen@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz#aa8387104f2687edca01c8687ee45013d02d19bd" - integrity sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag== +make-fetch-happen@^8.0.9: + version "8.0.14" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz#aaba73ae0ab5586ad8eaa68bd83332669393e222" + integrity sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ== dependencies: - agentkeepalive "^3.4.1" - cacache "^12.0.0" - http-cache-semantics "^3.8.1" - http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.3" - lru-cache "^5.1.1" - mississippi "^3.0.0" - node-fetch-npm "^2.0.2" - promise-retry "^1.1.1" - socks-proxy-agent "^4.0.0" - ssri "^6.0.0" + agentkeepalive "^4.1.3" + cacache "^15.0.5" + http-cache-semantics "^4.1.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^6.0.0" + minipass "^3.1.3" + minipass-collect "^1.0.2" + minipass-fetch "^1.3.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + promise-retry "^2.0.1" + socks-proxy-agent "^5.0.0" + ssri "^8.0.0" make-plural@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-6.0.1.tgz#ed3839fac3f469ebbe505751d48fe3319769edfc" integrity sha512-h0uBNi4tpDkiWUyYKrJNj8Kif6q3Ba5zp/8jnfPy3pQE+4XcTj6h3eZM5SYVUyDNX9Zk69Isr/dx0I+78aJUaQ== -map-age-cleaner@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" - integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== - dependencies: - p-defer "^1.0.0" - map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -9597,13 +9075,6 @@ mdast-util-definitions@^1.2.0: dependencies: unist-util-visit "^1.0.0" -mdast-util-phrasing@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-1.0.3.tgz#f71626caa78394a748a082ecae084de17af5520f" - integrity sha512-b1Ar28MjmPMMnTDUApnL1AUJY1L/KmBg5+iBLMd8/+0JqXh1sENow9+wj8Mp/SZBYtMlGRUQ1PkBWinPEDVeNQ== - dependencies: - unist-util-is "^3.0.0" - mdast-util-to-hast@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-3.0.4.tgz#132001b266031192348d3366a6b011f28e54dc40" @@ -9621,28 +9092,6 @@ mdast-util-to-hast@^3.0.0: unist-util-visit "^1.1.0" xtend "^4.0.1" -mdast-util-to-hast@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-4.0.0.tgz#d8467ce28ea73b4648667bc389aa39dfa9f67f18" - integrity sha512-yOTZSxR1aPvWRUxVeLaLZ1sCYrK87x2Wusp1bDM/Ao2jETBhYUKITI3nHvgy+HkZW54HuCAhHnS0mTcbECD5Ig== - dependencies: - collapse-white-space "^1.0.0" - detab "^2.0.0" - mdast-util-definitions "^1.2.0" - mdurl "^1.0.1" - trim "0.0.1" - trim-lines "^1.0.0" - unist-builder "^1.0.1" - unist-util-generated "^1.1.0" - unist-util-position "^3.0.0" - unist-util-visit "^1.1.0" - xtend "^4.0.1" - -mdast-util-to-string@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" - integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== - mdn-data@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" @@ -9658,15 +9107,6 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -mem@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" - integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== - dependencies: - map-age-cleaner "^0.1.1" - mimic-fn "^2.0.0" - p-is-promise "^2.0.0" - memoizee@^0.4.14: version "0.4.14" resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.14.tgz#07a00f204699f9a95c2d9e77218271c7cd610d57" @@ -9781,12 +9221,12 @@ mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.17, mime-types@~2.1.19, dependencies: mime-db "1.43.0" -mime@1.6.0, mime@^1.3.4, mime@^1.4.1, mime@^1.6.0: +mime@1.6.0, mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.0.3, mime@^2.2.0, mime@^2.4.4: +mime@^2.2.0, mime@^2.4.4: version "2.4.4" resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== @@ -9801,7 +9241,7 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== -mimic-fn@^2.0.0, mimic-fn@^2.1.0: +mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== @@ -9811,15 +9251,10 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== -mimic-response@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" - integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== - -mini-css-extract-plugin@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.2.1.tgz#30ea7dee632b3002b0c77aeed447790408cb247e" - integrity sha512-G3yw7/TQaPfkuiR73MDcyiqhyP8SnbmLhUbpC76H+wtQxA6wfKhMCQOCb6wnPK0dQbjORAeOILQqEesg4/wF7A== +mini-css-extract-plugin@1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.5.tgz#252166e78879c106e0130f229d44e0cbdfcebed3" + integrity sha512-tvmzcwqJJXau4OQE5vT72pRT18o2zF+tQJp8CWchqvfQnTlflkzS+dANYcRdyPRWUWRkfmeNTKltx0NZI/b5dQ== dependencies: loader-utils "^2.0.0" schema-utils "^3.0.0" @@ -9852,7 +9287,7 @@ minimist@^1.1.0, minimist@^1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -minimist@^1.2.3, minimist@^1.2.5: +minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== @@ -9869,6 +9304,17 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" +minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.3.3.tgz#34c7cea038c817a8658461bf35174551dce17a0a" + integrity sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ== + dependencies: + minipass "^3.1.0" + minipass-sized "^1.0.3" + minizlib "^2.0.0" + optionalDependencies: + encoding "^0.1.12" + minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" @@ -9876,6 +9322,14 @@ minipass-flush@^1.0.5: dependencies: minipass "^3.0.0" +minipass-json-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7" + integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg== + dependencies: + jsonparse "^1.3.1" + minipass "^3.0.0" + minipass-pipeline@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz#3dcb6bb4a546e32969c7ad710f2c79a86abba93a" @@ -9883,7 +9337,21 @@ minipass-pipeline@^1.2.2: dependencies: minipass "^3.0.0" -minipass@^2.3.5, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: +minipass-pipeline@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass-sized@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== + dependencies: + minipass "^3.0.0" + +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== @@ -9898,6 +9366,13 @@ minipass@^3.0.0, minipass@^3.1.1: dependencies: yallist "^4.0.0" +minipass@^3.1.0, minipass@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + minizlib@^1.2.1: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" @@ -9905,18 +9380,18 @@ minizlib@^1.2.1: dependencies: minipass "^2.9.0" -minizlib@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3" - integrity sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA== +minizlib@^2.0.0, minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== dependencies: minipass "^3.0.0" yallist "^4.0.0" -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== +minizlib@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3" + integrity sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA== dependencies: minipass "^3.0.0" yallist "^4.0.0" @@ -9945,19 +9420,19 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: +mkdirp-classic@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" -mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5: +mkdirp@^0.5.3: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -9974,23 +9449,6 @@ mkdirp@^1.0.4, mkdirp@~1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mocha@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" - integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== - dependencies: - browser-stdout "1.3.1" - commander "2.15.1" - debug "3.1.0" - diff "3.5.0" - escape-string-regexp "1.0.5" - glob "7.1.2" - growl "1.10.5" - he "1.1.1" - minimatch "3.0.4" - mkdirp "0.5.1" - supports-color "5.4.0" - morgan@^1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" @@ -10053,17 +9511,6 @@ multicast-dns@^6.0.1: dns-packet "^1.3.1" thunky "^1.0.2" -multimatch@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" - integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA== - dependencies: - "@types/minimatch" "^3.0.3" - array-differ "^3.0.0" - array-union "^2.1.0" - arrify "^2.0.1" - minimatch "^3.0.4" - mute-stream@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" @@ -10079,15 +9526,6 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -mz@^2.4.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" - integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== - dependencies: - any-promise "^1.0.0" - object-assign "^4.0.1" - thenify-all "^1.0.0" - nan@^2.12.1: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" @@ -10098,10 +9536,10 @@ nan@^2.14.2: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== -nanoid@^3.1.16: - version "3.1.16" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.16.tgz#b21f0a7d031196faf75314d7c65d36352beeef64" - integrity sha512-+AK8MN0WHji40lj8AEuwLOvLSbWYApQpre/aFJZD71r43wVRLrOYS4FmJOPQYon1TqB462RzrrxlfA74XRES8w== +nanoid@^3.1.20: + version "3.1.20" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" + integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== nanomatch@^1.2.9: version "1.2.13" @@ -10120,11 +9558,6 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" -napi-build-utils@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" - integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== - nash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/nash/-/nash-3.0.0.tgz#bced3a0cb8434c2ad30d1a0d567cfc0c37128eea" @@ -10135,16 +9568,20 @@ nash@^3.0.0: lodash "^4.17.5" minimist "^1.1.0" -native-request@^1.0.5: - version "1.0.7" - resolved "https://registry.yarnpkg.com/native-request/-/native-request-1.0.7.tgz#ff742dc555b4c8f2f1c14b548639ba174e573856" - integrity sha512-9nRjinI9bmz+S7dgNtf4A70+/vPhnd+2krGpy4SUlADuOuSa24IDkNaZ+R/QT1wQ6S8jBdi6wE7fLekFZNfUpQ== - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +needle@^2.5.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.6.0.tgz#24dbb55f2509e2324b4a99d61f413982013ccdbe" + integrity sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -10182,13 +9619,6 @@ no-case@^2.2.0: dependencies: lower-case "^1.1.1" -node-abi@^2.7.0: - version "2.19.3" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.19.3.tgz#252f5dcab12dad1b5503b2d27eddd4733930282d" - integrity sha512-9xZrlyfvKhWme2EXFKQhZRp1yNWT/uI1luYPr3sFl+H4keYY4xR+1jO7mvTTijIsHf1M+QDe9uWuKeEpLInIlg== - dependencies: - semver "^5.4.1" - node-emoji@^1.4.1: version "1.10.0" resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" @@ -10196,36 +9626,27 @@ node-emoji@^1.4.1: dependencies: lodash.toarray "^4.4.0" -node-fetch-npm@^2.0.2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz#6507d0e17a9ec0be3bec516958a497cec54bf5a4" - integrity sha512-iOuIQDWDyjhv9qSDrj9aq/klt6F9z1p2otB3AV7v3zBDcL/x+OfGsvGQZZCcMZbUf4Ujw1xGNQkjvGnVT22cKg== - dependencies: - encoding "^0.1.11" - json-parse-better-errors "^1.0.0" - safe-buffer "^5.1.1" - -node-fetch@^2.2.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - node-fetch@^2.3.0, node-fetch@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== -node-forge@0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" - integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== +node-fetch@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-forge@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" + integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== node-forge@^0.9.0, node-forge@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.1.tgz#775368e6846558ab6676858a4d8c6e8d16c677b5" integrity sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ== -node-gyp@^7.1.2: +node-gyp@^7.1.0, node-gyp@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== @@ -10284,20 +9705,15 @@ node-releases@^1.1.52: dependencies: semver "^6.3.0" -node-releases@^1.1.53: - version "1.1.54" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.54.tgz#0a6b3916a7ddfd9656983fcde82cf38d1dbe1a6f" - integrity sha512-tLzytKpgwKQr37yw9CEODjNM9lnmsNxzlv575GzOZ16AgMvPcJis/DgrJX4UEV1KIYoXk6XoVfY6YaMOPJESAQ== - node-releases@^1.1.65: version "1.1.66" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.66.tgz#609bd0dc069381015cd982300bae51ab4f1b1814" integrity sha512-JHEQ1iWPGK+38VLB2H9ef2otU4l8s3yAMt9Xf934r6+ojCYDMHPMqvCc9TnzfeFSP1QEOeU6YZEd3+De0LTCgg== -noop-logger@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" - integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= +node-releases@^1.1.70: + version "1.1.71" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" + integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== nopt@^5.0.0: version "5.0.0" @@ -10306,7 +9722,7 @@ nopt@^5.0.0: dependencies: abbrev "1" -normalize-package-data@^2.3.2, normalize-package-data@^2.4.0: +normalize-package-data@^2.3.2: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -10343,7 +9759,7 @@ normalize-url@^4.1.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== -npm-bundled@^1.0.1: +npm-bundled@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== @@ -10357,7 +9773,7 @@ npm-install-checks@^4.0.0: dependencies: semver "^7.1.1" -npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: +npm-normalize-package-bin@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== @@ -10371,16 +9787,6 @@ npm-package-arg@8.1.0: semver "^7.0.0" validate-npm-package-name "^3.0.0" -npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.1.tgz#02168cb0a49a2b75bf988a28698de7b529df5cb7" - integrity sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg== - dependencies: - hosted-git-info "^2.7.1" - osenv "^0.1.5" - semver "^5.6.0" - validate-npm-package-name "^3.0.0" - npm-package-arg@^8.0.0: version "8.0.1" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.0.1.tgz#9d76f8d7667b2373ffda60bb801a27ef71e3e270" @@ -10390,16 +9796,26 @@ npm-package-arg@^8.0.0: semver "^7.0.0" validate-npm-package-name "^3.0.0" -npm-packlist@^1.1.12: - version "1.4.8" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" - integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== +npm-package-arg@^8.0.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.1.tgz#00ebf16ac395c63318e67ce66780a06db6df1b04" + integrity sha512-CsP95FhWQDwNqiYS+Q0mZ7FAEDytDZAkNxQqea6IaAFJTAY9Lhhqyl0irU/6PMc7BGfUmnsbHcqxJD7XuVM/rg== dependencies: - ignore-walk "^3.0.1" - npm-bundled "^1.0.1" + hosted-git-info "^3.0.6" + semver "^7.0.0" + validate-npm-package-name "^3.0.0" + +npm-packlist@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.1.4.tgz#40e96b2b43787d0546a574542d01e066640d09da" + integrity sha512-Qzg2pvXC9U4I4fLnUrBmcIT4x0woLtUgxUi9eC+Zrcv1Xx5eamytGAfbDWQ67j7xOcQ2VW1I3su9smVTIdu7Hw== + dependencies: + glob "^7.1.6" + ignore-walk "^3.0.3" + npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" -npm-pick-manifest@6.1.0: +npm-pick-manifest@6.1.0, npm-pick-manifest@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.0.tgz#2befed87b0fce956790f62d32afb56d7539c022a" integrity sha512-ygs4k6f54ZxJXrzT0x34NybRlLeZ4+6nECAIbr2i0foTnijtS1TJiyzpqtuUAJOps/hO0tNDr8fRV5g+BtRlTw== @@ -10408,27 +9824,19 @@ npm-pick-manifest@6.1.0: npm-package-arg "^8.0.0" semver "^7.0.0" -npm-pick-manifest@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz#f4d9e5fd4be2153e5f4e5f9b7be8dc419a99abb7" - integrity sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw== +npm-registry-fetch@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" + integrity sha512-PuFYYtnQ8IyVl6ib9d3PepeehcUeHN9IO5N/iCRhyg9tStQcqGQBRVHmfmMWPDERU3KwZoHFvbJ4FPXPspvzbA== dependencies: - figgy-pudding "^3.5.1" - npm-package-arg "^6.0.0" - semver "^5.4.1" - -npm-registry-fetch@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-4.0.4.tgz#2da1ecf3f43d419d96abf313664291a4623d3ea5" - integrity sha512-6jb34hX/iYNQebqWUHtU8YF6Cjb1H6ouTFPClYsyiW6lpFkljTpdeftm53rRojtja1rKAvKNIIiTS5Sjpw4wsA== - dependencies: - JSONStream "^1.3.4" - bluebird "^3.5.1" - figgy-pudding "^3.4.1" - lru-cache "^5.1.1" - make-fetch-happen "^5.0.0" - npm-package-arg "^6.1.0" - safe-buffer "^5.2.0" + "@npmcli/ci-detect" "^1.0.0" + lru-cache "^6.0.0" + make-fetch-happen "^8.0.9" + minipass "^3.1.3" + minipass-fetch "^1.3.0" + minipass-json-stream "^1.0.1" + minizlib "^2.0.0" + npm-package-arg "^8.0.0" npm-run-all@^4.1.5: version "4.1.5" @@ -10452,7 +9860,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npmlog@^4.0.1, npmlog@^4.1.2: +npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -10469,11 +9877,6 @@ nth-check@^1.0.2: dependencies: boolbase "~1.0.0" -num2fraction@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" - integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= - number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" @@ -10490,26 +9893,21 @@ nunjucks@^3.1.6: optionalDependencies: chokidar "^2.0.0" -nwsapi@^2.0.9: - version "2.2.0" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" - integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== +"nwmatcher@>= 1.3.9 < 2.0.0": + version "1.4.4" + resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.4.tgz#2285631f34a95f0d0395cd900c96ed39b58f346e" + integrity sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ== oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= -object-component@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" - integrity sha1-8MaapQ78lbhmwYb0AKM3acsvEpE= - object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" @@ -10631,10 +10029,10 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -open@7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/open/-/open-7.3.0.tgz#45461fdee46444f3645b6e14eb3ca94b82e1be69" - integrity sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw== +open@7.4.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.0.tgz#ad95b98f871d9acb0ec8fecc557082cc9986626b" + integrity sha512-PGoBCX/lclIWlpS/R2PQuIR4NJoXh6X5AwVzE7WXnWRGvHg7+4TBCgsujUgiPpm0K1y4qvQeWnCWVTpTKZBtvA== dependencies: is-docker "^2.0.0" is-wsl "^2.1.1" @@ -10656,11 +10054,6 @@ opener@^1.5.1: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA== -opener@~1.4.0: - version "1.4.3" - resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" - integrity sha1-XG2ixdflgx6P+jlklQ+NZnSskLg= - opn@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" @@ -10668,7 +10061,7 @@ opn@^5.5.0: dependencies: is-wsl "^1.1.0" -optimist@0.6.x, optimist@~0.6.1: +optimist@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= @@ -10688,17 +10081,17 @@ optionator@^0.8.1, optionator@^0.8.2: type-check "~0.3.2" word-wrap "~1.2.3" -ora@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.1.0.tgz#b188cf8cd2d4d9b13fd25383bc3e5cba352c94f8" - integrity sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w== +ora@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.3.0.tgz#fb832899d3a1372fe71c8b2c534bbfe74961bb6f" + integrity sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g== dependencies: + bl "^4.0.3" chalk "^4.1.0" cli-cursor "^3.1.0" - cli-spinners "^2.4.0" + cli-spinners "^2.5.0" is-interactive "^1.0.0" log-symbols "^4.0.0" - mute-stream "0.0.8" strip-ansi "^6.0.0" wcwidth "^1.0.1" @@ -10738,38 +10131,16 @@ os-locale@^1.4.0: dependencies: lcid "^1.0.0" -os-locale@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" - integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== - dependencies: - execa "^1.0.0" - lcid "^2.0.0" - mem "^4.0.0" - -os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: +os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -osenv@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== -p-defer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" - integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= - p-defer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-3.0.0.tgz#d1dceb4ee9b2b604b1d94ffec83760175d4e6f83" @@ -10780,18 +10151,6 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= -p-is-promise@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" - integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== - -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - p-limit@^2.0.0, p-limit@^2.2.0: version "2.2.2" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" @@ -10806,13 +10165,6 @@ p-limit@^3.0.2: dependencies: p-try "^2.0.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -10846,26 +10198,11 @@ p-retry@^3.0.1: dependencies: retry "^0.12.0" -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -package-json@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" - integrity sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0= - dependencies: - got "^6.7.1" - registry-auth-token "^3.0.1" - registry-url "^3.0.3" - semver "^5.1.0" - package-json@^6.3.0: version "6.5.0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" @@ -10876,41 +10213,30 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" -pacote@9.5.12: - version "9.5.12" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.5.12.tgz#1e11dd7a8d736bcc36b375a9804d41bb0377bf66" - integrity sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ== +pacote@11.2.4: + version "11.2.4" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.2.4.tgz#dc7ca740a573ed86a3bf863511d22c1d413ec82f" + integrity sha512-GfTeVQGJ6WyBQbQD4t3ocHbyOmTQLmWjkCKSZPmKiGFKYKNUaM5U2gbLzUW8WG1XmS9yQFnsTFA0k3o1+q4klQ== dependencies: - bluebird "^3.5.3" - cacache "^12.0.2" - chownr "^1.1.2" - figgy-pudding "^3.5.1" - get-stream "^4.1.0" - glob "^7.1.3" + "@npmcli/git" "^2.0.1" + "@npmcli/installed-package-contents" "^1.0.5" + "@npmcli/promise-spawn" "^1.2.0" + "@npmcli/run-script" "^1.3.0" + cacache "^15.0.5" + chownr "^2.0.0" + fs-minipass "^2.1.0" infer-owner "^1.0.4" - lru-cache "^5.1.1" - make-fetch-happen "^5.0.0" - minimatch "^3.0.4" - minipass "^2.3.5" - mississippi "^3.0.0" - mkdirp "^0.5.1" - normalize-package-data "^2.4.0" - npm-normalize-package-bin "^1.0.0" - npm-package-arg "^6.1.0" - npm-packlist "^1.1.12" - npm-pick-manifest "^3.0.0" - npm-registry-fetch "^4.0.0" - osenv "^0.1.5" - promise-inflight "^1.0.1" + minipass "^3.1.3" + mkdirp "^1.0.3" + npm-package-arg "^8.0.1" + npm-packlist "^2.1.4" + npm-pick-manifest "^6.0.0" + npm-registry-fetch "^9.0.0" promise-retry "^1.1.1" - protoduck "^5.0.1" - rimraf "^2.6.2" - safe-buffer "^5.1.2" - semver "^5.6.0" - ssri "^6.0.1" - tar "^4.4.10" - unique-filename "^1.1.1" - which "^1.3.1" + read-package-json-fast "^1.1.3" + rimraf "^3.0.2" + ssri "^8.0.0" + tar "^6.1.0" pako@~1.0.2, pako@~1.0.5: version "1.0.11" @@ -10926,7 +10252,7 @@ parallel-transform@^1.1.0: inherits "^2.0.3" readable-stream "^2.1.5" -param-case@2.1.x, param-case@^2.1.0: +param-case@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= @@ -10940,11 +10266,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parent-require@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parent-require/-/parent-require-1.0.0.tgz#746a167638083a860b0eef6732cb27ed46c32977" - integrity sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc= - parse-asn1@^5.0.0: version "5.1.5" resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e" @@ -10992,6 +10313,11 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-node-version@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" + integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== + parse5-html-rewriting-stream@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz#de1820559317ab4e451ea72dba05fddfd914480b" @@ -11000,7 +10326,7 @@ parse5-html-rewriting-stream@6.0.1: parse5 "^6.0.1" parse5-sax-parser "^6.0.1" -parse5-htmlparser2-tree-adapter@^6.0.0: +parse5-htmlparser2-tree-adapter@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== @@ -11014,12 +10340,12 @@ parse5-sax-parser@^6.0.1: dependencies: parse5 "^6.0.1" -parse5@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" - integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== +parse5@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94" + integrity sha1-m387DeMr543CQBsXVzzK8Pb1nZQ= -parse5@^5.0.0, parse5@^5.1.1: +parse5@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== @@ -11029,30 +10355,6 @@ parse5@^6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== -parseqs@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" - integrity sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0= - dependencies: - better-assert "~1.0.0" - -parseqs@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.6.tgz#8e4bb5a19d1cdc844a08ac974d34e273afa670d5" - integrity sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w== - -parseuri@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a" - integrity sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo= - dependencies: - better-assert "~1.0.0" - -parseuri@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a" - integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow== - parseurl@^1.3.3, parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -11147,11 +10449,6 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pathval@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" - integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= - pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -11222,20 +10519,13 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" -pkg-dir@^4.1.0: +pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" -pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" - integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= - dependencies: - find-up "^2.1.0" - pkg-up@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" @@ -11257,11 +10547,6 @@ pluralize@^1.2.1: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" integrity sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU= -pn@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" - integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== - pnp-webpack-plugin@1.6.4: version "1.6.4" resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" @@ -11269,15 +10554,6 @@ pnp-webpack-plugin@1.6.4: dependencies: ts-pnp "^1.1.6" -portfinder@^1.0.13: - version "1.0.28" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" - integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== - dependencies: - async "^2.6.2" - debug "^3.1.1" - mkdirp "^0.5.5" - portfinder@^1.0.23: version "1.0.25" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca" @@ -11358,26 +10634,25 @@ postcss-discard-overridden@^4.0.1: dependencies: postcss "^7.0.0" -postcss-import@12.0.1: - version "12.0.1" - resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-12.0.1.tgz#cf8c7ab0b5ccab5649024536e565f841928b7153" - integrity sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw== +postcss-import@14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.0.0.tgz#3ed1dadac5a16650bde3f4cdea6633b9c3c78296" + integrity sha512-gFDDzXhqr9ELmnLHgCC3TbGfA6Dm/YMb/UN8/f7Uuq4fL7VTk2vOIj6hwINEwbokEmp123bLD7a5m+E+KIetRg== dependencies: - postcss "^7.0.1" - postcss-value-parser "^3.2.3" + postcss-value-parser "^4.0.0" read-cache "^1.0.0" resolve "^1.1.7" -postcss-loader@4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.0.4.tgz#b2d005b52e008a44991cf8123bee207e635eb53e" - integrity sha512-pntA9zIR14drQo84yGTjQJg1m7T0DkXR4vXYHBngiRZdJtEeCrojL6lOpqUanMzG375lIJbT4Yug85zC/AJWGw== +postcss-loader@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.2.0.tgz#f6993ea3e0f46600fb3ee49bbd010448123a7db4" + integrity sha512-mqgScxHqbiz1yxbnNcPdKYo/6aVt+XExURmEbQlviFVWogDbM4AJ0A/B+ZBpYsJrTRxKw7HyRazg9x0Q9SWwLA== dependencies: cosmiconfig "^7.0.0" klona "^2.0.4" loader-utils "^2.0.0" schema-utils "^3.0.0" - semver "^7.3.2" + semver "^7.3.4" postcss-merge-longhand@^4.0.11: version "4.0.11" @@ -11635,12 +10910,12 @@ postcss-unique-selectors@^4.0.1: postcss "^7.0.0" uniqs "^2.0.0" -postcss-value-parser@^3.0.0, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.1: +postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== -postcss-value-parser@^4.1.0: +postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== @@ -11654,14 +10929,14 @@ postcss@7.0.21: source-map "^0.6.1" supports-color "^6.1.0" -postcss@7.0.32: - version "7.0.32" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" - integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== +postcss@8.2.4: + version "8.2.4" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.4.tgz#20a98a39cf303d15129c2865a9ec37eda0031d04" + integrity sha512-kRFftRoExRVXZlwUuay9iC824qmXPcQQVzAjbCCgjpXnkdMCJYBu2gTwAaFBzv8ewND6O8xFb3aELmEkh9zTzg== dependencies: - chalk "^2.4.2" + colorette "^1.2.1" + nanoid "^3.1.20" source-map "^0.6.1" - supports-color "^6.1.0" postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.5: version "7.0.26" @@ -11672,61 +10947,30 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.5: source-map "^0.6.1" supports-color "^6.1.0" -postcss@^7.0.32: - version "7.0.34" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.34.tgz#f2baf57c36010df7de4009940f21532c16d65c20" - integrity sha512-H/7V2VeNScX9KE83GDrDZNiGT1m2H+UTnlinIzhjlLX9hfMUn1mHNnGeX81a1c8JSBdBvqk7c2ZOG6ZPn5itGw== - dependencies: - chalk "^2.4.2" - source-map "^0.6.1" - supports-color "^6.1.0" - -postcss@^8.1.1: - version "8.1.6" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.1.6.tgz#b022ba2cfb8701da234d073ed3128c5a384c35ff" - integrity sha512-JuifSl4h8dJ70SiMXKjzCxhalE6p2TnMHuq9G8ftyXj2jg6SXzqCsEuxMj9RkmJoO5D+Z9YrWunNkxqpRT02qg== +postcss@^8.1.4: + version "8.2.6" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.6.tgz#5d69a974543b45f87e464bc4c3e392a97d6be9fe" + integrity sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg== dependencies: colorette "^1.2.1" - line-column "^1.0.2" - nanoid "^3.1.16" + nanoid "^3.1.20" source-map "^0.6.1" -prebuild-install@^5.3.3: - version "5.3.6" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.6.tgz#7c225568d864c71d89d07f8796042733a3f54291" - integrity sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg== - dependencies: - detect-libc "^1.0.3" - expand-template "^2.0.3" - github-from-package "0.0.0" - minimist "^1.2.3" - mkdirp-classic "^0.5.3" - napi-build-utils "^1.0.1" - node-abi "^2.7.0" - noop-logger "^0.1.1" - npmlog "^4.0.1" - pump "^3.0.0" - rc "^1.2.7" - simple-get "^3.0.3" - tar-fs "^2.0.0" - tunnel-agent "^0.6.0" - which-pm-runs "^1.0.0" - prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= - prepend-http@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +pretty-bytes@^5.3.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + private@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -11775,6 +11019,14 @@ promise-retry@^1.1.1: err-code "^1.0.0" retry "^0.10.0" +promise-retry@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + property-information@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-4.2.0.tgz#f0e66e07cbd6fed31d96844d958d153ad3eb486e" @@ -11827,13 +11079,6 @@ protobufjs@^6.8.9: "@types/node" "^13.7.0" long "^4.0.0" -protoduck@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/protoduck/-/protoduck-5.0.1.tgz#03c3659ca18007b69a50fd82a7ebcc516261151f" - integrity sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg== - dependencies: - genfun "^5.0.0" - protractor@~7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/protractor/-/protractor-7.0.0.tgz#c3e263608bd72e2c2dc802b11a772711a4792d03" @@ -11873,6 +11118,11 @@ prr@~1.0.1: resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= +ps-list@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/ps-list/-/ps-list-7.2.0.tgz#3d110e1de8249a4b178c9b1cf2a215d1e4e42fc0" + integrity sha512-v4Bl6I3f2kJfr5o80ShABNHAokIgY+wFDTQfE+X3zWYgSGQOCBeYptLZUpoOALBqO5EawmDN/tjTldJesd0ujQ== + pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -11895,6 +11145,11 @@ public-encrypt@^4.0.0: randombytes "^2.0.1" safe-buffer "^5.1.2" +puka@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/puka/-/puka-1.0.1.tgz#a2df782b7eb4cf9564e4c93a5da422de0dfacc02" + integrity sha512-ssjRZxBd7BT3dte1RR3VoeT2cT/ODH8x+h0rUF1rMqB0srHYf48stSDWfiYakTp5UBZMxroZhB2+ExLDHm7W3g== + pump@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" @@ -11942,19 +11197,23 @@ pupa@^2.0.1: dependencies: escape-goat "^2.0.0" -puppeteer@^1.12.2: - version "1.20.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-1.20.0.tgz#e3d267786f74e1d87cf2d15acc59177f471bbe38" - integrity sha512-bt48RDBy2eIwZPrkgbcwHtb51mj2nKvHOPMaSH2IsWiv7lOG9k9zhaRzpDZafrk05ajMc3cu+lSQYYOfH2DkVQ== +puppeteer@5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.4.1.tgz#f2038eb23a0f593ed2cce0d6e7cd5c43aecd6756" + integrity sha512-8u6r9tFm3gtMylU4uCry1W/CeAA8uczKMONvGvivkTsGqKA7iB7DWO2CBFYlB9GY6/IEoq9vkI5slJWzUBkwNw== dependencies: debug "^4.1.0" - extract-zip "^1.6.6" - https-proxy-agent "^2.2.1" - mime "^2.0.3" + devtools-protocol "0.0.809251" + extract-zip "^2.0.0" + https-proxy-agent "^4.0.0" + node-fetch "^2.6.1" + pkg-dir "^4.2.0" progress "^2.0.1" proxy-from-env "^1.0.0" - rimraf "^2.6.1" - ws "^6.1.0" + rimraf "^3.0.2" + tar-fs "^2.0.0" + unbzip2-stream "^1.3.3" + ws "^7.2.3" q@1.4.1: version "1.4.1" @@ -11981,11 +11240,6 @@ qs@^6.6.0: resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== -qs@~2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-2.3.3.tgz#e9e85adbe75da0bbe4c8e0476a086290f863b404" - integrity sha1-6eha2+ddoLvkyOBHaghikPhjtAQ= - qs@~6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" @@ -12070,7 +11324,7 @@ raw-loader@4.0.2: loader-utils "^2.0.0" schema-utils "^3.0.0" -rc@^1.0.1, rc@^1.1.6, rc@^1.2.7, rc@^1.2.8: +rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -12104,6 +11358,22 @@ read-cache@^1.0.0: dependencies: pify "^2.3.0" +read-package-json-fast@^1.1.3: + version "1.2.2" + resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-1.2.2.tgz#fba77b0b0d66b1ab344e214cb0876577e749c423" + integrity sha512-39DbPJjkltEzfXJXB6D8/Ir3GFOU2YbSKa2HaB/Y3nKrc/zY+0XrALpID6/13ezWyzqvOHrBbR4t4cjQuTdBVQ== + dependencies: + json-parse-even-better-errors "^2.3.0" + npm-normalize-package-bin "^1.0.1" + +read-package-json-fast@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.2.tgz#2dcb24d9e8dd50fb322042c8c35a954e6cc7ac9e" + integrity sha512-5fyFUyO9B799foVk4n6ylcoAktG/FbE3jwRKxvwaeSrIunaoMc0u81dzXxjeAFKOce7O5KncdfwpGvvs6r5PsQ== + dependencies: + json-parse-even-better-errors "^2.3.0" + npm-normalize-package-bin "^1.0.1" + read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -12170,6 +11440,13 @@ readdirp@~3.4.0: dependencies: picomatch "^2.2.1" +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== + dependencies: + picomatch "^2.2.1" + readline2@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" @@ -12193,7 +11470,7 @@ redeyed@~2.1.0: dependencies: esprima "~4.0.0" -reflect-metadata@^0.1.13, reflect-metadata@^0.1.2: +reflect-metadata@^0.1.2: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== @@ -12306,14 +11583,6 @@ regexpu-core@^4.7.1: unicode-match-property-ecmascript "^1.0.4" unicode-match-property-value-ecmascript "^1.2.0" -registry-auth-token@^3.0.1: - version "3.4.0" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.4.0.tgz#d7446815433f5d5ed6431cd5dca21048f66b397e" - integrity sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A== - dependencies: - rc "^1.1.6" - safe-buffer "^5.0.1" - registry-auth-token@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.0.tgz#1d37dffda72bbecd0f581e4715540213a65eb7da" @@ -12321,13 +11590,6 @@ registry-auth-token@^4.0.0: dependencies: rc "^1.2.8" -registry-url@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" - integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI= - dependencies: - rc "^1.0.1" - registry-url@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" @@ -12366,20 +11628,6 @@ regjsparser@^0.6.4: dependencies: jsesc "~0.5.0" -rehype-minify-whitespace@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/rehype-minify-whitespace/-/rehype-minify-whitespace-2.0.4.tgz#954bd07b42633566f198df12cb7d816cc3b793bd" - integrity sha512-TQVOvOSURaWNbYepVgSzUofSTRsaAVQe/dH01Mo44u+Gr221jTmUem8l0jlJBAfSOHNaUF4jfDDbyEeb4ueAmA== - dependencies: - collapse-white-space "^1.0.0" - hast-util-embedded "^1.0.0" - hast-util-has-property "^1.0.0" - hast-util-is-body-ok-link "^1.0.0" - hast-util-is-element "^1.0.0" - html-whitespace-sensitive-tag-names "^1.0.0" - unist-util-is "^3.0.0" - unist-util-modify-children "^1.0.0" - rehype-parse@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-5.0.0.tgz#a103e2d9ee1b3a0dba9223e6f73623b8396e3682" @@ -12389,7 +11637,7 @@ rehype-parse@^5.0.0: parse5 "^5.0.0" xtend "^4.0.1" -rehype-parse@^6.0.0, rehype-parse@^6.0.2: +rehype-parse@^6.0.0: version "6.0.2" resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-6.0.2.tgz#aeb3fdd68085f9f796f1d3137ae2b85a98406964" integrity sha512-0S3CpvpTAgGmnz8kiCyFLGuW5yA4OQhyNTm/nwPopZ7+PI11WnGl1TTWTGv/2hPEe/g2jRLlhVVSsoDH8waRug== @@ -12398,13 +11646,6 @@ rehype-parse@^6.0.0, rehype-parse@^6.0.2: parse5 "^5.0.0" xtend "^4.0.0" -rehype-remark@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/rehype-remark/-/rehype-remark-5.0.2.tgz#541d6e7bc11d98bac3536831fe052e52157228ce" - integrity sha512-ppbJ8cgFeB2uFugT8jaEifqo6qA+fcFAwnQ4DNWdXC2+Si7CMvGhreeflG8mTTNWVnFSP4xu04ZSZnubZiOpfw== - dependencies: - hast-util-to-mdast "^4.0.0" - rehype-slug@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/rehype-slug/-/rehype-slug-2.0.3.tgz#cd0234de130f02e3607396ff2e873fc5a3bd0413" @@ -12424,14 +11665,6 @@ rehype-stringify@^4.0.0: hast-util-to-html "^4.0.0" xtend "^4.0.1" -rehype-stringify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/rehype-stringify/-/rehype-stringify-5.0.0.tgz#f66780704824ed31b8945ac1b19093a5dab0efd3" - integrity sha512-uQVzP/eLMM6kdM5Myo0mt5rrb5ntCAxFGNobtzMTTdL1+hqc6WNVxB9uwZyckm54qz/0SnDkzgvCV0/AZy4wCw== - dependencies: - hast-util-to-html "^5.0.0" - xtend "^4.0.1" - rehype-stringify@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/rehype-stringify/-/rehype-stringify-6.0.1.tgz#b6aa9f84d5276c5d247c62fc1ea15983a35a4575" @@ -12458,19 +11691,6 @@ rehype@^8.0.0: rehype-stringify "^6.0.0" unified "^7.0.0" -relateurl@0.2.x: - version "0.2.7" - resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" - integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= - -remark-frontmatter@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/remark-frontmatter/-/remark-frontmatter-1.3.3.tgz#67ec63c89da5a84bb793ecec166e11b4eb47af10" - integrity sha512-fM5eZPBvu2pVNoq3ZPW22q+5Ativ1oLozq2qYt9I2oNyxiUd/tDl0iLLntEVAegpZIslPWg1brhcP1VsaSVUag== - dependencies: - fault "^1.0.1" - xtend "^4.0.1" - remark-html@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/remark-html/-/remark-html-8.0.0.tgz#9fcb859a6f3cb40f3ef15402950f1a62ec301b3a" @@ -12481,16 +11701,6 @@ remark-html@^8.0.0: mdast-util-to-hast "^3.0.0" xtend "^4.0.1" -remark-html@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/remark-html/-/remark-html-9.0.1.tgz#37c097396651a63d2f7d48d0168a554b3ec6b307" - integrity sha512-1Kbk5nb3RCdVxWATOu+ZW66muXoe0NjVgIxFmCb5eOB9Vezgd7gqkOhkKjKks9Jgorwiv5l81av64UWAwuYD/Q== - dependencies: - hast-util-sanitize "^1.0.0" - hast-util-to-html "^5.0.0" - mdast-util-to-hast "^4.0.0" - xtend "^4.0.1" - remark-parse@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-5.0.0.tgz#4c077f9e499044d1d5c13f80d7a98cf7b9285d95" @@ -12512,27 +11722,6 @@ remark-parse@^5.0.0: vfile-location "^2.0.0" xtend "^4.0.1" -remark-parse@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-6.0.3.tgz#c99131052809da482108413f87b0ee7f52180a3a" - integrity sha512-QbDXWN4HfKTUC0hHa4teU463KclLAnwpn/FBn87j9cKYJWWawbiLgMfP2Q4XwhxxuuuOxHlw+pSN0OKuJwyVvg== - dependencies: - collapse-white-space "^1.0.2" - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" - is-whitespace-character "^1.0.0" - is-word-character "^1.0.0" - markdown-escapes "^1.0.0" - parse-entities "^1.1.0" - repeat-string "^1.5.4" - state-toggle "^1.0.0" - trim "0.0.1" - trim-trailing-lines "^1.0.0" - unherit "^1.0.4" - unist-util-remove-position "^1.0.0" - vfile-location "^2.0.0" - xtend "^4.0.1" - remark-stringify@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-5.0.0.tgz#336d3a4d4a6a3390d933eeba62e8de4bd280afba" @@ -12553,26 +11742,6 @@ remark-stringify@^5.0.0: unherit "^1.0.4" xtend "^4.0.1" -remark-stringify@^6.0.4: - version "6.0.4" - resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-6.0.4.tgz#16ac229d4d1593249018663c7bddf28aafc4e088" - integrity sha512-eRWGdEPMVudijE/psbIDNcnJLRVx3xhfuEsTDGgH4GsFF91dVhw5nhmnBppafJ7+NWINW6C7ZwWbi30ImJzqWg== - dependencies: - ccount "^1.0.0" - is-alphanumeric "^1.0.0" - is-decimal "^1.0.0" - is-whitespace-character "^1.0.0" - longest-streak "^2.0.1" - markdown-escapes "^1.0.0" - markdown-table "^1.1.0" - mdast-util-compact "^1.0.0" - parse-entities "^1.0.2" - repeat-string "^1.5.4" - state-toggle "^1.0.0" - stringify-entities "^1.0.1" - unherit "^1.0.4" - xtend "^4.0.1" - remark@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/remark/-/remark-9.0.0.tgz#c5cfa8ec535c73a67c4b0f12bfdbd3a67d8b2f60" @@ -12602,23 +11771,7 @@ replace-ext@1.0.0: resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= -request-promise-core@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" - integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== - dependencies: - lodash "^4.17.19" - -request-promise-native@^1.0.5, request-promise-native@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" - integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== - dependencies: - request-promise-core "1.1.4" - stealthy-require "^1.1.1" - tough-cookie "^2.3.3" - -request@^2.87.0, request@^2.88.0, request@^2.88.2: +request@^2.79.0, request@^2.87.0, request@^2.88.0, request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -12649,11 +11802,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= - require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -12715,12 +11863,12 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.18.1.tgz#018fcb2c5b207d2a6424aee361c5a266da8f4130" - integrity sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA== +resolve@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== dependencies: - is-core-module "^2.0.0" + is-core-module "^2.1.0" path-parse "^1.0.6" resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2: @@ -12730,13 +11878,6 @@ resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2: dependencies: path-parse "^1.0.6" -resolve@^1.8.1: - version "1.17.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" - integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== - dependencies: - path-parse "^1.0.6" - responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" @@ -12824,7 +11965,7 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -12858,12 +11999,12 @@ robots-parser@^2.0.1: resolved "https://registry.yarnpkg.com/robots-parser/-/robots-parser-2.1.1.tgz#41b289cf44a6aa136dc62be0085adca954573ab0" integrity sha512-6yWEYSdhK3bAEcYY0In3wgSBK70BiQoJArzdjZKCP/35b3gKIYu5Lc0qQqsoxjoLVebVoJiKK4VWGc5+oxvWBQ== -rollup@2.32.1: - version "2.32.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.32.1.tgz#625a92c54f5b4d28ada12d618641491d4dbb548c" - integrity sha512-Op2vWTpvK7t6/Qnm1TTh7VjEZZkN8RWgf0DHbkKzQBwNf748YhXbozHVefqpPp/Fuyk/PQPAnYsBxAEtlMvpUw== +rollup@2.38.4: + version "2.38.4" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.38.4.tgz#1b84ea8728c73b1a00a6a6e9c630ec8c3fe48cea" + integrity sha512-B0LcJhjiwKkTl79aGVF/u5KdzsH8IylVfV56Ut6c9ouWLJcUK17T83aZBetNYSnZtXf2OHD4+2PbmRW+Fp5ulg== optionalDependencies: - fsevents "~2.1.2" + fsevents "~2.3.1" router@^1.3.1: version "1.3.4" @@ -12916,18 +12057,6 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rx-lite-aggregates@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" - integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74= - dependencies: - rx-lite "*" - -rx-lite@*, rx-lite@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" - integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= - rx-lite@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" @@ -12952,7 +12081,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: +safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== @@ -12995,10 +12124,10 @@ safeps@7.0.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sass-loader@10.0.5: - version "10.0.5" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.0.5.tgz#f53505b5ddbedf43797470ceb34066ded82bb769" - integrity sha512-2LqoNPtKkZq/XbXNQ4C64GFEleSEHKv6NPSI+bMC/l+jpEXGJhiRYkAQToO24MR7NU4JRY2RpLpJ/gjo2Uf13w== +sass-loader@10.1.1: + version "10.1.1" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.1.1.tgz#4ddd5a3d7638e7949065dd6e9c7c04037f7e663d" + integrity sha512-W6gVDXAd5hR/WHsPicvZdjAWHBcEJ44UahgxcIE196fW2ong0ZHMPO1kZuI5q0VlvMQZh32gpv69PLWQm70qrw== dependencies: klona "^2.0.4" loader-utils "^2.0.0" @@ -13006,10 +12135,10 @@ sass-loader@10.0.5: schema-utils "^3.0.0" semver "^7.3.2" -sass@1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.27.0.tgz#0657ff674206b95ec20dc638a93e179c78f6ada2" - integrity sha512-0gcrER56OkzotK/GGwgg4fPrKuiFlPNitO7eUJ18Bs+/NBlofJfMxmxqpqJxjae9vu0Wq8TZzrSyxZal00WDig== +sass@1.32.6: + version "1.32.6" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.6.tgz#e3646c8325cd97ff75a8a15226007f3ccd221393" + integrity sha512-1bcDHDcSqeFtMr0JXI3xc/CXX6c4p0wHHivJdru8W7waM7a1WjKMm4m/Z5sY7CbVw4Whi2Chpcw6DFfSWwGLzQ== dependencies: chokidar ">=2.0.0 <4.0.0" @@ -13020,18 +12149,11 @@ saucelabs@^1.5.0: dependencies: https-proxy-agent "^2.2.1" -sax@>=0.6.0, sax@~1.2.4: +sax@>=0.6.0, sax@^1.2.1, sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -saxes@^3.1.5: - version "3.1.11" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" - integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== - dependencies: - xmlchars "^2.1.1" - scandirectory@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/scandirectory/-/scandirectory-2.5.0.tgz#6ce03f54a090b668e3cbedbf20edf9e310593e72" @@ -13091,19 +12213,12 @@ selenium-webdriver@3.6.0, selenium-webdriver@^3.0.1: tmp "0.0.30" xml2js "^0.4.17" -selfsigned@^1.10.7: - version "1.10.7" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b" - integrity sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA== +selfsigned@^1.10.8: + version "1.10.8" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.8.tgz#0d17208b7d12c33f8eac85c41835f27fc3d81a30" + integrity sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w== dependencies: - node-forge "0.9.0" - -semver-diff@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" - integrity sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY= - dependencies: - semver "^5.0.3" + node-forge "^0.10.0" semver-diff@^3.1.1: version "3.1.1" @@ -13126,7 +12241,7 @@ semver-intersect@1.4.0: dependencies: semver "^5.0.0" -"semver@2 || 3 || 4 || 5", semver@^5.0.0, semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.2.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.0.0, semver@^5.0.1, semver@^5.2.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -13136,10 +12251,12 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@7.3.2, semver@^7.3.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== +semver@7.3.4, semver@^7.3.4: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: version "6.3.0" @@ -13151,6 +12268,11 @@ semver@^7.0.0, semver@^7.1.1: resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6" integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA== +semver@^7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== + send@0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" @@ -13253,7 +12375,7 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== -sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.8: +sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== @@ -13320,20 +12442,6 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= -simple-concat@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" - integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== - -simple-get@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.0.tgz#b45be062435e50d159540b576202ceec40b9c6b3" - integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA== - dependencies: - decompress-response "^4.2.0" - once "^1.3.1" - simple-concat "^1.0.0" - simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" @@ -13341,12 +12449,7 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" - integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= - -slash@^3.0.0: +slash@3.0.0, slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== @@ -13398,96 +12501,71 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -socket.io-adapter@~1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9" - integrity sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g== +socket.io-adapter@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.1.0.tgz#edc5dc36602f2985918d631c1399215e97a1b527" + integrity sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg== -socket.io-client@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.3.0.tgz#14d5ba2e00b9bcd145ae443ab96b3f86cbcc1bb4" - integrity sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA== - dependencies: - backo2 "1.0.2" - base64-arraybuffer "0.1.5" - component-bind "1.0.0" - component-emitter "1.2.1" - debug "~4.1.0" - engine.io-client "~3.4.0" - has-binary2 "~1.0.2" - has-cors "1.1.0" - indexof "0.0.1" - object-component "0.0.3" - parseqs "0.0.5" - parseuri "0.0.5" - socket.io-parser "~3.3.0" - to-array "0.1.4" - -socket.io-parser@~3.3.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.1.tgz#f07d9c8cb3fb92633aa93e76d98fd3a334623199" - integrity sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ== +socket.io-parser@~4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.0.4.tgz#9ea21b0d61508d18196ef04a2c6b9ab630f4c2b0" + integrity sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g== dependencies: + "@types/component-emitter" "^1.2.10" component-emitter "~1.3.0" - debug "~3.1.0" - isarray "2.0.1" + debug "~4.3.1" -socket.io-parser@~3.4.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.4.1.tgz#b06af838302975837eab2dc980037da24054d64a" - integrity sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A== +socket.io@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-3.1.1.tgz#905e3d4a3b37d8e7970e67a4a6eb81110a5778ba" + integrity sha512-7cBWdsDC7bbyEF6WbBqffjizc/H4YF1wLdZoOzuYfo2uMNSFjJKuQ36t0H40o9B20DO6p+mSytEd92oP4S15bA== dependencies: - component-emitter "1.2.1" - debug "~4.1.0" - isarray "2.0.1" + "@types/cookie" "^0.4.0" + "@types/cors" "^2.8.8" + "@types/node" "^14.14.10" + accepts "~1.3.4" + base64id "~2.0.0" + debug "~4.3.1" + engine.io "~4.1.0" + socket.io-adapter "~2.1.0" + socket.io-parser "~4.0.3" -socket.io@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.3.0.tgz#cd762ed6a4faeca59bc1f3e243c0969311eb73fb" - integrity sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg== +sockjs-client@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.5.0.tgz#2f8ff5d4b659e0d092f7aba0b7c386bd2aa20add" + integrity sha512-8Dt3BDi4FYNrCFGTL/HtwVzkARrENdwOUf1ZoW/9p3M8lZdFT35jVdrHza+qgxuG9H3/shR4cuX/X9umUrjP8Q== dependencies: - debug "~4.1.0" - engine.io "~3.4.0" - has-binary2 "~1.0.2" - socket.io-adapter "~1.1.0" - socket.io-client "2.3.0" - socket.io-parser "~3.4.0" - -sockjs-client@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" - integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== - dependencies: - debug "^3.2.5" + debug "^3.2.6" eventsource "^1.0.7" - faye-websocket "~0.11.1" - inherits "^2.0.3" - json3 "^3.3.2" - url-parse "^1.4.3" + faye-websocket "^0.11.3" + inherits "^2.0.4" + json3 "^3.3.3" + url-parse "^1.4.7" -sockjs@0.3.20: - version "0.3.20" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.20.tgz#b26a283ec562ef8b2687b44033a4eeceac75d855" - integrity sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA== +sockjs@^0.3.21: + version "0.3.21" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.21.tgz#b34ffb98e796930b60a0cfa11904d6a339a7d417" + integrity sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw== dependencies: - faye-websocket "^0.10.0" + faye-websocket "^0.11.3" uuid "^3.4.0" - websocket-driver "0.6.5" + websocket-driver "^0.7.4" -socks-proxy-agent@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" - integrity sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg== +socks-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz#7c0f364e7b1cf4a7a437e71253bed72e9004be60" + integrity sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA== dependencies: - agent-base "~4.2.1" - socks "~2.3.2" + agent-base "6" + debug "4" + socks "^2.3.3" -socks@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.3.tgz#01129f0a5d534d2b897712ed8aceab7ee65d78e3" - integrity sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA== +socks@^2.3.3: + version "2.5.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.5.1.tgz#7720640b6b5ec9a07d556419203baa3f0596df5f" + integrity sha512-oZCsJJxapULAYJaEYBSzMcz8m3jqgGrHaGhkmU/o/PQfFWYWxkAaA0UMGImb6s6tEXfKi959X6VJjMMQ3P6TTQ== dependencies: - ip "1.1.5" + ip "^1.1.5" smart-buffer "^4.1.0" source-list-map@^2.0.0, source-list-map@^2.0.1: @@ -13495,10 +12573,10 @@ source-list-map@^2.0.0, source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-loader@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-1.1.2.tgz#5b782bf08496d3a7f355e1780df0e25190a80991" - integrity sha512-bjf6eSENOYBX4JZDfl9vVLNsGAQ6Uz90fLmOazcmMcyDYOBFsGxPNn83jXezWLY9bJsVAo1ObztxPcV8HAbjVA== +source-map-loader@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-1.1.3.tgz#7dbc2fe7ea09d3e43c51fd9fc478b7f016c1f820" + integrity sha512-6YHeF+XzDOrT/ycFJNI53cgEsp/tHTMl37hi7uVyqFAlTXW109JazaQCkbc+jjoL2637qkH1amLi+JzrIpt5lA== dependencies: abab "^2.0.5" iconv-lite "^0.6.2" @@ -13518,6 +12596,14 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" +source-map-resolve@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" + integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + source-map-support@0.5.19, source-map-support@~0.5.19: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" @@ -13636,21 +12722,21 @@ spdy@^4.0.2: select-hose "^2.0.0" spdy-transport "^3.0.0" -speed-measure-webpack-plugin@1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.3.tgz#6ff894fc83e8a6310dde3af863a0329cd79da4f5" - integrity sha512-2ljD4Ch/rz2zG3HsLsnPfp23osuPBS0qPuz9sGpkNXTN1Ic4M+W9xB8l8rS8ob2cO4b1L+WTJw/0AJwWYVgcxQ== - dependencies: - chalk "^2.0.1" - -speedline-core@1.4.2: +speed-measure-webpack-plugin@1.4.2: version "1.4.2" - resolved "https://registry.yarnpkg.com/speedline-core/-/speedline-core-1.4.2.tgz#bb061444a218d67b4cd52f63a262386197b90c8a" - integrity sha512-9/5CApkKKl6bS6jJ2D0DQllwz/1xq3cyJCR6DLgAQnkj5djCuq8NbflEdD2TI01p8qzS9qaKjzxM9cHT11ezmg== + resolved "https://registry.yarnpkg.com/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.4.2.tgz#1608e62d3bdb45f01810010e1b5bfedefedfa58f" + integrity sha512-AtVzD0bnIy2/B0fWqJpJgmhcrfWFhBlduzSo0uwplr/QvB33ZNZj2NEth3NONgdnZJqicK0W0mSxnLSbsVCDbw== + dependencies: + chalk "^4.1.0" + +speedline-core@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/speedline-core/-/speedline-core-1.4.3.tgz#4d6e7276e2063c2d36a375cb25a523ac73475319" + integrity sha512-DI7/OuAUD+GMpR6dmu8lliO2Wg5zfeh+/xsdyJZCzd8o5JgFUjCeLsBDuZjIQJdwXS3J0L/uZYrELKYqx+PXog== dependencies: "@types/node" "*" image-ssim "^0.2.0" - jpeg-js "^0.1.2" + jpeg-js "^0.4.1" split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" @@ -13684,7 +12770,7 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -ssri@^6.0.0, ssri@^6.0.1: +ssri@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== @@ -13726,10 +12812,10 @@ static-extend@^0.1.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -stealthy-require@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" - integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= +stemmer@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/stemmer/-/stemmer-1.0.5.tgz#fd89beaf8bff5d04b6643bfffcaed0fc420deec0" + integrity sha512-SLq7annzSKRDStasOJJoftCSCzBCKmBmH38jC4fDtCunAqOzpTpIm9zmaHmwNJiZ8gLe9qpVdBVbEG2DC5dE2A== stream-browserify@^2.0.1: version "2.0.2" @@ -13752,13 +12838,6 @@ stream-each@^1.1.0: end-of-stream "^1.1.0" stream-shift "^1.0.0" -stream-events@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5" - integrity sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg== - dependencies: - stubs "^3.0.0" - stream-http@^2.7.2: version "2.8.3" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" @@ -13800,7 +12879,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -13943,11 +13022,6 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -stubs@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" - integrity sha1-6NK6H6nJBXAwPAMLaQD31fiavls= - style-loader@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c" @@ -13965,10 +13039,10 @@ stylehacks@^4.0.0: postcss "^7.0.0" postcss-selector-parser "^3.0.0" -stylus-loader@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-4.1.1.tgz#0e94f5d6274932a2dad054d1a736b32146ac7a99" - integrity sha512-Vnm7J/nIs/P6swIrdwJW/dflhsCOiFmb1U3PeQ6phRtg1soPLN4uKnnL7AtGIJDe173elbtYIXVzmCyF493CfA== +stylus-loader@4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-4.3.3.tgz#381bb6341272ac50bcdfd0b877707eac99b6b757" + integrity sha512-PpWB5PnCXUzW4WMYhCvNzAHJBjIBPMXwsdfkkKuA9W7k8OQFMl/19/AQvaWsxz2IptxUlCseyJ6TY/eEKJ4+UQ== dependencies: fast-glob "^3.2.4" klona "^2.0.4" @@ -14028,13 +13102,6 @@ superstatic@^7.0.0: optionalDependencies: re2 "^1.15.0" -supports-color@5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" - integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== - dependencies: - has-flag "^3.0.0" - supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -14096,12 +13163,12 @@ swap-case@^1.1.0: lower-case "^1.1.1" upper-case "^1.1.1" -symbol-observable@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" - integrity sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA== +symbol-observable@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-3.0.0.tgz#eea8f6478c651018e059044268375c408c15c533" + integrity sha512-6tDOXSHiVjuCaasQSWTmHUWn4PuG7qa3+1WT031yTc/swT7+rLiw3GOrFxaH1E3lLP09dH3bVuVDf2gK5rxG3Q== -symbol-tree@^3.2.2: +symbol-tree@^3.2.1: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== @@ -14123,20 +13190,20 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tapable@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.0.0.tgz#a49c3d6a8a2bb606e7db372b82904c970d537a08" - integrity sha512-bjzn0C0RWoffnNdTzNi7rNDhs1Zlwk2tRXgk8EiHKAOX1Mag3d6T0Y5zNa7l9CJ+EoUne/0UHdwS8tMbkh9zDg== +tapable@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" + integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== tar-fs@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" - integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + version "2.1.0" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.0.tgz#d1cdd121ab465ee0eb9ccde2d35049d3f3daf0d5" + integrity sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg== dependencies: chownr "^1.1.1" mkdirp-classic "^0.5.2" pump "^3.0.0" - tar-stream "^2.1.4" + tar-stream "^2.0.0" tar-stream@^1.5.0: version "1.6.2" @@ -14151,6 +13218,17 @@ tar-stream@^1.5.0: to-buffer "^1.1.1" xtend "^4.0.0" +tar-stream@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.2.tgz#6d5ef1a7e5783a95ff70b69b97455a5968dc1325" + integrity sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q== + dependencies: + bl "^4.0.1" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + tar-stream@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3" @@ -14162,18 +13240,7 @@ tar-stream@^2.1.0: inherits "^2.0.3" readable-stream "^3.1.1" -tar-stream@^2.1.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - -tar@^4.3.0, tar@^4.4.10: +tar@^4.3.0: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== @@ -14198,7 +13265,7 @@ tar@^6.0.2: mkdirp "^1.0.3" yallist "^4.0.0" -tar@^6.0.5: +tar@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== @@ -14238,24 +13305,6 @@ tcp-port-used@^1.0.1: debug "4.1.0" is2 "2.0.1" -teeny-request@^6.0.0: - version "6.0.3" - resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-6.0.3.tgz#b617f9d5b7ba95c76a3f257f6ba2342b70228b1f" - integrity sha512-TZG/dfd2r6yeji19es1cUIwAlVD8y+/svB1kAC2Y0bjEyysrfbO8EZvJBRwIE6WkwmUoB7uvWLwTIhJbMXZ1Dw== - dependencies: - http-proxy-agent "^4.0.0" - https-proxy-agent "^5.0.0" - node-fetch "^2.2.0" - stream-events "^1.0.5" - uuid "^7.0.0" - -term-size@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" - integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk= - dependencies: - execa "^0.7.0" - term-size@^2.1.0: version "2.2.1" resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" @@ -14291,10 +13340,10 @@ terser-webpack-plugin@^1.4.3: webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser@5.3.7: - version "5.3.7" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.7.tgz#798a4ae2e7ff67050c3e99fcc4e00725827d97e2" - integrity sha512-lJbKdfxWvjpV330U4PBZStCT9h3N9A4zZVA5Y4k9sCWXknrpdyxi1oMsRKLmQ/YDMDxSBKIh88v0SkdhdqX06w== +terser@5.5.1: + version "5.5.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.1.tgz#540caa25139d6f496fdea056e414284886fb2289" + integrity sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ== dependencies: commander "^2.20.0" source-map "~0.7.2" @@ -14328,24 +13377,10 @@ text-table@0.2.0, text-table@~0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= -thenify-all@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" - integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= - dependencies: - thenify ">= 3.1.0 < 4" - -"thenify@>= 3.1.0 < 4": - version "3.3.1" - resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" - integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== - dependencies: - any-promise "^1.0.0" - -third-party-web@^0.11.1: - version "0.11.1" - resolved "https://registry.yarnpkg.com/third-party-web/-/third-party-web-0.11.1.tgz#4b5b176f0014be696002c104d76f40692318aec9" - integrity sha512-PBS478cWhvCM8seuloomV5lGHvu2qMOCj8gq8wKOApdfAaGh9l2rYZkdsBDaQyQg/6plov3uodc6sZ/3c1lu/g== +third-party-web@^0.12.2: + version "0.12.2" + resolved "https://registry.yarnpkg.com/third-party-web/-/third-party-web-0.12.2.tgz#6d9ce50ff94d88f7e57998dc4423a5e3e7b6735d" + integrity sha512-LWkBqBnubxaXkKU1eoobaASUxzjqmIGSTrnei5OhrAvBPojq+d21/U5xbwh0LBaeMypCLBhlL3BneyDVjBc//A== through2@2.0.1: version "2.0.1" @@ -14370,7 +13405,7 @@ through2@^3.0.1: dependencies: readable-stream "2 || 3" -"through@>=2.2.7 <3", through@X.X.X, through@^2.3.6: +"through@>=2.2.7 <3", through@X.X.X, through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -14380,7 +13415,7 @@ thunky@^1.0.2: resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== -timed-out@4.0.1, timed-out@^4.0.0: +timed-out@4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= @@ -14400,6 +13435,11 @@ timers-ext@^0.1.5: es5-ext "~0.10.46" next-tick "1" +timezone-mock@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/timezone-mock/-/timezone-mock-1.1.3.tgz#f5ca25befec2cdd2d64c07ee7a5293275c06b97e" + integrity sha512-r+fz9M1tflNkF6mJK0DwmlFyNWeSCXxZ68pu2Bv+DddIHK/czOZwnasEql17VfHqP/OcZRuPq/qi6wm7eQmQow== + timsort@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" @@ -14434,11 +13474,6 @@ tmp@0.2.1: dependencies: rimraf "^3.0.0" -to-array@0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" - integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= - to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" @@ -14496,7 +13531,7 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== -tough-cookie@^2.3.3, tough-cookie@^2.5.0, tough-cookie@~2.5.0: +tough-cookie@^2.3.2, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== @@ -14511,12 +13546,10 @@ toxic@^1.0.0: dependencies: lodash "^4.17.10" -tr46@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" - integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= - dependencies: - punycode "^2.1.0" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= "traverse@>=0.3.0 <0.4": version "0.3.9" @@ -14538,11 +13571,6 @@ trim-trailing-lines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz#7f0739881ff76657b7776e10874128004b625a94" integrity sha512-4ku0mmjXifQcTVfYDfR5lpgV7zVqPg6zV9rdZmwOPqq0+Zq19xDqEgagqVbc4pOOShbncuAOIs59R3+3gcF3ZA== -trim-trailing-lines@^1.1.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" - integrity sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ== - trim@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" @@ -14563,15 +13591,6 @@ try-require@^1.0.0: resolved "https://registry.yarnpkg.com/try-require/-/try-require-1.2.1.tgz#34489a2cac0c09c1cc10ed91ba011594d4333be2" integrity sha1-NEiaLKwMCcHMEO2RugEVlNQzO+I= -ts-morph@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-9.1.0.tgz#10d2088387c71f3c674f82492a3cec1e3538f0dd" - integrity sha512-sei4u651MBenr27sD6qLDXN3gZ4thiX71E3qV7SuVtDas0uvK2LtgZkIYUf9DKm/fLJ6AB/+yhRJ1vpEBJgy7Q== - dependencies: - "@dsherret/to-absolute-glob" "^2.0.2" - "@ts-morph/common" "~0.7.0" - code-block-writer "^10.1.1" - ts-node@^8.4.1: version "8.6.2" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.6.2.tgz#7419a01391a818fbafa6f826a33c1a13e9464e35" @@ -14588,21 +13607,16 @@ ts-pnp@^1.1.6: resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== -tslib@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" - integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== +tslib@2.1.0, tslib@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== -tslib@^1.13.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - tslib@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.0.tgz#18d13fc2dce04051e20f074cc8387fd8089ce4f3" @@ -14671,11 +13685,6 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-detect@^4.0.0, type-detect@^4.0.5: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" @@ -14718,61 +13727,31 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typeorm@^0.2.29: - version "0.2.30" - resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.30.tgz#a0df2256402cbcdde8049a244437560495ce9b38" - integrity sha512-qpr8AO3Phi6ZF7qMHOrRdNisVt8jE1KfmW0ooLFcXscA87aJ12aBPyB9cJfxGNjNwd7B3WIK9ZlBveWiqd74QA== - dependencies: - "@sqltools/formatter" "1.2.2" - app-root-path "^3.0.0" - buffer "^5.5.0" - chalk "^4.1.0" - cli-highlight "^2.1.10" - debug "^4.1.1" - dotenv "^8.2.0" - glob "^7.1.6" - js-yaml "^3.14.0" - mkdirp "^1.0.4" - reflect-metadata "^0.1.13" - sha.js "^2.4.11" - tslib "^1.13.0" - xml2js "^0.4.23" - yargonaut "^1.1.2" - yargs "^16.0.3" - -typescript@4.0.5, typescript@~4.0.2: - version "4.0.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.5.tgz#ae9dddfd1069f1cb5beb3ef3b2170dd7c1332389" - integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ== +typescript@4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" + integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== typescript@^3.2.2: version "3.7.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== -typescript@~4.1.2: - version "4.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" - integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== +typescript@~4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3" + integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw== -ua-parser-js@0.7.21: - version "0.7.21" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" - integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== +ua-parser-js@^0.7.23: + version "0.7.24" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c" + integrity sha512-yo+miGzQx5gakzVK3QFfN0/L9uVhosXBBO7qmnk7c2iw1IhL212wfA3zbnI54B0obGwC/5NWub/iT9sReMx+Fw== uberproto@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/uberproto/-/uberproto-1.2.0.tgz#61d4eab024f909c4e6ea52be867c4894a4beeb76" integrity sha1-YdTqsCT5CcTm6lK+hnxIlKS+63Y= -uglify-js@3.4.x: - version "3.4.10" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" - integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw== - dependencies: - commander "~2.19.0" - source-map "~0.6.1" - uglify-js@^3.0.15: version "3.7.7" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.7.tgz#21e52c7dccda80a53bf7cde69628a7e511aec9c9" @@ -14793,10 +13772,13 @@ unbounded@^1.2.0: dependencies: editions "^2.2.0" -unc-path-regex@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" - integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= +unbzip2-stream@^1.3.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" + integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== + dependencies: + buffer "^5.2.1" + through "^2.3.8" underscore@^1.9.1: version "1.9.2" @@ -14851,7 +13833,7 @@ unified@^6.0.0: vfile "^2.0.0" x-is-string "^0.1.0" -unified@^7.0.0, unified@^7.1.0: +unified@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/unified/-/unified-7.1.0.tgz#5032f1c1ee3364bd09da12e27fdd4a7553c7be13" integrity sha512-lbk82UOIGuCEsZhPj8rNAkXSDXd6p0QLzIuSsCdxrqnqU56St4eyOB+AlXsVgVeRmetPTYydIuvFfpDIed8mqw== @@ -14875,13 +13857,6 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^2.0.1" -union@~0.4.3: - version "0.4.6" - resolved "https://registry.yarnpkg.com/union/-/union-0.4.6.tgz#198fbdaeba254e788b0efcb630bc11f24a2959e0" - integrity sha1-GY+9rrolTniLDvy2MLwR8kopWeA= - dependencies: - qs "~2.3.3" - uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" @@ -14906,13 +13881,6 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" -unique-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" - integrity sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo= - dependencies: - crypto-random-string "^1.0.0" - unique-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" @@ -14935,11 +13903,6 @@ unist-util-filter@^0.2.1: flatmap "0.0.3" unist-util-is "^1.0.0" -unist-util-flatmap@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unist-util-flatmap/-/unist-util-flatmap-1.0.0.tgz#f914ed6b36ff040afce938d848f379f88b94b448" - integrity sha512-IG32jcKJlhARCYT2LsYPJWdoXYkzz3ESAdl1aa2hn9Auh+cgUmU6wgkII4yCc/1GgeWibRdELdCZh/p3QKQ1dQ== - unist-util-generated@^1.1.0: version "1.1.5" resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.5.tgz#1e903e68467931ebfaea386dae9ea253628acd42" @@ -14960,13 +13923,6 @@ unist-util-is@^3.0.0: resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-3.0.0.tgz#d9e84381c2468e82629e4a5be9d7d05a2dd324cd" integrity sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A== -unist-util-modify-children@^1.0.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/unist-util-modify-children/-/unist-util-modify-children-1.1.6.tgz#1587130ca0ab5c56155fa60837ff524c3fbfbfaa" - integrity sha512-TOA6W9QLil+BrHqIZNR4o6IA5QwGOveMbnQxnWYq+7EFORx9vz/CHrtzF36zWrW61E2UKw7sM1KPtIgeceVwXw== - dependencies: - array-iterate "^1.0.0" - unist-util-position@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.0.4.tgz#5872be7aec38629b971fdb758051f78817b0040a" @@ -14979,13 +13935,6 @@ unist-util-remove-position@^1.0.0: dependencies: unist-util-visit "^1.1.0" -unist-util-remove@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-1.0.3.tgz#58ec193dfa84b52d5a055ffbc58e5444eb8031a3" - integrity sha512-mB6nCHCQK0pQffUAcCVmKgIWzG/AXs/V8qpS8K72tMPtOSCMSjDeMc5yN+Ye8rB0FhcE+JvW++o1xRNc0R+++g== - dependencies: - unist-util-is "^3.0.0" - unist-util-source@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/unist-util-source/-/unist-util-source-1.0.5.tgz#539b537552b60f057b498c984b9e4f353b8d20fb" @@ -15017,7 +13966,7 @@ unist-util-visit-parents@^2.0.0: dependencies: unist-util-is "^3.0.0" -unist-util-visit@^1.0.0, unist-util-visit@^1.1.0, unist-util-visit@^1.1.1, unist-util-visit@^1.4.1: +unist-util-visit@^1.0.0, unist-util-visit@^1.1.0, unist-util-visit@^1.1.1: version "1.4.1" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.1.tgz#4724aaa8486e6ee6e26d7ff3c8685960d560b1e3" integrity sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw== @@ -15065,11 +14014,6 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -unzip-response@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" - integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c= - unzipper@^0.10.10: version "0.10.11" resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.10.11.tgz#0b4991446472cbdb92ee7403909f26c2419c782e" @@ -15091,22 +14035,6 @@ upath@^1.1.1: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== -update-notifier@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6" - integrity sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw== - dependencies: - boxen "^1.2.1" - chalk "^2.0.1" - configstore "^3.0.0" - import-lazy "^2.1.0" - is-ci "^1.0.10" - is-installed-globally "^0.1.0" - is-npm "^1.0.0" - latest-version "^3.0.0" - semver-diff "^2.0.0" - xdg-basedir "^3.0.0" - update-notifier@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3" @@ -15155,18 +14083,6 @@ url-join@0.0.1: resolved "https://registry.yarnpkg.com/url-join/-/url-join-0.0.1.tgz#1db48ad422d3402469a87f7d97bdebfe4fb1e3c8" integrity sha1-HbSK1CLTQCRpqH99l73r/k+x48g= -url-join@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/url-join/-/url-join-2.0.5.tgz#5af22f18c052a000a48d7b82c5e9c2e2feeda728" - integrity sha1-WvIvGMBSoACkjXuCxenC4v7tpyg= - -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= - dependencies: - prepend-http "^1.0.1" - url-parse-lax@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" @@ -15182,6 +14098,14 @@ url-parse@^1.4.3: querystringify "^2.1.1" requires-port "^1.0.0" +url-parse@^1.4.7: + version "1.5.1" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.1.tgz#d5fa9890af8a5e1f274a2c98376510f6425f6e3b" + integrity sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -15248,21 +14172,16 @@ uuid@3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== -uuid@8.3.1: - version "8.3.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31" - integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg== +uuid@8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== uuid@^3.0.0, uuid@^3.3.2, uuid@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^7.0.0: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== - valid-url@^1: version "1.0.9" resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" @@ -15288,7 +14207,7 @@ validate.js@^0.12.0: resolved "https://registry.yarnpkg.com/validate.js/-/validate.js-0.12.0.tgz#17f989e37c192ea2f826bbf19bf4e97e6e4be68f" integrity sha512-/x2RJSvbqEyxKj0RPN4xaRquK+EggjeVXiDDEyrJzsJogjtiZ9ov7lj/svVb4DM5Q5braQF4cooAryQbUwOxlA== -vary@~1.1.2: +vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= @@ -15357,22 +14276,6 @@ void-elements@^2.0.0: resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= -w3c-hr-time@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" - integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== - dependencies: - domexception "^1.0.1" - webidl-conversions "^4.0.2" - xml-name-validator "^3.0.0" - walkdir@^0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.0.11.tgz#a16d025eb931bd03b52f308caed0f40fcebe9532" @@ -15442,9 +14345,9 @@ webdriver-js-extender@2.1.0: selenium-webdriver "^3.0.1" webdriver-manager@^12.1.7: - version "12.1.7" - resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.1.7.tgz#ed4eaee8f906b33c146e869b55e850553a1b1162" - integrity sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA== + version "12.1.8" + resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.1.8.tgz#5e70e73eaaf53a0767d5745270addafbc5905fd4" + integrity sha512-qJR36SXG2VwKugPcdwhaqcLQOD7r8P2Xiv9sfNbfZrKBnX243iAkOueX1yAmeNgIKhJ3YAT/F2gq6IiEZzahsg== dependencies: adm-zip "^0.4.9" chalk "^1.1.1" @@ -15458,7 +14361,12 @@ webdriver-manager@^12.1.7: semver "^5.3.0" xml2js "^0.4.17" -webidl-conversions@^4.0.2: +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + +webidl-conversions@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== @@ -15474,10 +14382,10 @@ webpack-dev-middleware@3.7.2, webpack-dev-middleware@^3.7.2: range-parser "^1.2.1" webpack-log "^2.0.0" -webpack-dev-server@3.11.0: - version "3.11.0" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz#8f154a3bce1bcfd1cc618ef4e703278855e7ff8c" - integrity sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg== +webpack-dev-server@3.11.2: + version "3.11.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz#695ebced76a4929f0d5de7fd73fafe185fe33708" + integrity sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ== dependencies: ansi-html "0.0.7" bonjour "^3.5.0" @@ -15499,11 +14407,11 @@ webpack-dev-server@3.11.0: p-retry "^3.0.1" portfinder "^1.0.26" schema-utils "^1.0.0" - selfsigned "^1.10.7" + selfsigned "^1.10.8" semver "^6.3.0" serve-index "^1.9.1" - sockjs "0.3.20" - sockjs-client "1.4.0" + sockjs "^0.3.21" + sockjs-client "^1.5.0" spdy "^4.0.2" strip-ansi "^3.0.1" supports-color "^6.1.0" @@ -15521,18 +14429,18 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" -webpack-merge@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.2.0.tgz#31cbcc954f8f89cd4b06ca8d97a38549f7f3f0c9" - integrity sha512-QBglJBg5+lItm3/Lopv8KDDK01+hjdg2azEwi/4vKJ8ZmGPdtJsTpjtNNOW3a4WiqzXdCATtTudOZJngE7RKkA== +webpack-merge@5.7.3: + version "5.7.3" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.7.3.tgz#2a0754e1877a25a8bbab3d2475ca70a052708213" + integrity sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA== dependencies: clone-deep "^4.0.1" wildcard "^2.0.0" -webpack-sources@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.0.1.tgz#1467f6e692ddce91e88b8044c44347b1087bbd4f" - integrity sha512-A9oYz7ANQBK5EN19rUXbvNgfdfZf5U2gP0769OXsj9CvYkCR6OHOsd6OKyEy4H38GGxpsQPKIL83NC64QY6Xmw== +webpack-sources@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac" + integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w== dependencies: source-list-map "^2.0.1" source-map "^0.6.1" @@ -15545,10 +14453,10 @@ webpack-sources@^1.1.0, webpack-sources@^1.2.0, webpack-sources@^1.3.0, webpack- source-list-map "^2.0.0" source-map "~0.6.1" -webpack-subresource-integrity@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-1.5.1.tgz#6f44ea99987266b70c4ec42ac51064d33e982277" - integrity sha512-uekbQ93PZ9e7BFB8Hl9cFIVYQyQqiXp2ExKk9Zv+qZfH/zHXHrCFAfw1VW0+NqWbTWrs/HnuDrto3+tiPXh//Q== +webpack-subresource-integrity@1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-1.5.2.tgz#e40b6578d3072e2d24104975249c52c66e9a743e" + integrity sha512-GBWYBoyalbo5YClwWop9qe6Zclp8CIXYGIz12OPclJhIrSplDxs1Ls1JDMH8xBPPrg1T6ISaTW9Y6zOrwEiAzw== dependencies: webpack-sources "^1.3.0" @@ -15581,13 +14489,6 @@ webpack@4.44.2: watchpack "^1.7.4" webpack-sources "^1.4.1" -websocket-driver@0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" - integrity sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY= - dependencies: - websocket-extensions ">=0.1.1" - websocket-driver@>=0.5.1: version "0.7.3" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9" @@ -15597,43 +14498,46 @@ websocket-driver@>=0.5.1: safe-buffer ">=5.1.0" websocket-extensions ">=0.1.1" +websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + websocket-extensions@>=0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== -whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: +whatwg-encoding@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== dependencies: iconv-lite "0.4.24" -whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: +whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" - integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== +whatwg-url@^4.3.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.8.0.tgz#d2981aa9148c1e00a41c5a6131166ab4683bbcc0" + integrity sha1-0pgaqRSMHgCkHFphMRZqtGg7vMA= dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" + tr46 "~0.0.3" + webidl-conversions "^3.0.0" which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which-pm-runs@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" - integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= - -which@^1.2.1, which@^1.2.9, which@^1.3.1: +which@^1.2.1, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -15654,13 +14558,6 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2" -widest-line@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" - integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== - dependencies: - string-width "^2.1.1" - widest-line@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" @@ -15777,15 +14674,6 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@^2.0.0: - version "2.4.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" - integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== - dependencies: - graceful-fs "^4.1.11" - imurmurhash "^0.1.4" - signal-exit "^3.0.2" - write-file-atomic@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" @@ -15812,7 +14700,7 @@ ws@3.3.2: safe-buffer "~5.1.0" ultron "~1.1.0" -ws@^6.1.0, ws@^6.1.2, ws@^6.2.1: +ws@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== @@ -15824,44 +14712,32 @@ ws@^7.1.0: resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.1.tgz#03ed52423cd744084b2cf42ed197c8b65a936b8e" integrity sha512-sucePNSafamSKoOqoNfBd8V0StlkzJKL2ZAhGQinCfNQ+oacw+Pk7lcdAElecBF2VkLNZRiIb5Oi1Q5lVUVt2A== -ws@^7.1.2: - version "7.3.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" - integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== - ws@^7.2.3: version "7.3.0" resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.0.tgz#4b2f7f219b3d3737bc1a2fbf145d825b94d38ffd" integrity sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w== -ws@~6.1.0: - version "6.1.4" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9" - integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA== - dependencies: - async-limiter "~1.0.0" +ws@~7.4.2: + version "7.4.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.3.tgz#1f9643de34a543b8edb124bdcbc457ae55a6e5cd" + integrity sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA== x-is-string@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82" integrity sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI= -xdg-basedir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" - integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ= - xdg-basedir@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xml-name-validator@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" + integrity sha1-TYuPHszTQZqjYgYb7O9RXh5VljU= -xml2js@^0.4.17, xml2js@^0.4.23: +xml2js@^0.4.17: version "0.4.23" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== @@ -15879,11 +14755,6 @@ xmlbuilder@~11.0.0: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== -xmlchars@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== - xmldom@0.1.19: version "0.1.19" resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.19.tgz#631fc07776efd84118bf25171b37ed4d075a0abc" @@ -15894,11 +14765,6 @@ xmldom@0.1.x: resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== -xmlhttprequest-ssl@~1.5.4: - version "1.5.5" - resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" - integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= - xregexp@^4.0.0: version "4.3.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50" @@ -15916,16 +14782,11 @@ y18n@^3.2.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= -"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: +y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== -y18n@^5.0.2: - version "5.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.3.tgz#978115b82befe2b5c762bf55980b7b01a4a2d5d9" - integrity sha512-JeFbcHQ/7hVmMBXW6UB6Tg7apStHd/ztGz1JN78y3pFi/q0Ht1eA6PVkvw56gm7UA8fcJR/ziRlYEDMGoju0yQ== - y18n@^5.0.5: version "5.0.5" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" @@ -15951,23 +14812,6 @@ yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== -yargonaut@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/yargonaut/-/yargonaut-1.1.4.tgz#c64f56432c7465271221f53f5cc517890c3d6e0c" - integrity sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA== - dependencies: - chalk "^1.1.1" - figlet "^1.1.1" - parent-require "^1.0.0" - -yargs-parser@^11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" - integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" @@ -15976,7 +14820,7 @@ yargs-parser@^13.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^18.1.0, yargs-parser@^18.1.1, yargs-parser@^18.1.3: +yargs-parser@^18.1.1: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== @@ -15989,53 +14833,10 @@ yargs-parser@^20.2.2: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.2.tgz#84562c6b1c41ccec2f13d346c7dd83f8d1a0dc70" integrity sha512-XmrpXaTl6noDsf1dKpBuUNCOHqjs0g3jRMXf/ztRxdOmb+er8kE5z5b55Lz3p5u2T8KJ59ENBnASS8/iapVJ5g== -yargs@15.3.0: - version "15.3.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.0.tgz#403af6edc75b3ae04bf66c94202228ba119f0976" - integrity sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.0" - -yargs@3.32.0, yargs@^3.32.0: - version "3.32.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" - integrity sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU= - dependencies: - camelcase "^2.0.1" - cliui "^3.0.3" - decamelize "^1.1.1" - os-locale "^1.4.0" - string-width "^1.0.1" - window-size "^0.1.4" - y18n "^3.2.0" - -yargs@^12.0.5: - version "12.0.5" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" - integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== - dependencies: - cliui "^4.0.0" - decamelize "^1.2.0" - find-up "^3.0.0" - get-caller-file "^1.0.1" - os-locale "^3.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1 || ^4.0.0" - yargs-parser "^11.1.1" +yargs-parser@^20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== yargs@^13.3.2: version "13.3.2" @@ -16070,7 +14871,7 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.1" -yargs@^16.0.0, yargs@^16.0.3: +yargs@^16.1.1, yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== @@ -16083,18 +14884,18 @@ yargs@^16.0.0, yargs@^16.0.3: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^16.1.0: - version "16.1.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.1.0.tgz#fc333fe4791660eace5a894b39d42f851cd48f2a" - integrity sha512-upWFJOmDdHN0syLuESuvXDmrRcWd1QafJolHskzaw79uZa7/x53gxQKiR07W59GWY1tFhhU/Th9DrtSfpS782g== +yargs@^3.32.0: + version "3.32.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" + integrity sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU= dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.2" - yargs-parser "^20.2.2" + camelcase "^2.0.1" + cliui "^3.0.3" + decamelize "^1.1.1" + os-locale "^1.4.0" + string-width "^1.0.1" + window-size "^0.1.4" + y18n "^3.2.0" yauzl@^2.10.0: version "2.10.0" @@ -16104,11 +14905,6 @@ yauzl@^2.10.0: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" -yeast@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" - integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= - yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" @@ -16138,9 +14934,9 @@ zone.js@~0.10.3: resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.10.3.tgz#3e5e4da03c607c9dcd92e37dd35687a14a140c16" integrity sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg== -zone.js@~0.11.3: - version "0.11.3" - resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.11.3.tgz#255a6313174731cc014d63233ef04fd9858da375" - integrity sha512-Y4hTHoh4VcxU5BDGAqEoOnOiyT254w6CiHtpQxAJUSMZPyVgdbKf+5R7Mwz6xsPhMIeBXk5rTopRZDpjssTCUg== +zone.js@~0.11.4: + version "0.11.4" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.11.4.tgz#0f70dcf6aba80f698af5735cbb257969396e8025" + integrity sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw== dependencies: tslib "^2.0.0" diff --git a/dev-infra/BUILD.bazel b/dev-infra/BUILD.bazel index d3e3bee4f2..b3ca536634 100644 --- a/dev-infra/BUILD.bazel +++ b/dev-infra/BUILD.bazel @@ -1,6 +1,6 @@ -load("@build_bazel_rules_nodejs//:index.bzl", "generated_file_test", "pkg_npm") +load("@build_bazel_rules_nodejs//:index.bzl", "pkg_npm") load("@npm//@bazel/typescript:index.bzl", "ts_library") -load("@npm//@bazel/rollup:index.bzl", "rollup_bundle") +load("//dev-infra:index.bzl", "ng_dev_rolled_up_generated_file") ts_library( name = "cli", @@ -24,30 +24,6 @@ ts_library( ], ) -rollup_bundle( - name = "cli_rollup", - args = [ - "--plugin", - "rollup-plugin-hashbang", - ], - entry_point = ":cli.ts", - format = "cjs", - silent = True, - sourcemap = "false", - deps = [ - ":cli", - # TODO(josephperrott): Determine if this plugin is the best method for ensuring the hashbang - # in both local and published use case. - "@npm//rollup-plugin-hashbang", - ], -) - -generated_file_test( - name = "local_ng_dev", - src = "ng-dev.js", - generated = "cli_rollup", -) - genrule( name = "package-json", srcs = [ @@ -72,17 +48,17 @@ pkg_npm( substitutions = { # angular/angular should not consume it's own packages, so we use # substitutions to replace these in the published version of dev-infra. - "//dev-infra/": "@npm_angular_dev_infra_private//", + "//dev-infra/": "@npm//@angular/dev-infra-private/", "//packages/benchpress": "@npm//@angular/benchpress", "//packages/bazel": "@npm//@angular/bazel", - "//packages/zone.js/bundles:zone.umd.js": "@npm//:node_modules/zone.js/dist/zone.js", + "//packages/zone.js/bundles:zone.umd.js": "@npm//zone.js", "//packages/core": "@npm//@angular/core", "//packages/platform-browser": "@npm//@angular/platform-browser", # This substitution is particularly verbose because we need to make sure # that only things available via Angular Bazel are imported from # tools/defaults.bzl. - "load\(\"//tools:defaults.bzl\", \"ng_module\"\)": "load(\"@npm//@angular/bazel:index.bzl\", \"ng_module\")", + "load\\(\"//tools:defaults.bzl\", \"ng_module\"\\)": "load(\"@npm//@angular/bazel:index.bzl\", \"ng_module\")", }, visibility = ["//visibility:public"], deps = [ @@ -93,3 +69,34 @@ pkg_npm( "//dev-infra/ts-circular-dependencies", ], ) + +# Because the angular/angular repository relies on the local repository for running ng-dev commands, +# the rollup generated javascript files are committed into the repository to be used as node +# scripts. To ensure they stay up to date, they are created using a generated file test. +# +# Currently there are two generated files which are needed +# ng-dev.js - The main script representing ng-dev +# build-worker.js - The worker script for the `ng-dev release build` command, allowing it to run +# in a forked process. +ng_dev_rolled_up_generated_file( + name = "ng-dev", + entry_point = ":cli.ts", + rollup_args = [ + "--plugin", + "rollup-plugin-hashbang", + ], + deps = [ + ":cli", + # TODO(josephperrott): Determine if this plugin is the best method for ensuring the hashbang + # in both local and published use case. + "@npm//rollup-plugin-hashbang", + ], +) + +ng_dev_rolled_up_generated_file( + name = "build-worker", + entry_point = "//dev-infra/release/build:build-worker.ts", + deps = [ + "//dev-infra/release/build", + ], +) diff --git a/dev-infra/benchmark/component_benchmark/component_benchmark.bzl b/dev-infra/benchmark/component_benchmark/component_benchmark.bzl index 62a82272c0..4076a0f363 100644 --- a/dev-infra/benchmark/component_benchmark/component_benchmark.bzl +++ b/dev-infra/benchmark/component_benchmark/component_benchmark.bzl @@ -1,6 +1,7 @@ load("//dev-infra/benchmark/ng_rollup_bundle:ng_rollup_bundle.bzl", "ng_rollup_bundle") load("//tools:defaults.bzl", "ng_module") -load("@npm//@bazel/typescript:index.bzl", "ts_devserver", "ts_library") +load("@npm//@bazel/typescript:index.bzl", "ts_library") +load("@npm//@bazel/concatjs:index.bzl", "concatjs_devserver") load(":benchmark_test.bzl", "benchmark_test") def copy_default_file(origin, destination): @@ -113,7 +114,7 @@ def component_benchmark( tsconfig = "//dev-infra/benchmark/component_benchmark:tsconfig-e2e.json", ) - # Bundle the application (needed by ts_devserver). + # Bundle the application (needed by concatjs_devserver). ng_rollup_bundle( name = app_main, entry_point = entry_point, @@ -130,7 +131,7 @@ def component_benchmark( ) # The server for our application. - ts_devserver( + concatjs_devserver( name = server, bootstrap = ["//packages/zone.js/bundles:zone.umd.js"], port = 4200, diff --git a/dev-infra/benchmark/ng_rollup_bundle/rollup.config-tmpl.js b/dev-infra/benchmark/ng_rollup_bundle/rollup.config-tmpl.js index 305d96256b..e480962cc9 100644 --- a/dev-infra/benchmark/ng_rollup_bundle/rollup.config-tmpl.js +++ b/dev-infra/benchmark/ng_rollup_bundle/rollup.config-tmpl.js @@ -28,8 +28,8 @@ function log_verbose(...m) { const useBuildOptimizer = TMPL_build_optimizer; const bannerFile = TMPL_banner_file; const ivyEnabled = 'TMPL_angular_ivy_enabled' === 'True'; -// `bazel_stamp_file` is a substitution that is applied by `@bazel/rollup`. -const stampDataFile = bazel_stamp_file; +// `bazel_version_file` is a substitution that is applied by `@bazel/rollup`. +const stampDataFile = bazel_version_file; log_verbose(`running with cwd: ${process.cwd()} diff --git a/dev-infra/browsers/README.md b/dev-infra/browsers/README.md index e48e2dcaaa..0ecb57c0ed 100644 --- a/dev-infra/browsers/README.md +++ b/dev-infra/browsers/README.md @@ -1,71 +1,70 @@ # Browser configuration and versioning for testing of Angular -Within the Angular organization, we use Chrome and Firefox to perform most of the local testing, -and rely on Sauce Labs and BrowserStack to do cross-browser testing on our CI. +Within the Angular organization, we use Chrome and Firefox to perform most of the local testing, and rely on Sauce Labs and BrowserStack to do cross-browser testing on our CI. -The version of Chrome used in tests within this monorepo is configured and controlled via -Bazel and `puppeteer`. We manually keep the configuration of these two tools in sync to -create a consistent testing environment across unit, e2e, and integration tests. +The version of Chrome used in tests within this monorepo is configured and controlled via Bazel and `puppeteer`. +We manually keep the configuration of these two tools in sync to create a consistent testing environment across unit, e2e, and integration tests. ## Bazel -Bazel `karma_web_test_suite` and `protractor_web_test_suite` targets will use Chromium -or Firefox provisioned by `//dev-infra/browsers`. The version of Chrome and Firefox are -specified in the `chromium.bzl` and `firefox.bzl` files in `/dev-infra/browsers`. +Bazel `karma_web_test_suite` and `protractor_web_test_suite` targets will use Chromium or Firefox provisioned by `//dev-infra/browsers`. +The version of Chrome and Firefox are specified in the `chromium.bzl` and `firefox.bzl` files in `/dev-infra/browsers`. -The process of updating the Chrome or Firefox version is not straightforward, but below -are dedicated sections for each browser. +The process of updating the Chrome or Firefox version is not straightforward, but below are dedicated sections for each browser. ## Updating Chromium -1) Visit https://chromium.woolyss.com/ and note the version (commit position) of the latest -stable version. +1. Visit https://chromium.woolyss.com/ and note the version (commit position) of the latest stable version. -For example, "Google Chrome 83.0.4103.97 (756066) • Wednesday, 3 Jun 2020". Alternatively, you -can look in https://omahaproxy.appspot.com/. + For example, "Google Chrome 83.0.4103.97 (756066) • Wednesday, 3 Jun 2020". + Alternatively, you can look in https://omahaproxy.appspot.com/. -1) Find the closest commit position number available for each platform in chromium-browser-snapshots: - https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html +2. Find the closest commit position number available for each platform in chromium-browser-snapshots: https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html - For example: - * https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/756066/ - * https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Mac/756053/ - * https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Win/756065/ + For example: + * https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/756066/ + * https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Mac/756053/ + * https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Win/756065/ - You can download Chromium for your local platform and double check that the `--version` matches - up with what you expect. + You can download Chromium for your local platform and double check that the `--version` matches up with what you expect. - For example: + For example: ``` bash $ ~/Downloads/chrome-mac/Chromium.app/Contents/MacOS/Chromium --version Chromium 83.0.4103.0 ``` -2) Update the chrome & chrome driver build numbers in `dev-infra/browsers/chromium/chromium.bzl` -and run either run `bazel query @org_chromium_chromium_amd64//...` to prompt Bazel to calculate -the new `sha256` for each platform binary or determine the new `sha256` values manually. +3. Update the chrome & chrome driver build numbers in `dev-infra/browsers/chromium/chromium.bzl` and either run `bazel query @org_chromium_chromium_amd64//...` to prompt Bazel to calculate the new `sha256` for each platform binary or determine the new `sha256` values manually. Here is an example with `curl` & `shasum`: ``` bash - curl https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/756066/chrome-linux.zip | shasum -a 256 + curl -L https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/756066/chrome-linux.zip | shasum -a 256 ``` ## Puppeteer -Visit https://github.com/puppeteer/puppeteer/blob/master/docs/api.md to determine which version -of puppeteer corresponds to the version of Chrome desired. Then update -`scripts/puppeteer-chrome-versions.js` and all of the puppeteer versions throughout the repo, +1. Visit https://github.com/puppeteer/puppeteer/blob/master/docs/api.md to determine which version of puppeteer corresponds to the version of Chrome desired. -* `package.json` -* `aio/package.json` -* `aio/tools/examples/shared/package.json` +2. Visit https://chromedriver.chromium.org/downloads to determine which version of ChromeDriver should be used for the version of Chrome desired. -and their corresponding `yarn.lock` files. + > NOTE: + > The version of Chrome does not necessarily correspond exactly with the version of ChromeDriver. + > For example, you might have to use ChromeDriver v87.0.4280.x to drive Chrome v87.0.4272.x. + +3. Update `scripts/puppeteer-chromedriver-versions.js` to include and entry with the new version of puppeteer as key and the new version of ChromeDriver as value (as determined in the two previous steps). + +4. Update all of the puppeteer versions throughout the repo: + + * `package.json` + * `aio/package.json` + * `aio/tools/examples/shared/package.json` + + ...and their corresponding `yarn.lock` files. ## Firefox -In order to update Firefox, open the `dev-infra/browsers/firefox/firefox.bzl` file and update -the repository URLs to the desired version. e.g. +In order to update Firefox, open the `dev-infra/browsers/firefox/firefox.bzl` file and update the repository URLs to the desired version. +For example: ```bzl platform_http_file( @@ -77,11 +76,35 @@ platform_http_file( ) ``` -Go to the `urls` property and update the URL by replacing all `78.0` occurrences with the -version you intend to use. Once done, do the same change for other platforms (such as `macos`). +1. Go to the `urls` property and update the URL by replacing all `78.0` occurrences with the version you intend to use. + Once done, do the same change for other platforms (such as `macos`). -Finally, update the `sha256`checksum of the browser archives. You can do this by downloading the -artifacts from the URLs you just updated, and then running on those files: `sha256 `. +2. Update the `sha256` checksum of the browser archives. + You can do this by downloading the artifacts from the URLs you just updated, and then running `shasum` on those files: + ```sh + curl -L | shasum -a 256 + ``` +In the same file, you can also update the version of gecko driver (the WebDriver implementation for Firefox browsers). +1. Go to https://firefox-source-docs.mozilla.org/testing/geckodriver/Support.html and find a version that is compatible with the used version of Firefox. +2. Update the `geckodriver` repository URLs to the desired version: + + ```bzl + platform_http_file( + name = "org_mozilla_geckodriver_amd64", + licenses = ["reciprocal"], # MPL 2.0 + sha256 = "d59ca434d8e41ec1e30dd7707b0c95171dd6d16056fb6db9c978449ad8b93cc0", + # Geckodriver v0.26.0 + urls = ["https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-linux64.tar.gz"], + ) + ``` + + For example, replace all occurrences of `0.26.0` with the newer version. + +3. Update the `sha256` checksum of the driver archives. + You can do this by downloading the artifacts from the URLs you just updated, and then running `shasum` on those files: + ```sh + curl -L | shasum -a 256 + ``` diff --git a/dev-infra/browsers/chromium/chromium.bzl b/dev-infra/browsers/chromium/chromium.bzl index 4b2b6d1d78..0f3747a438 100644 --- a/dev-infra/browsers/chromium/chromium.bzl +++ b/dev-infra/browsers/chromium/chromium.bzl @@ -12,47 +12,47 @@ def define_chromium_repositories(): platform_http_file( name = "org_chromium_chromium_amd64", licenses = ["notice"], # BSD 3-clause (maybe more?) - sha256 = "0e303931d9c3e065a160f5d31f1178c647f0748fb0b58b1945b84b04fe1c1165", - # 84.0.4147 - urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/768968/chrome-linux.zip"], + sha256 = "36759ed6d151645d00a3a015200334edc70188b422eec51bcaa5790c8e906e27", + # 87.0.4280 + urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/812847/chrome-linux.zip"], ) platform_http_file( name = "org_chromium_chromium_macos", licenses = ["notice"], # BSD 3-clause (maybe more?) - sha256 = "39118c96db1b3fdb0129f434912a329c5ca07d3a1c6c6cda673d3383d83e2f9a", - # 84.0.4147 - urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac/768968/chrome-mac.zip"], + sha256 = "e10533c84ef57232975d6bde9cd28fd0354371e9556dda85e01178e6dcd56b93", + # 87.0.4280 + urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac/812851/chrome-mac.zip"], ) platform_http_file( name = "org_chromium_chromium_windows", licenses = ["notice"], # BSD 3-clause (maybe more?) - sha256 = "3429746fa80c917c6f4d5d96aba4e58894b905a2b8392e43ddb470c5ba612d60", - # 84.0.4147 - urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Win/768975/chrome-win.zip"], + sha256 = "40d0dec1892d729db2f7d8f27feff762b070a02f04d4e14f4e37b97d6b7c3c8f", + # 87.0.4280 + urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Win/812822/chrome-win.zip"], ) platform_http_file( name = "org_chromium_chromedriver_amd64", licenses = ["reciprocal"], # BSD 3-clause, ICU, MPL 1.1, libpng (BSD/MIT-like), Academic Free License v. 2.0, BSD 2-clause, MIT - sha256 = "f6b9852031d185739a2c1816508fe8158eb92782d13e831b8345957ef2506fe8", - # 84.0.4147 - urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/768968/chromedriver_linux64.zip"], + sha256 = "d859f8ecb21e26d3ddaf3f229da695bc86512f4e6c9fe32533af7a8b36783ec5", + # 87.0.4280 + urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/812847/chromedriver_linux64.zip"], ) platform_http_file( name = "org_chromium_chromedriver_macos", licenses = ["reciprocal"], # BSD 3-clause, ICU, MPL 1.1, libpng (BSD/MIT-like), Academic Free License v. 2.0, BSD 2-clause, MIT - sha256 = "aa0124085146556d5d32ad172670e5dcef79b7429380112ad02898047ba7a8b7", - # 84.0.4147 - urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac/768968/chromedriver_mac64.zip"], + sha256 = "aa7a99fa23287725d7108cc07baa94e6f0ef4171ff7b134018387a939a67d93d", + # 87.0.4280 + urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac/812851/chromedriver_mac64.zip"], ) platform_http_file( name = "org_chromium_chromedriver_windows", licenses = ["reciprocal"], # BSD 3-clause, ICU, MPL 1.1, libpng (BSD/MIT-like), Academic Free License v. 2.0, BSD 2-clause, MIT - sha256 = "c4b04fd263e757d3aa99c596832f2c414f9f00e80d2769590e2b9044072b140e", - # 84.0.4147 - urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Win/768975/chromedriver_win32.zip"], + sha256 = "826f2bd0c50b823e7642860ed08cacf69d3756002a71ac30cdd77c68f31d2d24", + # 87.0.4280 + urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Win/812822/chromedriver_win32.zip"], ) diff --git a/dev-infra/browsers/firefox/firefox.bzl b/dev-infra/browsers/firefox/firefox.bzl index cb0153f2fe..7b112767be 100644 --- a/dev-infra/browsers/firefox/firefox.bzl +++ b/dev-infra/browsers/firefox/firefox.bzl @@ -12,31 +12,31 @@ def define_firefox_repositories(): platform_http_file( name = "org_mozilla_firefox_amd64", licenses = ["reciprocal"], # MPL 2.0 - sha256 = "bde6e020556a21561e4b8d7aaecf8db7077951f179b98ca5d0305435bc6802c9", - # Firefox v78.0 - urls = ["https://ftp.mozilla.org/pub/firefox/releases/78.0/linux-x86_64/en-US/firefox-78.0.tar.bz2"], + sha256 = "601e5a9a12ce680ecd82177c7887dae008d8f33690da43be1a690b76563cd992", + # Firefox v84.0 + urls = ["https://ftp.mozilla.org/pub/firefox/releases/84.0/linux-x86_64/en-US/firefox-84.0.tar.bz2"], ) platform_http_file( name = "org_mozilla_firefox_macos", licenses = ["reciprocal"], # MPL 2.0 - sha256 = "69a0ae139814cc314d0c5e3fd3859e0ac9de8517550d7d32b06c57022a14f49e", - # Firefox v78.0 - urls = ["https://ftp.mozilla.org/pub/firefox/releases/78.0/mac/en-US/Firefox%2078.0.dmg"], + sha256 = "4c7bca050eb228f4f6f93a9895af0a87473e03c67401d1d2f1ba907faf87fefd", + # Firefox v84.0 + urls = ["https://ftp.mozilla.org/pub/firefox/releases/84.0/mac/en-US/Firefox%2084.0.dmg"], ) platform_http_file( name = "org_mozilla_geckodriver_amd64", licenses = ["reciprocal"], # MPL 2.0 - sha256 = "d59ca434d8e41ec1e30dd7707b0c95171dd6d16056fb6db9c978449ad8b93cc0", - # Geckodriver v0.26.0 - urls = ["https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-linux64.tar.gz"], + sha256 = "61bfc547a623d7305256611a81ecd24e6bf9dac555529ed6baeafcf8160900da", + # Geckodriver v0.28.0 + urls = ["https://github.com/mozilla/geckodriver/releases/download/v0.28.0/geckodriver-v0.28.0-linux64.tar.gz"], ) platform_http_file( name = "org_mozilla_geckodriver_macos", licenses = ["reciprocal"], # MPL 2.0 - sha256 = "4739ef8f8af5d89bd4a8015788b4dc45c2f5f16b2fdc001254c9a92fe7261947", - # Geckodriver v0.26.0 - urls = ["https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-macos.tar.gz"], + sha256 = "c288ff6db39adfd5eea0e25b4c3e71bfd9fb383eccf521cdd65f67ea78eb1761", + # Geckodriver v0.28.0 + urls = ["https://github.com/mozilla/geckodriver/releases/download/v0.28.0/geckodriver-v0.28.0-macos.tar.gz"], ) diff --git a/dev-infra/commit-message/BUILD.bazel b/dev-infra/commit-message/BUILD.bazel index ed4abcc30a..3b4dddaa3d 100644 --- a/dev-infra/commit-message/BUILD.bazel +++ b/dev-infra/commit-message/BUILD.bazel @@ -11,10 +11,14 @@ ts_library( visibility = ["//dev-infra:__subpackages__"], deps = [ "//dev-infra/utils", + "@npm//@types/conventional-commits-parser", + "@npm//@types/git-raw-commits", "@npm//@types/inquirer", "@npm//@types/node", "@npm//@types/shelljs", "@npm//@types/yargs", + "@npm//conventional-commits-parser", + "@npm//git-raw-commits", "@npm//inquirer", "@npm//shelljs", "@npm//yargs", diff --git a/dev-infra/commit-message/builder.spec.ts b/dev-infra/commit-message/builder.spec.ts deleted file mode 100644 index 5b45ac4140..0000000000 --- a/dev-infra/commit-message/builder.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import * as config from '../utils/config'; -import * as console from '../utils/console'; - -import {buildCommitMessage} from './builder'; - - -describe('commit message building:', () => { - beforeEach(() => { - // stub logging calls to prevent noise in test log - spyOn(console, 'info').and.stub(); - // provide a configuration for DevInfra when loaded - spyOn(config, 'getConfig').and.returnValue({ - commitMessage: { - scopes: ['core'], - } - } as any); - }); - - it('creates a commit message with a scope', async () => { - buildPromptResponseSpies('fix', 'core', 'This is a summary'); - - expect(await buildCommitMessage()).toMatch(/^fix\(core\): This is a summary/); - }); - - it('creates a commit message without a scope', async () => { - buildPromptResponseSpies('build', false, 'This is a summary'); - - expect(await buildCommitMessage()).toMatch(/^build: This is a summary/); - }); -}); - - -/** Create spies to return the mocked selections from prompts. */ -function buildPromptResponseSpies(type: string, scope: string|false, summary: string) { - spyOn(console, 'promptAutocomplete') - .and.returnValues(Promise.resolve(type), Promise.resolve(scope)); - spyOn(console, 'promptInput').and.returnValue(Promise.resolve(summary)); -} diff --git a/dev-infra/commit-message/builder.ts b/dev-infra/commit-message/builder.ts deleted file mode 100644 index f663f3619d..0000000000 --- a/dev-infra/commit-message/builder.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -import {ListChoiceOptions} from 'inquirer'; - -import {info, promptAutocomplete, promptInput} from '../utils/console'; - -import {COMMIT_TYPES, CommitType, getCommitMessageConfig, ScopeRequirement} from './config'; - -/** Validate commit message at the provided file path. */ -export async function buildCommitMessage() { - // TODO(josephperrott): Add support for skipping wizard with local untracked config file - // TODO(josephperrott): Add default commit message information/commenting into generated messages - info('Just a few questions to start building the commit message!'); - - /** The commit message type. */ - const type = await promptForCommitMessageType(); - /** The commit message scope. */ - const scope = await promptForCommitMessageScopeForType(type); - /** The commit message summary. */ - const summary = await promptForCommitMessageSummary(); - - return `${type.name}${scope ? '(' + scope + ')' : ''}: ${summary}\n\n`; -} - -/** Prompts in the terminal for the commit message's type. */ -async function promptForCommitMessageType(): Promise { - info('The type of change in the commit. Allows a reader to know the effect of the change,'); - info('whether it brings a new feature, adds additional testing, documents the `project, etc.'); - - /** List of commit type options for the autocomplete prompt. */ - const typeOptions: ListChoiceOptions[] = - Object.values(COMMIT_TYPES).map(({description, name}) => { - return { - name: `${name} - ${description}`, - value: name, - short: name, - }; - }); - /** The key of a commit message type, selected by the user via prompt. */ - const typeName = await promptAutocomplete('Select a type for the commit:', typeOptions); - - return COMMIT_TYPES[typeName]; -} - -/** Prompts in the terminal for the commit message's scope. */ -async function promptForCommitMessageScopeForType(type: CommitType): Promise { - // If the commit type's scope requirement is forbidden, return early. - if (type.scope === ScopeRequirement.Forbidden) { - info(`Skipping scope selection as the '${type.name}' type does not allow scopes`); - return false; - } - /** Commit message configuration */ - const config = getCommitMessageConfig(); - - info('The area of the repository the changes in this commit most affects.'); - return await promptAutocomplete( - 'Select a scope for the commit:', config.commitMessage.scopes, - type.scope === ScopeRequirement.Optional ? '' : ''); -} - -/** Prompts in the terminal for the commit message's summary. */ -async function promptForCommitMessageSummary(): Promise { - info('Provide a short summary of what the changes in the commit do'); - return await promptInput('Provide a short summary of the commit'); -} diff --git a/dev-infra/commit-message/cli.ts b/dev-infra/commit-message/cli.ts index f952ab569c..0ed65b71e3 100644 --- a/dev-infra/commit-message/cli.ts +++ b/dev-infra/commit-message/cli.ts @@ -10,14 +10,12 @@ import * as yargs from 'yargs'; import {RestoreCommitMessageModule} from './restore-commit-message/cli'; import {ValidateFileModule} from './validate-file/cli'; import {ValidateRangeModule} from './validate-range/cli'; -import {WizardModule} from './wizard/cli'; /** Build the parser for the commit-message commands. */ export function buildCommitMessageParser(localYargs: yargs.Argv) { return localYargs.help() .strict() .command(RestoreCommitMessageModule) - .command(WizardModule) .command(ValidateFileModule) .command(ValidateRangeModule); } diff --git a/dev-infra/commit-message/commit-message-draft.ts b/dev-infra/commit-message/commit-message-draft.ts deleted file mode 100644 index 86a5655fd0..0000000000 --- a/dev-infra/commit-message/commit-message-draft.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -import {existsSync, readFileSync, unlinkSync, writeFileSync} from 'fs'; - -/** Load the commit message draft from the file system if it exists. */ -export function loadCommitMessageDraft(basePath: string) { - const commitMessageDraftPath = `${basePath}.ngDevSave`; - if (existsSync(commitMessageDraftPath)) { - return readFileSync(commitMessageDraftPath).toString(); - } - return ''; -} - -/** Remove the commit message draft from the file system. */ -export function deleteCommitMessageDraft(basePath: string) { - const commitMessageDraftPath = `${basePath}.ngDevSave`; - if (existsSync(commitMessageDraftPath)) { - unlinkSync(commitMessageDraftPath); - } -} - -/** Save the commit message draft to the file system for later retrieval. */ -export function saveCommitMessageDraft(basePath: string, commitMessage: string) { - writeFileSync(`${basePath}.ngDevSave`, commitMessage); -} diff --git a/dev-infra/commit-message/commit-message-source.ts b/dev-infra/commit-message/commit-message-source.ts deleted file mode 100644 index c8a9640e83..0000000000 --- a/dev-infra/commit-message/commit-message-source.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -/** - * The source triggering the git commit message creation. - * As described in: https://git-scm.com/docs/githooks#_prepare_commit_msg - */ -export type CommitMsgSource = 'message'|'template'|'merge'|'squash'|'commit'; diff --git a/dev-infra/commit-message/config.ts b/dev-infra/commit-message/config.ts index 613c59decb..53994fae4e 100644 --- a/dev-infra/commit-message/config.ts +++ b/dev-infra/commit-message/config.ts @@ -31,7 +31,7 @@ export function getCommitMessageConfig() { return config as Required; } -/** Scope requirement level to be set for each commit type. */ +/** Scope requirement level to be set for each commit type. */ export enum ScopeRequirement { Required, Optional, diff --git a/dev-infra/commit-message/parse.spec.ts b/dev-infra/commit-message/parse.spec.ts index 22a79934a9..b1ec355a81 100644 --- a/dev-infra/commit-message/parse.spec.ts +++ b/dev-infra/commit-message/parse.spec.ts @@ -6,27 +6,40 @@ * found in the LICENSE file at https://angular.io/license */ -import {parseCommitMessage, ParsedCommitMessage} from './parse'; +import {parseCommitMessage} from './parse'; const commitValues = { prefix: '', type: 'fix', + npmScope: '', scope: 'changed-area', summary: 'This is a short summary of the change', - body: 'This is a longer description of the change Closes #1', + body: 'This is a longer description of the change', + footer: 'Closes #1', }; -function buildCommitMessage(params = {}) { - const {prefix, type, scope, summary, body} = {...commitValues, ...params}; - return `${prefix}${type}${scope ? '(' + scope + ')' : ''}: ${summary}\n\n${body}`; +function buildCommitMessage(params: Partial = {}) { + const {prefix, npmScope, type, scope, summary, body, footer} = {...commitValues, ...params}; + const scopeSlug = npmScope ? `${npmScope}/${scope}` : scope; + return `${prefix}${type}${scopeSlug ? '(' + scopeSlug + ')' : ''}: ${summary}\n\n${body}\n\n${ + footer}`; } describe('commit message parsing:', () => { - it('parses the scope', () => { - const message = buildCommitMessage(); - expect(parseCommitMessage(message).scope).toBe(commitValues.scope); + describe('parses the scope', () => { + it('when only a scope is defined', () => { + const message = buildCommitMessage(); + expect(parseCommitMessage(message).scope).toBe(commitValues.scope); + expect(parseCommitMessage(message).npmScope).toBe(''); + }); + + it('when an npmScope and scope are defined', () => { + const message = buildCommitMessage({npmScope: 'myNpmPackage'}); + expect(parseCommitMessage(message).scope).toBe(commitValues.scope); + expect(parseCommitMessage(message).npmScope).toBe('myNpmPackage'); + }); }); it('parses the type', () => { @@ -45,12 +58,6 @@ describe('commit message parsing:', () => { expect(parseCommitMessage(message).body).toBe(commitValues.body); }); - it('parses the body without Github linking', () => { - const body = 'This has linking\nCloses #1'; - const message = buildCommitMessage({body}); - expect(parseCommitMessage(message).bodyWithoutLinking).toBe('This has linking\n'); - }); - it('parses the subject', () => { const message = buildCommitMessage(); expect(parseCommitMessage(message).subject).toBe(commitValues.summary); @@ -100,6 +107,71 @@ describe('commit message parsing:', () => { expect(parsedMessage.body) .toBe( 'This is line 1 of the actual body.\n' + - 'This is line 2 of the actual body (and it also contains a # but it not a comment).\n'); + 'This is line 2 of the actual body (and it also contains a # but it not a comment).'); + }); + + describe('parses breaking change notes', () => { + const summary = 'This breaks things'; + const description = 'This is how it breaks things.'; + + it('when only a summary is provided', () => { + const message = buildCommitMessage({ + footer: `BREAKING CHANGE: ${summary}`, + }); + const parsedMessage = parseCommitMessage(message); + expect(parsedMessage.breakingChanges[0].text).toBe(summary); + expect(parsedMessage.breakingChanges.length).toBe(1); + }); + + it('when only a description is provided', () => { + const message = buildCommitMessage({ + footer: `BREAKING CHANGE:\n\n${description}`, + }); + const parsedMessage = parseCommitMessage(message); + expect(parsedMessage.breakingChanges[0].text).toBe(description); + expect(parsedMessage.breakingChanges.length).toBe(1); + }); + + it('when a summary and description are provied', () => { + const message = buildCommitMessage({ + footer: `BREAKING CHANGE: ${summary}\n\n${description}`, + }); + const parsedMessage = parseCommitMessage(message); + expect(parsedMessage.breakingChanges[0].text).toBe(`${summary}\n\n${description}`); + expect(parsedMessage.breakingChanges.length).toBe(1); + }); + }); + + describe('parses deprecation notes', () => { + const summary = 'This will break things later'; + const description = 'This is a long winded explanation of why it \nwill break things later.'; + + + it('when only a summary is provided', () => { + const message = buildCommitMessage({ + footer: `DEPRECATED: ${summary}`, + }); + const parsedMessage = parseCommitMessage(message); + expect(parsedMessage.deprecations[0].text).toBe(summary); + expect(parsedMessage.deprecations.length).toBe(1); + }); + + it('when only a description is provided', () => { + const message = buildCommitMessage({ + footer: `DEPRECATED:\n\n${description}`, + }); + const parsedMessage = parseCommitMessage(message); + expect(parsedMessage.deprecations[0].text).toBe(description); + expect(parsedMessage.deprecations.length).toBe(1); + }); + + it('when a summary and description are provied', () => { + const message = buildCommitMessage({ + footer: `DEPRECATED: ${summary}\n\n${description}`, + }); + const parsedMessage = parseCommitMessage(message); + expect(parsedMessage.deprecations[0].text).toBe(`${summary}\n\n${description}`); + expect(parsedMessage.deprecations.length).toBe(1); + }); }); }); diff --git a/dev-infra/commit-message/parse.ts b/dev-infra/commit-message/parse.ts index bbd95502e7..a630de2ad2 100644 --- a/dev-infra/commit-message/parse.ts +++ b/dev-infra/commit-message/parse.ts @@ -6,101 +6,147 @@ * found in the LICENSE file at https://angular.io/license */ -import {exec} from '../utils/shelljs'; +import {Commit as ParsedCommit, Options, sync as parse} from 'conventional-commits-parser'; -/** A parsed commit message. */ -export interface ParsedCommitMessage { + +/** A parsed commit, containing the information needed to validate the commit. */ +export interface Commit { + /** The full raw text of the commit. */ + fullText: string; + /** The header line of the commit, will be used in the changelog entries. */ header: string; + /** The full body of the commit, not including the footer. */ body: string; - bodyWithoutLinking: string; + /** The footer of the commit, containing issue references and note sections. */ + footer: string; + /** A list of the references to other issues made throughout the commit message. */ + references: ParsedCommit.Reference[]; + /** The type of the commit message. */ type: string; + /** The scope of the commit message. */ scope: string; + /** The npm scope of the commit message. */ + npmScope: string; + /** The subject of the commit message. */ subject: string; + /** A list of breaking change notes in the commit message. */ + breakingChanges: ParsedCommit.Note[]; + /** A list of deprecation notes in the commit message. */ + deprecations: ParsedCommit.Note[]; + /** Whether the commit is a fixup commit. */ isFixup: boolean; + /** Whether the commit is a squash commit. */ isSquash: boolean; + /** Whether the commit is a revert commit. */ isRevert: boolean; } +/** + * A list of tuples expressing the fields to extract from each commit log entry. The tuple contains + * two values, the first is the key for the property and the second is the template shortcut for the + * git log command. + */ +const commitFields = { + hash: '%H', + shortHash: '%h', + author: '%aN', +}; +/** The additional fields to be included in commit log entries for parsing. */ +export type CommitFields = typeof commitFields; +/** The commit fields described as git log format entries for parsing. */ +export const commitFieldsAsFormat = (fields: CommitFields) => { + return Object.entries(fields).map(([key, value]) => `%n-${key}-%n${value}`).join(''); +}; +/** + * The git log format template to create git log entries for parsing. + * + * The conventional commits parser expects to parse the standard git log raw body (%B) into its + * component parts. Additionally it will parse additional fields with keys defined by + * `-{key name}-` separated by new lines. + * */ +export const gitLogFormatForParsing = `%B${commitFieldsAsFormat(commitFields)}`; +/** Markers used to denote the start of a note section in a commit. */ +enum NoteSections { + BREAKING_CHANGE = 'BREAKING CHANGE', + DEPRECATED = 'DEPRECATED', +} /** Regex determining if a commit is a fixup. */ const FIXUP_PREFIX_RE = /^fixup! /i; -/** Regex finding all github keyword links. */ -const GITHUB_LINKING_RE = /((closed?s?)|(fix(es)?(ed)?)|(resolved?s?))\s\#(\d+)/ig; /** Regex determining if a commit is a squash. */ const SQUASH_PREFIX_RE = /^squash! /i; /** Regex determining if a commit is a revert. */ const REVERT_PREFIX_RE = /^revert:? /i; -/** Regex determining the scope of a commit if provided. */ -const TYPE_SCOPE_RE = /^(\w+)(?:\(([^)]+)\))?\:\s(.+)$/; -/** Regex determining the entire header line of the commit. */ -const COMMIT_HEADER_RE = /^(.*)/i; -/** Regex determining the body of the commit. */ -const COMMIT_BODY_RE = /^.*\n\n([\s\S]*)$/; +/** + * Regex pattern for parsing the header line of a commit. + * + * Several groups are being matched to be used in the parsed commit object, being mapped to the + * `headerCorrespondence` object. + * + * The pattern can be broken down into component parts: + * - `(\w+)` - a capturing group discovering the type of the commit. + * - `(?:\((?:([^/]+)\/)?([^)]+)\))?` - a pair of capturing groups to capture the scope and, + * optionally the npmScope of the commit. + * - `(.*)` - a capturing group discovering the subject of the commit. + */ +const headerPattern = /^(\w+)(?:\((?:([^/]+)\/)?([^)]+)\))?: (.*)$/; +/** + * The property names used for the values extracted from the header via the `headerPattern` regex. + */ +const headerCorrespondence = ['type', 'npmScope', 'scope', 'subject']; +/** + * Configuration options for the commit parser. + * + * NOTE: An extended type from `Options` must be used because the current + * @types/conventional-commits-parser version does not include the `notesPattern` field. + */ +const parseOptions: Options&{notesPattern: (keywords: string) => RegExp} = { + commentChar: '#', + headerPattern, + headerCorrespondence, + noteKeywords: [NoteSections.BREAKING_CHANGE, NoteSections.DEPRECATED], + notesPattern: (keywords: string) => new RegExp(`(${keywords})(?:: ?)(.*)`), +}; + /** Parse a full commit message into its composite parts. */ -export function parseCommitMessage(commitMsg: string): ParsedCommitMessage { - // Ignore comments (i.e. lines starting with `#`). Comments are automatically removed by git and - // should not be considered part of the final commit message. - commitMsg = commitMsg.split('\n').filter(line => !line.startsWith('#')).join('\n'); +export function parseCommitMessage(fullText: string|Buffer): Commit { + // Ensure the fullText symbol is a `string`, even if a Buffer was provided. + fullText = fullText.toString(); + /** The commit message text with the fixup and squash markers stripped out. */ + const strippedCommitMsg = fullText.replace(FIXUP_PREFIX_RE, '') + .replace(SQUASH_PREFIX_RE, '') + .replace(REVERT_PREFIX_RE, ''); + /** The initially parsed commit. */ + const commit = parse(strippedCommitMsg, parseOptions); + /** A list of breaking change notes from the commit. */ + const breakingChanges: ParsedCommit.Note[] = []; + /** A list of deprecation notes from the commit. */ + const deprecations: ParsedCommit.Note[] = []; - let header = ''; - let body = ''; - let bodyWithoutLinking = ''; - let type = ''; - let scope = ''; - let subject = ''; + // Extract the commit message notes by marked types into their respective lists. + commit.notes.forEach((note: ParsedCommit.Note) => { + if (note.title === NoteSections.BREAKING_CHANGE) { + return breakingChanges.push(note); + } + if (note.title === NoteSections.DEPRECATED) { + return deprecations.push(note); + } + }); - 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), + fullText, + breakingChanges, + deprecations, + body: commit.body || '', + footer: commit.footer || '', + header: commit.header || '', + references: commit.references, + scope: commit.scope || '', + subject: commit.subject || '', + type: commit.type || '', + npmScope: commit.npmScope || '', + isFixup: FIXUP_PREFIX_RE.test(fullText), + isSquash: SQUASH_PREFIX_RE.test(fullText), + isRevert: REVERT_PREFIX_RE.test(fullText), }; } - -/** Retrieve and parse each commit message in a provide range. */ -export function parseCommitMessagesForRange(range: string): ParsedCommitMessage[] { - /** A random number used as a 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}`); - if (result.code) { - throw new Error(`Failed to get all commits in the range:\n ${result.stderr}`); - } - - return result - // Separate the commits from a single string into individual commits. - .split(randomValueSeparator) - // Remove extra space before and after each commit message. - .map(l => l.trim()) - // Remove any superfluous lines which remain from the split. - .filter(line => !!line) - // Parse each commit message. - .map(commit => parseCommitMessage(commit)); -} diff --git a/dev-infra/commit-message/restore-commit-message/cli.ts b/dev-infra/commit-message/restore-commit-message/cli.ts index 6f4a2dacdc..785ecaaa77 100644 --- a/dev-infra/commit-message/restore-commit-message/cli.ts +++ b/dev-infra/commit-message/restore-commit-message/cli.ts @@ -8,7 +8,7 @@ import {Arguments, Argv, CommandModule} from 'yargs'; -import {CommitMsgSource} from '../commit-message-source'; +import {CommitMsgSource} from './commit-message-source'; import {restoreCommitMessage} from './restore-commit-message'; @@ -50,11 +50,11 @@ async function handler({fileEnvVariable, file, source}: Arguments = { handler, builder, diff --git a/dev-infra/commit-message/restore-commit-message/restore-commit-message.ts b/dev-infra/commit-message/restore-commit-message/restore-commit-message.ts index 8008728d0b..d2551732be 100644 --- a/dev-infra/commit-message/restore-commit-message/restore-commit-message.ts +++ b/dev-infra/commit-message/restore-commit-message/restore-commit-message.ts @@ -10,8 +10,8 @@ import {writeFileSync} from 'fs'; import {debug, log} from '../../utils/console'; -import {loadCommitMessageDraft} from '../commit-message-draft'; -import {CommitMsgSource} from '../commit-message-source'; +import {loadCommitMessageDraft} from './commit-message-draft'; +import {CommitMsgSource} from './commit-message-source'; /** * Restore the commit message draft to the git to be used as the default commit message. diff --git a/dev-infra/commit-message/validate-file/cli.ts b/dev-infra/commit-message/validate-file/cli.ts index 602b3145cd..82f9800c3e 100644 --- a/dev-infra/commit-message/validate-file/cli.ts +++ b/dev-infra/commit-message/validate-file/cli.ts @@ -53,7 +53,7 @@ async function handler({error, file, fileEnvVariable}: Arguments = { handler, builder, diff --git a/dev-infra/commit-message/validate-file/validate-file.ts b/dev-infra/commit-message/validate-file/validate-file.ts index 459fa52fa9..965b0c6a5c 100644 --- a/dev-infra/commit-message/validate-file/validate-file.ts +++ b/dev-infra/commit-message/validate-file/validate-file.ts @@ -11,7 +11,7 @@ import {resolve} from 'path'; import {getRepoBaseDir} from '../../utils/config'; import {error, green, info, log, red, yellow} from '../../utils/console'; -import {deleteCommitMessageDraft, saveCommitMessageDraft} from '../commit-message-draft'; +import {deleteCommitMessageDraft, saveCommitMessageDraft} from '../restore-commit-message/commit-message-draft'; import {printValidationErrors, validateCommitMessage} from '../validate'; /** Validate commit message at the provided file path. */ diff --git a/dev-infra/commit-message/validate-range/cli.ts b/dev-infra/commit-message/validate-range/cli.ts index 6eea3ff95e..2878dc1b7a 100644 --- a/dev-infra/commit-message/validate-range/cli.ts +++ b/dev-infra/commit-message/validate-range/cli.ts @@ -14,21 +14,27 @@ import {validateCommitRange} from './validate-range'; export interface ValidateRangeOptions { - range: string; + startingRef: string; + endingRef: string; } /** Builds the command. */ function builder(yargs: Argv) { - return yargs.option('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, - }); + return yargs + .positional('startingRef', { + description: 'The first ref in the range to select', + type: 'string', + demandOption: true, + }) + .positional('endingRef', { + description: 'The last ref in the range to select', + type: 'string', + default: 'HEAD', + }); } /** Handles the command. */ -async function handler({range}: Arguments) { +async function handler({startingRef, endingRef}: Arguments) { // If on CI, and no 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') { @@ -38,13 +44,13 @@ async function handler({range}: Arguments) { info(`Skipping check of provided commit range`); return; } - validateCommitRange(range); + await validateCommitRange(startingRef, endingRef); } -/** yargs command module describing the command. */ +/** yargs command module describing the command. */ export const ValidateRangeModule: CommandModule<{}, ValidateRangeOptions> = { handler, builder, - command: 'validate-range', + command: 'validate-range [ending-ref]', describe: 'Validate a range of commit messages', }; diff --git a/dev-infra/commit-message/validate-range/validate-range.ts b/dev-infra/commit-message/validate-range/validate-range.ts index 56158e74de..5f506b9916 100644 --- a/dev-infra/commit-message/validate-range/validate-range.ts +++ b/dev-infra/commit-message/validate-range/validate-range.ts @@ -5,24 +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 {error, info} from '../../utils/console'; - -import {parseCommitMessagesForRange, ParsedCommitMessage} from '../parse'; +import {error, green, info, red} from '../../utils/console'; +import {Commit} from '../parse'; +import {getCommitsInRange} from '../utils'; import {printValidationErrors, validateCommitMessage, ValidateCommitMessageOptions} from '../validate'; // Whether the provided commit is a fixup commit. -const isNonFixup = (commit: ParsedCommitMessage) => !commit.isFixup; +const isNonFixup = (commit: Commit) => !commit.isFixup; // Extracts commit header (first line of commit message). -const extractCommitHeader = (commit: ParsedCommitMessage) => commit.header; +const extractCommitHeader = (commit: Commit) => commit.header; /** Validate all commits in a provided git commit range. */ -export function validateCommitRange(range: string) { +export async function validateCommitRange(from: string, to: string) { /** A list of tuples of the commit header string and a list of error messages for the commit. */ const errors: [commitHeader: string, errors: string[]][] = []; + /** A list of parsed commit messages from the range. */ - const commits = parseCommitMessagesForRange(range); - info(`Examining ${commits.length} commit(s) in the provided range: ${range}`); + const commits = await getCommitsInRange(from, to); + info(`Examining ${commits.length} commit(s) in the provided range: ${from}..${to}`); /** * Whether all commits in the range are valid, commits are allowed to be fixup commits for other @@ -33,7 +34,7 @@ export function validateCommitRange(range: string) { disallowSquash: true, nonFixupCommitHeaders: isNonFixup(commit) ? undefined : - commits.slice(0, i).filter(isNonFixup).map(extractCommitHeader) + commits.slice(i + 1).filter(isNonFixup).map(extractCommitHeader) }; const {valid, errors: localErrors} = validateCommitMessage(commit, options); if (localErrors.length) { @@ -43,9 +44,9 @@ export function validateCommitRange(range: string) { }); if (allCommitsInRangeValid) { - info('√ All commit messages in range valid.'); + info(green('√ All commit messages in range valid.')); } else { - error('✘ Invalid commit message'); + error(red('✘ Invalid commit message')); errors.forEach(([header, validationErrors]) => { error.group(header); printValidationErrors(validationErrors); diff --git a/dev-infra/commit-message/validate.spec.ts b/dev-infra/commit-message/validate.spec.ts index 24fb996698..0d92bdccb2 100644 --- a/dev-infra/commit-message/validate.spec.ts +++ b/dev-infra/commit-message/validate.spec.ts @@ -23,6 +23,7 @@ const config: {commitMessage: CommitMessageConfig} = { 'compiler', 'core', 'packaging', + '@angular-devkit/build-angular', ] } }; @@ -63,7 +64,7 @@ describe('validate-commit-message.js', () => { }); it('should skip max length limit for URLs', () => { - const msg = 'fix(compiler): this is just an usual commit message tile\n\n' + + const msg = 'fix(compiler): this is just a usual commit message title\n\n' + 'This is a normal commit message body which does not exceed the max length\n' + 'limit. For more details see the following super long URL:\n\n' + 'https://github.com/angular/components/commit/e2ace018ddfad10608e0e32932c43dcfef4095d7#diff-9879d6db96fd29134fc802214163b95a'; @@ -87,6 +88,11 @@ describe('validate-commit-message.js', () => { [`'weird' is not an allowed type.\n => TYPES: ${TYPES}`]); }); + it('should pass when scope contains NPM scope', () => { + expectValidationResult( + validateCommitMessage('fix(@angular-devkit/build-angular): something'), true); + }); + it('should fail when scope is invalid', () => { const errorMessageFor = (scope: string, header: string) => `'${scope}' is not an allowed scope.\n => SCOPES: ${SCOPES}`; @@ -253,6 +259,14 @@ describe('validate-commit-message.js', () => { ]); }); + it('should pass validation even if the total non-header content is longer than `minBodyLength`, even if the body contains a `#` reference usage', + () => { + expectValidationResult( + validateCommitMessage( + 'fix(core): something\n\n Explanation of how #123 motivated this change'), + VALID); + }); + it('should pass validation if the body is shorter than `minBodyLength` but the commit type is in the `minBodyLengthTypeExclusions` list', () => { expectValidationResult(validateCommitMessage('docs: just fixing a typo'), VALID); @@ -263,5 +277,72 @@ describe('validate-commit-message.js', () => { VALID); }); }); + + describe('breaking change', () => { + it('should allow valid breaking change commit descriptions', () => { + const msgWithSummary = 'feat(compiler): this is just a usual commit message title\n\n' + + 'This is a normal commit message body which does not exceed the max length\n' + + 'limit. For more details see the following super long URL:\n\n' + + 'BREAKING CHANGE: This is a summary of a breaking change.'; + expectValidationResult(validateCommitMessage(msgWithSummary), VALID); + + const msgWithDescription = 'feat(compiler): this is just a usual commit message title\n\n' + + 'This is a normal commit message body which does not exceed the max length\n' + + 'limit. For more details see the following super long URL:\n\n' + + 'BREAKING CHANGE:\n\n' + + 'This is a full description of the breaking change.'; + expectValidationResult(validateCommitMessage(msgWithDescription), VALID); + + const msgWithSummaryAndDescription = + 'feat(compiler): this is just a usual commit message title\n\n' + + 'This is a normal commit message body which does not exceed the max length\n' + + 'limit. For more details see the following super long URL:\n\n' + + 'BREAKING CHANGE: This is a summary of a breaking change.\n\n' + + 'This is a full description of the breaking change.'; + expectValidationResult(validateCommitMessage(msgWithSummaryAndDescription), VALID); + + const msgWithNonBreaking = 'feat(compiler): this is just a usual commit message title\n\n' + + 'This is not a\n' + + 'breaking change commit.'; + expectValidationResult(validateCommitMessage(msgWithNonBreaking), VALID); + }); + + it('should fail for non-valid breaking change commit descriptions', () => { + const msgWithSummary = 'feat(compiler): this is just a usual commit message title\n\n' + + 'This is a normal commit message body which does not exceed the max length\n' + + 'limit. For more details see the following super long URL:\n\n' + + 'BREAKING CHANGE This is a summary of a breaking change.'; + expectValidationResult( + validateCommitMessage(msgWithSummary), INVALID, + [`The commit message body contains an invalid breaking change description.`]); + + const msgWithPlural = 'feat(compiler): this is just a usual commit message title\n\n' + + 'This is a normal commit message body which does not exceed the max length\n' + + 'limit. For more details see the following super long URL:\n\n' + + 'BREAKING CHANGES: This is a summary of a breaking change.'; + expectValidationResult( + validateCommitMessage(msgWithPlural), INVALID, + [`The commit message body contains an invalid breaking change description.`]); + + const msgWithDescription = 'feat(compiler): this is just a usual commit message title\n\n' + + 'This is a normal commit message body which does not exceed the max length\n' + + 'limit. For more details see the following super long URL:\n\n' + + 'BREAKING CHANGE:\n' + + 'This is a full description of the breaking change.'; + expectValidationResult( + validateCommitMessage(msgWithDescription), INVALID, + [`The commit message body contains an invalid breaking change description.`]); + + const msgWithSummaryAndDescription = + 'feat(compiler): this is just a usual commit message title\n\n' + + 'This is a normal commit message body which does not exceed the max length\n' + + 'limit. For more details see the following super long URL:\n\n' + + 'BREAKING CHANGE\n\n' + + 'This is a full description of the breaking change.'; + expectValidationResult( + validateCommitMessage(msgWithSummaryAndDescription), INVALID, + [`The commit message body contains an invalid breaking change description.`]); + }); + }); }); }); diff --git a/dev-infra/commit-message/validate.ts b/dev-infra/commit-message/validate.ts index 578f599973..4b630f7a7d 100644 --- a/dev-infra/commit-message/validate.ts +++ b/dev-infra/commit-message/validate.ts @@ -5,10 +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 {error} from '../utils/console'; import {COMMIT_TYPES, getCommitMessageConfig, ScopeRequirement} from './config'; -import {parseCommitMessage, ParsedCommitMessage} from './parse'; +import {Commit, parseCommitMessage} from './parse'; /** Options for commit message validation. */ export interface ValidateCommitMessageOptions { @@ -20,15 +21,25 @@ export interface ValidateCommitMessageOptions { export interface ValidateCommitMessageResult { valid: boolean; errors: string[]; - commit: ParsedCommitMessage; + commit: Commit; } /** Regex matching a URL for an entire commit body line. */ const COMMIT_BODY_URL_LINE_RE = /^https?:\/\/.*$/; +/** + * Regex matching a breaking change. + * + * - Starts with BREAKING CHANGE + * - Followed by a colon + * - Followed by a single space or two consecutive new lines + * + * NB: Anything after `BREAKING CHANGE` is optional to facilitate the validation. + */ +const COMMIT_BODY_BREAKING_CHANGE_RE = /^BREAKING CHANGE(:( |\n{2}))?/m; /** Validate a commit message against using the local repo's config. */ export function validateCommitMessage( - commitMsg: string|ParsedCommitMessage, + commitMsg: string|Commit, options: ValidateCommitMessageOptions = {}): ValidateCommitMessageResult { const config = getCommitMessageConfig().commitMessage; const commit = typeof commitMsg === 'string' ? parseCommitMessage(commitMsg) : commitMsg; @@ -36,8 +47,6 @@ export function validateCommitMessage( /** Perform the validation checks against the parsed commit. */ function validateCommitAndCollectErrors() { - // TODO(josephperrott): Remove early return calls when commit message errors are found - //////////////////////////////////// // Checking revert, squash, fixup // //////////////////////////////////// @@ -107,9 +116,10 @@ export function validateCommitMessage( return false; } - if (commit.scope && !config.scopes.includes(commit.scope)) { + const fullScope = commit.npmScope ? `${commit.npmScope}/${commit.scope}` : commit.scope; + if (fullScope && !config.scopes.includes(fullScope)) { errors.push( - `'${commit.scope}' is not an allowed scope.\n => SCOPES: ${config.scopes.join(', ')}`); + `'${fullScope}' is not an allowed scope.\n => SCOPES: ${config.scopes.join(', ')}`); return false; } @@ -122,26 +132,45 @@ export function validateCommitMessage( // Checking commit body // ////////////////////////// + // Due to an issue in which conventional-commits-parser considers all parts of a commit after + // a `#` reference to be the footer, we check the length of all of the commit content after the + // header. In the future, we expect to be able to check only the body once the parser properly + // handles this case. + const allNonHeaderContent = `${commit.body.trim()}\n${commit.footer.trim()}`; + if (!config.minBodyLengthTypeExcludes?.includes(commit.type) && - commit.bodyWithoutLinking.trim().length < config.minBodyLength) { + allNonHeaderContent.length < config.minBodyLength) { errors.push(`The commit message body does not meet the minimum length of ${ config.minBodyLength} characters`); return false; } const bodyByLine = commit.body.split('\n'); - const lineExceedsMaxLength = bodyByLine.some(line => { + const lineExceedsMaxLength = bodyByLine.some((line: string) => { // Check if any line exceeds the max line length limit. The limit is ignored for // lines that just contain an URL (as these usually cannot be wrapped or shortened). return line.length > config.maxLineLength && !COMMIT_BODY_URL_LINE_RE.test(line); }); if (lineExceedsMaxLength) { - errors.push( - `The commit message body contains lines greater than ${config.maxLineLength} characters`); + errors.push(`The commit message body contains lines greater than ${ + config.maxLineLength} characters.`); return false; } + // Breaking change + // Check if the commit message contains a valid break change description. + // https://github.com/angular/angular/blob/88fbc066775ab1a2f6a8c75f933375b46d8fa9a4/CONTRIBUTING.md#commit-message-footer + const hasBreakingChange = COMMIT_BODY_BREAKING_CHANGE_RE.exec(commit.fullText); + if (hasBreakingChange !== null) { + const [, breakingChangeDescription] = hasBreakingChange; + if (!breakingChangeDescription) { + // Not followed by :, space or two consecutive new lines, + errors.push(`The commit message body contains an invalid breaking change description.`); + return false; + } + } + return true; } @@ -160,4 +189,9 @@ export function printValidationErrors(errors: string[], print = error) { print(); print(''); print(); + print(`BREAKING CHANGE: `); + print(); + print(``); + print(); + print(); } diff --git a/dev-infra/commit-message/wizard/cli.ts b/dev-infra/commit-message/wizard/cli.ts deleted file mode 100644 index b4cc94cfa6..0000000000 --- a/dev-infra/commit-message/wizard/cli.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Arguments, Argv, CommandModule} from 'yargs'; - -import {CommitMsgSource} from '../commit-message-source'; - -import {runWizard} from './wizard'; - - -export interface WizardOptions { - filePath: string; - commitSha: string|undefined; - source: CommitMsgSource|undefined; -} - -/** Builds the command. */ -function builder(yargs: Argv) { - return yargs - .positional('filePath', { - description: 'The file path to write the generated commit message into', - type: 'string', - demandOption: true, - }) - .positional('source', { - choices: ['message', 'template', 'merge', 'squash', 'commit'] as const, - description: 'The source of the commit message as described here: ' + - 'https://git-scm.com/docs/githooks#_prepare_commit_msg' - }) - .positional('commitSha', { - description: 'The commit sha if source is set to `commit`', - type: 'string', - }); -} - -/** Handles the command. */ -async function handler(args: Arguments) { - await runWizard(args); -} - -/** yargs command module describing the command. */ -export const WizardModule: CommandModule<{}, WizardOptions> = { - handler, - builder, - command: 'wizard [source] [commitSha]', - // Description: Run the wizard to build a base commit message before opening to complete. - // No describe is defiend to hide the command from the --help. - describe: false, -}; diff --git a/dev-infra/commit-message/wizard/wizard.ts b/dev-infra/commit-message/wizard/wizard.ts deleted file mode 100644 index 40623fed10..0000000000 --- a/dev-infra/commit-message/wizard/wizard.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -import {writeFileSync} from 'fs'; - -import {getUserConfig} from '../../utils/config'; -import {debug, info} from '../../utils/console'; - -import {buildCommitMessage} from '../builder'; -import {CommitMsgSource} from '../commit-message-source'; - - -/** The default commit message used if the wizard does not procude a commit message. */ -const defaultCommitMessage = `():
    - -# \n\n`; - -export async function runWizard( - args: {filePath: string, source?: CommitMsgSource, commitSha?: string}) { - if (getUserConfig().commitMessage?.disableWizard) { - debug('Skipping commit message wizard due to enabled `commitMessage.disableWizard` option in'); - debug('user config.'); - process.exitCode = 0; - return; - } - - if (args.source !== undefined) { - info(`Skipping commit message wizard because the commit was created via '${ - args.source}' source`); - process.exitCode = 0; - return; - } - - // Set the default commit message to be updated if the user cancels out of the wizard in progress - writeFileSync(args.filePath, defaultCommitMessage); - - /** The generated commit message. */ - const commitMessage = await buildCommitMessage(); - writeFileSync(args.filePath, commitMessage); -} diff --git a/dev-infra/format/formatters/buildifier.ts b/dev-infra/format/formatters/buildifier.ts index 8edb68a71a..9f52e50cd0 100644 --- a/dev-infra/format/formatters/buildifier.ts +++ b/dev-infra/format/formatters/buildifier.ts @@ -28,7 +28,7 @@ export class Buildifier extends Formatter { commandFlags: `${BAZEL_WARNING_FLAG} --lint=warn --mode=check --format=json`, callback: (_: string, code: number, stdout: string) => { - return code !== 0 || !JSON.parse(stdout)['success']; + return code !== 0 || !(JSON.parse(stdout) as {success: string}).success; }, }, format: { diff --git a/dev-infra/index.bzl b/dev-infra/index.bzl index cd495e8552..34867f0994 100644 --- a/dev-infra/index.bzl +++ b/dev-infra/index.bzl @@ -3,7 +3,32 @@ # 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 -# File is currently empty but serves as indicator for `rules_nodejs` and instructs it to -# preserve the content output in the NPM install workspace. This allows consumers to use -# rules and targets from within Bazel. e.g. by using `@npm//@angular/dev-infra-private/<..>`. +load("@build_bazel_rules_nodejs//:index.bzl", "generated_file_test") +load("@npm//@bazel/rollup:index.bzl", "rollup_bundle") + +# This file continues to serve as indicator for `rules_nodejs` and instructs it preserve the +# content output in the NPM install workspace. This allows consumers to use rules and targets from +# within Bazel. e.g. by using `@npm//@angular/dev-infra-private/<..>`. # See: https://github.com/bazelbuild/rules_nodejs/commit/4f508b1a0be1f5444e9c13b0439e649449792fef. + +def ng_dev_rolled_up_generated_file(name, entry_point, deps = [], rollup_args = []): + """Rollup and generated file test macro. + + This provides a single macro to create a rollup bundled script and a generated file test for the + created script to ensure it stays up to date in the repository. + """ + rollup_bundle( + name = "%s_bundle" % name, + args = rollup_args, + entry_point = entry_point, + format = "cjs", + silent = True, + sourcemap = "false", + deps = deps, + ) + + generated_file_test( + name = name, + src = "%s.js" % name, + generated = "%s_bundle" % name, + ) diff --git a/dev-infra/ng-dev.js b/dev-infra/ng-dev.js index 39e9f71014..e35e507a94 100755 --- a/dev-infra/ng-dev.js +++ b/dev-infra/ng-dev.js @@ -8,7 +8,6 @@ var tslib = require('tslib'); var chalk = _interopDefault(require('chalk')); var fs = require('fs'); var inquirer = require('inquirer'); -var inquirerAutocomplete = require('inquirer-autocomplete-prompt'); var path = require('path'); var shelljs = require('shelljs'); var url = require('url'); @@ -20,6 +19,8 @@ var fetch = _interopDefault(require('node-fetch')); var semver = require('semver'); var multimatch = require('multimatch'); var yaml = require('yaml'); +var conventionalCommitsParser = require('conventional-commits-parser'); +var gitCommits_ = require('git-raw-commits'); var cliProgress = require('cli-progress'); var os = require('os'); var minimatch = require('minimatch'); @@ -121,7 +122,7 @@ function validateCommonConfig(config) { */ function readConfigFile(configPath, returnEmptyObjectOnError) { if (returnEmptyObjectOnError === void 0) { returnEmptyObjectOnError = false; } - // If the the `.ts` extension has not been set up already, and a TypeScript based + // If the `.ts` extension has not been set up already, and a TypeScript based // version of the given configuration seems to exist, set up `ts-node` if available. if (require.extensions['.ts'] === undefined && fs.existsSync(configPath + ".ts") && isTsNodeAvailable()) { @@ -231,53 +232,6 @@ function promptConfirm(message, defaultValue) { }); }); } -function promptAutocomplete(message, choices, noChoiceText) { - return tslib.__awaiter(this, void 0, void 0, function () { - var prompt, result; - return tslib.__generator(this, function (_a) { - switch (_a.label) { - case 0: - prompt = inquirer.createPromptModule({}).registerPrompt('autocomplete', inquirerAutocomplete); - if (noChoiceText) { - choices = tslib.__spread([noChoiceText], choices); - } - return [4 /*yield*/, prompt({ - type: 'autocomplete', - name: 'result', - message: message, - source: function (_, input) { - if (!input) { - return Promise.resolve(choices); - } - return Promise.resolve(choices.filter(function (choice) { - if (typeof choice === 'string') { - return choice.includes(input); - } - return choice.name.includes(input); - })); - } - })]; - case 1: - result = (_a.sent()).result; - if (result === noChoiceText) { - return [2 /*return*/, false]; - } - return [2 /*return*/, result]; - } - }); - }); -} -/** Prompts the user for one line of input. */ -function promptInput(message) { - return tslib.__awaiter(this, void 0, void 0, function () { - return tslib.__generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, inquirer.prompt({ type: 'input', name: 'result', message: message })]; - case 1: return [2 /*return*/, (_a.sent()).result]; - } - }); - }); -} /** * Supported levels for logging functions. * @@ -313,7 +267,7 @@ function buildLogLevelFunction(loadCommand, level) { for (var _i = 0; _i < arguments.length; _i++) { text[_i] = arguments[_i]; } - runConsoleCommand.apply(void 0, tslib.__spread([loadCommand, level], text)); + runConsoleCommand.apply(void 0, tslib.__spreadArray([loadCommand, level], tslib.__read(text))); }; /** Start a group at the LOG_LEVEL, optionally starting it as collapsed. */ loggingFunction.group = function (text, collapsed) { @@ -342,9 +296,9 @@ function runConsoleCommand(loadCommand, logLevel) { text[_i - 2] = arguments[_i]; } if (getLogLevel() >= logLevel) { - loadCommand().apply(void 0, tslib.__spread(text)); + loadCommand().apply(void 0, tslib.__spreadArray([], tslib.__read(text))); } - printToLogFile.apply(void 0, tslib.__spread([logLevel], text)); + printToLogFile.apply(void 0, tslib.__spreadArray([logLevel], tslib.__read(text))); } /** * Retrieve the log level from environment variables, if the value found @@ -387,7 +341,9 @@ function captureLogOutputForCommand(argv) { LOGGED_TEXT += headerLine + "\nCommand: " + argv.$0 + " " + argv._.join(' ') + "\nRan at: " + now + "\n"; // On process exit, write the logged output to the appropriate log files process.on('exit', function (code) { - LOGGED_TEXT += "Command ran in " + (new Date().getTime() - now.getTime()) + "ms"; + LOGGED_TEXT += headerLine + "\n"; + LOGGED_TEXT += "Command ran in " + (new Date().getTime() - now.getTime()) + "ms\n"; + LOGGED_TEXT += "Exit Code: " + code + "\n"; /** Path to the log file location. */ var logFilePath = path.join(getRepoBaseDir(), '.ng-dev.log'); // Strip ANSI escape codes from log outputs. @@ -396,7 +352,9 @@ function captureLogOutputForCommand(argv) { // For failure codes greater than 1, the new logged lines should be written to a specific log // file for the command run failure. if (code > 1) { - fs.writeFileSync(path.join(getRepoBaseDir(), ".ng-dev.err-" + now.getTime() + ".log"), LOGGED_TEXT); + var logFileName = ".ng-dev.err-" + now.getTime() + ".log"; + console.error("Exit code: " + code + ". Writing full log to " + logFileName); + fs.writeFileSync(path.join(getRepoBaseDir(), logFileName), LOGGED_TEXT); } }); // Mark file logging as enabled to prevent the function from executing multiple times. @@ -1631,11 +1589,11 @@ function handler$1({ fileEnvVariable, file, source }) { restoreCommitMessage(fileFromEnv, sourceFromEnv); return; } - throw new Error('No file path and commit message source provide. Provide values via positional command ' + + throw new Error('No file path and commit message source provide. Provide values via positional command ' + 'arguments, or via the --file-env-variable flag'); }); } -/** yargs command module describing the command. */ +/** yargs command module describing the command. */ const RestoreCommitMessageModule = { handler: handler$1, builder: builder$1, @@ -1664,7 +1622,7 @@ function getCommitMessageConfig() { assertNoErrors(errors); return config; } -/** Scope requirement level to be set for each commit type. */ +/** Scope requirement level to be set for each commit type. */ var ScopeRequirement; (function (ScopeRequirement) { ScopeRequirement[ScopeRequirement["Required"] = 0] = "Required"; @@ -1727,82 +1685,110 @@ const COMMIT_TYPES = { * 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 */ +/** + * A list of tuples expressing the fields to extract from each commit log entry. The tuple contains + * two values, the first is the key for the property and the second is the template shortcut for the + * git log command. + */ +const commitFields = { + hash: '%H', + shortHash: '%h', + author: '%aN', +}; +/** The commit fields described as git log format entries for parsing. */ +const commitFieldsAsFormat = (fields) => { + return Object.entries(fields).map(([key, value]) => `%n-${key}-%n${value}`).join(''); +}; +/** + * The git log format template to create git log entries for parsing. + * + * The conventional commits parser expects to parse the standard git log raw body (%B) into its + * component parts. Additionally it will parse additional fields with keys defined by + * `-{key name}-` separated by new lines. + * */ +const gitLogFormatForParsing = `%B${commitFieldsAsFormat(commitFields)}`; +/** Markers used to denote the start of a note section in a commit. */ +var NoteSections; +(function (NoteSections) { + NoteSections["BREAKING_CHANGE"] = "BREAKING CHANGE"; + NoteSections["DEPRECATED"] = "DEPRECATED"; +})(NoteSections || (NoteSections = {})); /** Regex determining if a commit is a fixup. */ const FIXUP_PREFIX_RE = /^fixup! /i; -/** Regex finding all github keyword links. */ -const GITHUB_LINKING_RE = /((closed?s?)|(fix(es)?(ed)?)|(resolved?s?))\s\#(\d+)/ig; /** Regex determining if a commit is a squash. */ const SQUASH_PREFIX_RE = /^squash! /i; /** Regex determining if a commit is a revert. */ const REVERT_PREFIX_RE = /^revert:? /i; -/** Regex determining the scope of a commit if provided. */ -const TYPE_SCOPE_RE = /^(\w+)(?:\(([^)]+)\))?\:\s(.+)$/; -/** Regex determining the entire header line of the commit. */ -const COMMIT_HEADER_RE = /^(.*)/i; -/** Regex determining the body of the commit. */ -const COMMIT_BODY_RE = /^.*\n\n([\s\S]*)$/; +/** + * Regex pattern for parsing the header line of a commit. + * + * Several groups are being matched to be used in the parsed commit object, being mapped to the + * `headerCorrespondence` object. + * + * The pattern can be broken down into component parts: + * - `(\w+)` - a capturing group discovering the type of the commit. + * - `(?:\((?:([^/]+)\/)?([^)]+)\))?` - a pair of capturing groups to capture the scope and, + * optionally the npmScope of the commit. + * - `(.*)` - a capturing group discovering the subject of the commit. + */ +const headerPattern = /^(\w+)(?:\((?:([^/]+)\/)?([^)]+)\))?: (.*)$/; +/** + * The property names used for the values extracted from the header via the `headerPattern` regex. + */ +const headerCorrespondence = ['type', 'npmScope', 'scope', 'subject']; +/** + * Configuration options for the commit parser. + * + * NOTE: An extended type from `Options` must be used because the current + * @types/conventional-commits-parser version does not include the `notesPattern` field. + */ +const parseOptions = { + commentChar: '#', + headerPattern, + headerCorrespondence, + noteKeywords: [NoteSections.BREAKING_CHANGE, NoteSections.DEPRECATED], + notesPattern: (keywords) => new RegExp(`(${keywords})(?:: ?)(.*)`), +}; /** Parse a full commit message into its composite parts. */ -function parseCommitMessage(commitMsg) { - // Ignore comments (i.e. lines starting with `#`). Comments are automatically removed by git and - // should not be considered part of the final commit message. - commitMsg = commitMsg.split('\n').filter(line => !line.startsWith('#')).join('\n'); - 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]; - } +function parseCommitMessage(fullText) { + // Ensure the fullText symbol is a `string`, even if a Buffer was provided. + fullText = fullText.toString(); + /** The commit message text with the fixup and squash markers stripped out. */ + const strippedCommitMsg = fullText.replace(FIXUP_PREFIX_RE, '') + .replace(SQUASH_PREFIX_RE, '') + .replace(REVERT_PREFIX_RE, ''); + /** The initially parsed commit. */ + const commit = conventionalCommitsParser.sync(strippedCommitMsg, parseOptions); + /** A list of breaking change notes from the commit. */ + const breakingChanges = []; + /** A list of deprecation notes from the commit. */ + const deprecations = []; + // Extract the commit message notes by marked types into their respective lists. + commit.notes.forEach((note) => { + if (note.title === NoteSections.BREAKING_CHANGE) { + return breakingChanges.push(note); + } + if (note.title === NoteSections.DEPRECATED) { + return deprecations.push(note); + } + }); 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), + fullText, + breakingChanges, + deprecations, + body: commit.body || '', + footer: commit.footer || '', + header: commit.header || '', + references: commit.references, + scope: commit.scope || '', + subject: commit.subject || '', + type: commit.type || '', + npmScope: commit.npmScope || '', + isFixup: FIXUP_PREFIX_RE.test(fullText), + isSquash: SQUASH_PREFIX_RE.test(fullText), + isRevert: REVERT_PREFIX_RE.test(fullText), }; } -/** Retrieve and parse each commit message in a provide range. */ -function parseCommitMessagesForRange(range) { - /** A random number used as a 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}`); - if (result.code) { - throw new Error(`Failed to get all commits in the range:\n ${result.stderr}`); - } - return result - // Separate the commits from a single string into individual commits. - .split(randomValueSeparator) - // Remove extra space before and after each commit message. - .map(l => l.trim()) - // Remove any superfluous lines which remain from the split. - .filter(line => !!line) - // Parse each commit message. - .map(commit => parseCommitMessage(commit)); -} /** * @license @@ -1813,6 +1799,16 @@ function parseCommitMessagesForRange(range) { */ /** Regex matching a URL for an entire commit body line. */ const COMMIT_BODY_URL_LINE_RE = /^https?:\/\/.*$/; +/** + * Regex matching a breaking change. + * + * - Starts with BREAKING CHANGE + * - Followed by a colon + * - Followed by a single space or two consecutive new lines + * + * NB: Anything after `BREAKING CHANGE` is optional to facilitate the validation. + */ +const COMMIT_BODY_BREAKING_CHANGE_RE = /^BREAKING CHANGE(:( |\n{2}))?/m; /** Validate a commit message against using the local repo's config. */ function validateCommitMessage(commitMsg, options = {}) { const config = getCommitMessageConfig().commitMessage; @@ -1820,11 +1816,10 @@ function validateCommitMessage(commitMsg, options = {}) { const errors = []; /** Perform the validation checks against the parsed commit. */ function validateCommitAndCollectErrors() { - // TODO(josephperrott): Remove early return calls when commit message errors are found - var _a; //////////////////////////////////// // Checking revert, squash, fixup // //////////////////////////////////// + var _a; // All revert commits are considered valid. if (commit.isRevert) { return true; @@ -1876,8 +1871,9 @@ function validateCommitMessage(commitMsg, options = {}) { errors.push(`Scopes are required for commits with type '${commit.type}', but no scope was provided.`); return false; } - if (commit.scope && !config.scopes.includes(commit.scope)) { - errors.push(`'${commit.scope}' is not an allowed scope.\n => SCOPES: ${config.scopes.join(', ')}`); + const fullScope = commit.npmScope ? `${commit.npmScope}/${commit.scope}` : commit.scope; + if (fullScope && !config.scopes.includes(fullScope)) { + errors.push(`'${fullScope}' is not an allowed scope.\n => SCOPES: ${config.scopes.join(', ')}`); return false; } // Commits with the type of `release` do not require a commit body. @@ -1887,21 +1883,38 @@ function validateCommitMessage(commitMsg, options = {}) { ////////////////////////// // Checking commit body // ////////////////////////// + // Due to an issue in which conventional-commits-parser considers all parts of a commit after + // a `#` reference to be the footer, we check the length of all of the commit content after the + // header. In the future, we expect to be able to check only the body once the parser properly + // handles this case. + const allNonHeaderContent = `${commit.body.trim()}\n${commit.footer.trim()}`; if (!((_a = config.minBodyLengthTypeExcludes) === null || _a === void 0 ? void 0 : _a.includes(commit.type)) && - commit.bodyWithoutLinking.trim().length < config.minBodyLength) { + allNonHeaderContent.length < config.minBodyLength) { errors.push(`The commit message body does not meet the minimum length of ${config.minBodyLength} characters`); return false; } const bodyByLine = commit.body.split('\n'); - const lineExceedsMaxLength = bodyByLine.some(line => { + const lineExceedsMaxLength = bodyByLine.some((line) => { // Check if any line exceeds the max line length limit. The limit is ignored for // lines that just contain an URL (as these usually cannot be wrapped or shortened). return line.length > config.maxLineLength && !COMMIT_BODY_URL_LINE_RE.test(line); }); if (lineExceedsMaxLength) { - errors.push(`The commit message body contains lines greater than ${config.maxLineLength} characters`); + errors.push(`The commit message body contains lines greater than ${config.maxLineLength} characters.`); return false; } + // Breaking change + // Check if the commit message contains a valid break change description. + // https://github.com/angular/angular/blob/88fbc066775ab1a2f6a8c75f933375b46d8fa9a4/CONTRIBUTING.md#commit-message-footer + const hasBreakingChange = COMMIT_BODY_BREAKING_CHANGE_RE.exec(commit.fullText); + if (hasBreakingChange !== null) { + const [, breakingChangeDescription] = hasBreakingChange; + if (!breakingChangeDescription) { + // Not followed by :, space or two consecutive new lines, + errors.push(`The commit message body contains an invalid breaking change description.`); + return false; + } + } return true; } return { valid: validateCommitAndCollectErrors(), errors, commit }; @@ -1917,6 +1930,11 @@ function printValidationErrors(errors, print = error) { print(); print(''); print(); + print(`BREAKING CHANGE: `); + print(); + print(``); + print(); + print(); } /** @@ -1996,7 +2014,7 @@ function handler$2({ error, file, fileEnvVariable }) { validateFile(filePath, error); }); } -/** yargs command module describing the command. */ +/** yargs command module describing the command. */ const ValidateFileModule = { handler: handler$2, builder: builder$2, @@ -2011,48 +2029,69 @@ const ValidateFileModule = { * 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 */ +// Set `gitCommits` as this imported value to address "Cannot call a namespace" error. +const gitCommits = gitCommits_; +/** + * Find all commits within the given range and return an object describing those. + */ +function getCommitsInRange(from, to = 'HEAD') { + return new Promise((resolve, reject) => { + /** List of parsed commit objects. */ + const commits = []; + /** Stream of raw git commit strings in the range provided. */ + const commitStream = gitCommits({ from, to, format: gitLogFormatForParsing }); + // Accumulate the parsed commits for each commit from the Readable stream into an array, then + // resolve the promise with the array when the Readable stream ends. + commitStream.on('data', (commit) => commits.push(parseCommitMessage(commit))); + commitStream.on('error', (err) => reject(err)); + commitStream.on('end', () => resolve(commits)); + }); +} + // Whether the provided commit is a fixup commit. const isNonFixup = (commit) => !commit.isFixup; // Extracts commit header (first line of commit message). const extractCommitHeader = (commit) => commit.header; /** Validate all commits in a provided git commit range. */ -function validateCommitRange(range) { - /** A list of tuples of the commit header string and a list of error messages for the commit. */ - const errors = []; - /** A list of parsed commit messages from the range. */ - const commits = parseCommitMessagesForRange(range); - info(`Examining ${commits.length} commit(s) in the provided range: ${range}`); - /** - * Whether all commits in the range are valid, commits are allowed to be fixup commits for other - * commits in the provided commit range. - */ - const allCommitsInRangeValid = commits.every((commit, i) => { - const options = { - disallowSquash: true, - nonFixupCommitHeaders: isNonFixup(commit) ? - undefined : - commits.slice(0, i).filter(isNonFixup).map(extractCommitHeader) - }; - const { valid, errors: localErrors } = validateCommitMessage(commit, options); - if (localErrors.length) { - errors.push([commit.header, localErrors]); - } - return valid; - }); - if (allCommitsInRangeValid) { - info('√ All commit messages in range valid.'); - } - else { - error('✘ Invalid commit message'); - errors.forEach(([header, validationErrors]) => { - error.group(header); - printValidationErrors(validationErrors); - error.groupEnd(); +function validateCommitRange(from, to) { + return tslib.__awaiter(this, void 0, void 0, function* () { + /** A list of tuples of the commit header string and a list of error messages for the commit. */ + const errors = []; + /** A list of parsed commit messages from the range. */ + const commits = yield getCommitsInRange(from, to); + info(`Examining ${commits.length} commit(s) in the provided range: ${from}..${to}`); + /** + * Whether all commits in the range are valid, commits are allowed to be fixup commits for other + * commits in the provided commit range. + */ + const allCommitsInRangeValid = commits.every((commit, i) => { + const options = { + disallowSquash: true, + nonFixupCommitHeaders: isNonFixup(commit) ? + undefined : + commits.slice(i + 1).filter(isNonFixup).map(extractCommitHeader) + }; + const { valid, errors: localErrors } = validateCommitMessage(commit, options); + if (localErrors.length) { + errors.push([commit.header, localErrors]); + } + return valid; }); - // Exit with a non-zero exit code if invalid commit messages have - // been discovered. - process.exit(1); - } + if (allCommitsInRangeValid) { + info(green('√ All commit messages in range valid.')); + } + else { + error(red('✘ Invalid commit message')); + errors.forEach(([header, validationErrors]) => { + error.group(header); + printValidationErrors(validationErrors); + error.groupEnd(); + }); + // Exit with a non-zero exit code if invalid commit messages have + // been discovered. + process.exit(1); + } + }); } /** @@ -2064,15 +2103,20 @@ function validateCommitRange(range) { */ /** Builds the command. */ function builder$3(yargs) { - return yargs.option('range', { - description: 'The range of commits to check, e.g. --range abc123..xyz456', - demandOption: ' A range must be provided, e.g. --range abc123..xyz456', + return yargs + .positional('startingRef', { + description: 'The first ref in the range to select', type: 'string', - requiresArg: true, + demandOption: true, + }) + .positional('endingRef', { + description: 'The last ref in the range to select', + type: 'string', + default: 'HEAD', }); } /** Handles the command. */ -function handler$3({ range }) { +function handler$3({ startingRef, endingRef }) { return tslib.__awaiter(this, void 0, void 0, function* () { // If on CI, and no pull request number is provided, assume the branch // being run on is an upstream branch. @@ -2083,146 +2127,22 @@ function handler$3({ range }) { info(`Skipping check of provided commit range`); return; } - validateCommitRange(range); + yield validateCommitRange(startingRef, endingRef); }); } -/** yargs command module describing the command. */ +/** yargs command module describing the command. */ const ValidateRangeModule = { handler: handler$3, builder: builder$3, - command: 'validate-range', + command: 'validate-range [ending-ref]', describe: 'Validate a range of commit messages', }; -/** Validate commit message at the provided file path. */ -function buildCommitMessage() { - return tslib.__awaiter(this, void 0, void 0, function* () { - // TODO(josephperrott): Add support for skipping wizard with local untracked config file - // TODO(josephperrott): Add default commit message information/commenting into generated messages - info('Just a few questions to start building the commit message!'); - /** The commit message type. */ - const type = yield promptForCommitMessageType(); - /** The commit message scope. */ - const scope = yield promptForCommitMessageScopeForType(type); - /** The commit message summary. */ - const summary = yield promptForCommitMessageSummary(); - return `${type.name}${scope ? '(' + scope + ')' : ''}: ${summary}\n\n`; - }); -} -/** Prompts in the terminal for the commit message's type. */ -function promptForCommitMessageType() { - return tslib.__awaiter(this, void 0, void 0, function* () { - info('The type of change in the commit. Allows a reader to know the effect of the change,'); - info('whether it brings a new feature, adds additional testing, documents the `project, etc.'); - /** List of commit type options for the autocomplete prompt. */ - const typeOptions = Object.values(COMMIT_TYPES).map(({ description, name }) => { - return { - name: `${name} - ${description}`, - value: name, - short: name, - }; - }); - /** The key of a commit message type, selected by the user via prompt. */ - const typeName = yield promptAutocomplete('Select a type for the commit:', typeOptions); - return COMMIT_TYPES[typeName]; - }); -} -/** Prompts in the terminal for the commit message's scope. */ -function promptForCommitMessageScopeForType(type) { - return tslib.__awaiter(this, void 0, void 0, function* () { - // If the commit type's scope requirement is forbidden, return early. - if (type.scope === ScopeRequirement.Forbidden) { - info(`Skipping scope selection as the '${type.name}' type does not allow scopes`); - return false; - } - /** Commit message configuration */ - const config = getCommitMessageConfig(); - info('The area of the repository the changes in this commit most affects.'); - return yield promptAutocomplete('Select a scope for the commit:', config.commitMessage.scopes, type.scope === ScopeRequirement.Optional ? '' : ''); - }); -} -/** Prompts in the terminal for the commit message's summary. */ -function promptForCommitMessageSummary() { - return tslib.__awaiter(this, void 0, void 0, function* () { - info('Provide a short summary of what the changes in the commit do'); - return yield promptInput('Provide a short summary of the commit'); - }); -} - -/** The default commit message used if the wizard does not procude a commit message. */ -const defaultCommitMessage = `(): - -# \n\n`; -function runWizard(args) { - var _a; - return tslib.__awaiter(this, void 0, void 0, function* () { - if ((_a = getUserConfig().commitMessage) === null || _a === void 0 ? void 0 : _a.disableWizard) { - debug('Skipping commit message wizard due to enabled `commitMessage.disableWizard` option in'); - debug('user config.'); - process.exitCode = 0; - return; - } - if (args.source !== undefined) { - info(`Skipping commit message wizard because the commit was created via '${args.source}' source`); - process.exitCode = 0; - return; - } - // Set the default commit message to be updated if the user cancels out of the wizard in progress - fs.writeFileSync(args.filePath, defaultCommitMessage); - /** The generated commit message. */ - const commitMessage = yield buildCommitMessage(); - fs.writeFileSync(args.filePath, commitMessage); - }); -} - -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -/** Builds the command. */ -function builder$4(yargs) { - return yargs - .positional('filePath', { - description: 'The file path to write the generated commit message into', - type: 'string', - demandOption: true, - }) - .positional('source', { - choices: ['message', 'template', 'merge', 'squash', 'commit'], - description: 'The source of the commit message as described here: ' + - 'https://git-scm.com/docs/githooks#_prepare_commit_msg' - }) - .positional('commitSha', { - description: 'The commit sha if source is set to `commit`', - type: 'string', - }); -} -/** Handles the command. */ -function handler$4(args) { - return tslib.__awaiter(this, void 0, void 0, function* () { - yield runWizard(args); - }); -} -/** yargs command module describing the command. */ -const WizardModule = { - handler: handler$4, - builder: builder$4, - command: 'wizard [source] [commitSha]', - // Description: Run the wizard to build a base commit message before opening to complete. - // No describe is defiend to hide the command from the --help. - describe: false, -}; - /** Build the parser for the commit-message commands. */ function buildCommitMessageParser(localYargs) { return localYargs.help() .strict() .command(RestoreCommitMessageModule) - .command(WizardModule) .command(ValidateFileModule) .command(ValidateRangeModule); } @@ -2250,7 +2170,7 @@ function allChangedFilesSince(sha) { var diffFiles = gitOutputAsArray("git diff --name-only --diff-filter=d " + sha); var untrackedFiles = gitOutputAsArray("git ls-files --others --exclude-standard"); // Use a set to deduplicate the list as its possible for a file to show up in both lists. - return Array.from(new Set(tslib.__spread(diffFiles, untrackedFiles))); + return Array.from(new Set(tslib.__spreadArray(tslib.__spreadArray([], tslib.__read(diffFiles)), tslib.__read(untrackedFiles)))); } /** * A list of all staged files which have been modified. @@ -2387,7 +2307,7 @@ class Buildifier extends Formatter { check: { commandFlags: `${BAZEL_WARNING_FLAG} --lint=warn --mode=check --format=json`, callback: (_, code, stdout) => { - return code !== 0 || !JSON.parse(stdout)['success']; + return code !== 0 || !JSON.parse(stdout).success; }, }, format: { @@ -2782,21 +2702,21 @@ var InvalidTargetLabelError = /** @class */ (function () { /** Gets the target label from the specified pull request labels. */ function getTargetLabelFromPullRequest(config, labels) { var e_1, _a; + /** List of discovered target labels for the PR. */ + var matches = []; var _loop_1 = function (label) { var match = config.labels.find(function (_a) { var pattern = _a.pattern; return matchesPattern(label, pattern); }); if (match !== undefined) { - return { value: match }; + matches.push(match); } }; try { for (var labels_1 = tslib.__values(labels), labels_1_1 = labels_1.next(); !labels_1_1.done; labels_1_1 = labels_1.next()) { var label = labels_1_1.value; - var state_1 = _loop_1(label); - if (typeof state_1 === "object") - return state_1.value; + _loop_1(label); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } @@ -2806,7 +2726,13 @@ function getTargetLabelFromPullRequest(config, labels) { } finally { if (e_1) throw e_1.error; } } - return null; + if (matches.length === 1) { + return matches[0]; + } + if (matches.length === 0) { + throw new InvalidTargetLabelError('Unable to determine target for the PR as it has no target label.'); + } + throw new InvalidTargetLabelError('Unable to determine target for the PR as it has multiple target labels.'); } /** * Gets the branches from the specified target label. @@ -2842,7 +2768,7 @@ function getBranchesFromTargetLabel(label, githubTargetBranch) { * 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 */ -function checkTargetBranchesForPr(prNumber, jsonOutput = false) { +function getTargetBranchesForPr(prNumber) { return tslib.__awaiter(this, void 0, void 0, function* () { /** The ng-dev configuration. */ const config = getConfig(); @@ -2862,17 +2788,26 @@ function checkTargetBranchesForPr(prNumber, jsonOutput = false) { /** The branch targetted via the Github UI. */ const githubTargetBranch = prData.base.ref; /** The active label which is being used for targetting the PR. */ - const targetLabel = getTargetLabelFromPullRequest(mergeConfig, labels); - if (targetLabel === null) { - error(red(`No target label was found on pr #${prNumber}`)); - process.exitCode = 1; - return; + let targetLabel; + try { + targetLabel = getTargetLabelFromPullRequest(mergeConfig, labels); + } + catch (e) { + if (e instanceof InvalidTargetLabelError) { + error(red(e.failureMessage)); + process.exitCode = 1; + return; + } + throw e; } /** The target branches based on the target label and branch targetted in the Github UI. */ - const targets = yield getBranchesFromTargetLabel(targetLabel, githubTargetBranch); - // When requested, print a json output to stdout, rather than using standard ng-dev logging. - if (jsonOutput) { - process.stdout.write(JSON.stringify(targets)); + return yield getBranchesFromTargetLabel(targetLabel, githubTargetBranch); + }); +} +function printTargetBranchesForPr(prNumber) { + return tslib.__awaiter(this, void 0, void 0, function* () { + const targets = yield getTargetBranchesForPr(prNumber); + if (targets === undefined) { return; } info.group(`PR #${prNumber} will merge into:`); @@ -2889,29 +2824,23 @@ function checkTargetBranchesForPr(prNumber, jsonOutput = false) { * found in the LICENSE file at https://angular.io/license */ /** Builds the command. */ -function builder$5(yargs) { - return yargs - .positional('pr', { +function builder$4(yargs) { + return yargs.positional('pr', { description: 'The pull request number', type: 'number', demandOption: true, - }) - .option('json', { - type: 'boolean', - default: false, - description: 'Print response as json', }); } /** Handles the command. */ -function handler$5({ pr, json }) { +function handler$4({ pr }) { return tslib.__awaiter(this, void 0, void 0, function* () { - yield checkTargetBranchesForPr(pr, json); + yield printTargetBranchesForPr(pr); }); } /** yargs command module describing the command. */ const CheckTargetBranchesModule = { - handler: handler$5, - builder: builder$5, + handler: handler$4, + builder: builder$4, command: 'check-target-branches ', describe: 'Check a PR to determine what branches it is currently targeting', }; @@ -2934,7 +2863,7 @@ function getPr(prSchema, prNumber, git) { PR_QUERY = typedGraphqlify.params({ $number: 'Int!', $owner: 'String!', - $name: 'String!', + $name: 'String!', // The organization to query for }, { repository: typedGraphqlify.params({ owner: '$owner', name: '$name' }, { pullRequest: typedGraphqlify.params({ number: '$number' }, prSchema), @@ -2960,7 +2889,7 @@ function getPendingPrs(prSchema, git) { $first: 'Int', $after: 'String', $owner: 'String!', - $name: 'String!', + $name: 'String!', // The repository to query for }, { repository: typedGraphqlify.params({ owner: '$owner', name: '$name' }, { pullRequests: typedGraphqlify.params({ @@ -2990,7 +2919,7 @@ function getPendingPrs(prSchema, git) { return [4 /*yield*/, git.github.graphql.query(PRS_QUERY, params_1)]; case 2: results = _b.sent(); - prs.push.apply(prs, tslib.__spread(results.repository.pullRequests.nodes)); + prs.push.apply(prs, tslib.__spreadArray([], tslib.__read(results.repository.pullRequests.nodes))); hasNextPage = results.repository.pullRequests.pageInfo.hasNextPage; cursor = results.repository.pullRequests.pageInfo.endCursor; return [3 /*break*/, 1]; @@ -3116,11 +3045,11 @@ function checkOutPullRequestLocally(prNumber, githubToken, opts = {}) { * found in the LICENSE file at https://angular.io/license */ /** Builds the checkout pull request command. */ -function builder$6(yargs) { +function builder$5(yargs) { return addGithubTokenOption(yargs).positional('prNumber', { type: 'number', demandOption: true }); } /** Handles the checkout pull request command. */ -function handler$6({ prNumber, githubToken }) { +function handler$5({ prNumber, githubToken }) { return tslib.__awaiter(this, void 0, void 0, function* () { const prCheckoutOptions = { allowIfMaintainerCannotModify: true, branchName: `pr-${prNumber}` }; yield checkOutPullRequestLocally(prNumber, githubToken, prCheckoutOptions); @@ -3128,8 +3057,8 @@ function handler$6({ prNumber, githubToken }) { } /** yargs command module for checking out a PR */ const CheckoutCommandModule = { - handler: handler$6, - builder: builder$6, + handler: handler$5, + builder: builder$5, command: 'checkout ', describe: 'Checkout a PR from the upstream repo', }; @@ -3336,9 +3265,6 @@ var PullRequestFailure = /** @class */ (function () { PullRequestFailure.notMergeReady = function () { return new this("Not marked as merge ready."); }; - PullRequestFailure.noTargetLabel = function () { - return new this("No target branch could be determined. Please ensure a target label is set."); - }; PullRequestFailure.mismatchingTargetBranch = function (allowedBranches) { return new this("Pull request is set to wrong base branch. Please update the PR in the Github UI " + ("to one of the following branches: " + allowedBranches.join(', ') + ".")); @@ -3416,9 +3342,14 @@ function loadAndValidatePullRequest(_a, prNumber, ignoreNonFatalFailures) { if (!labels.some(function (name) { return matchesPattern(name, config.claSignedLabel); })) { return [2 /*return*/, PullRequestFailure.claUnsigned()]; } - targetLabel = getTargetLabelFromPullRequest(config, labels); - if (targetLabel === null) { - return [2 /*return*/, PullRequestFailure.noTargetLabel()]; + try { + targetLabel = getTargetLabelFromPullRequest(config, labels); + } + catch (error) { + if (error instanceof InvalidTargetLabelError) { + return [2 /*return*/, new PullRequestFailure(error.failureMessage)]; + } + throw error; } return [4 /*yield*/, git.github.repos.getCombinedStatusForRef(tslib.__assign(tslib.__assign({}, git.remoteParams), { ref: prData.head.sha }))]; case 2: @@ -3583,7 +3514,7 @@ var MergeStrategy = /** @class */ (function () { // Checkout the local target branch. this.git.run(['checkout', localTargetBranch]); // Cherry-pick the refspec into the target branch. - if (this.git.runGraceful(tslib.__spread(['cherry-pick'], cherryPickArgs)).status !== 0) { + if (this.git.runGraceful(tslib.__spreadArray(['cherry-pick'], tslib.__read(cherryPickArgs))).status !== 0) { // Abort the failed cherry-pick. We do this because Git persists the failed // cherry-pick state globally in the repository. This could prevent future // pull request merges as a Git thinks a cherry-pick is still in progress. @@ -3622,7 +3553,7 @@ var MergeStrategy = /** @class */ (function () { }); // Fetch all target branches with a single command. We don't want to fetch them // individually as that could cause an unnecessary slow-down. - this.git.run(tslib.__spread(['fetch', '-q', '-f', this.git.repoGitUrl], fetchRefspecs, extraRefspecs)); + this.git.run(tslib.__spreadArray(tslib.__spreadArray(['fetch', '-q', '-f', this.git.repoGitUrl], tslib.__read(fetchRefspecs)), tslib.__read(extraRefspecs))); }; /** Pushes the given target branches upstream. */ MergeStrategy.prototype.pushTargetBranchesUpstream = function (names) { @@ -3633,7 +3564,7 @@ var MergeStrategy = /** @class */ (function () { }); // Push all target branches with a single command if we don't run in dry-run mode. // We don't want to push them individually as that could cause an unnecessary slow-down. - this.git.run(tslib.__spread(['push', this.git.repoGitUrl], pushRefspecs)); + this.git.run(tslib.__spreadArray(['push', this.git.repoGitUrl], tslib.__read(pushRefspecs))); }; return MergeStrategy; }()); @@ -3900,35 +3831,52 @@ var AutosquashMergeStrategy = /** @class */ (function (_super) { */ AutosquashMergeStrategy.prototype.merge = function (pullRequest) { return tslib.__awaiter(this, void 0, void 0, function () { - var prNumber, targetBranches, requiredBaseSha, needsCommitMessageFixup, baseSha, revisionRange, branchOrRevisionBeforeRebase, rebaseEnv, failedBranches; + var prNumber, targetBranches, requiredBaseSha, needsCommitMessageFixup, githubTargetBranch, baseSha, revisionRange, branchOrRevisionBeforeRebase, rebaseEnv, failedBranches, localBranch, sha; return tslib.__generator(this, function (_a) { - prNumber = pullRequest.prNumber, targetBranches = pullRequest.targetBranches, requiredBaseSha = pullRequest.requiredBaseSha, needsCommitMessageFixup = pullRequest.needsCommitMessageFixup; - // In case a required base is specified for this pull request, check if the pull - // request contains the given commit. If not, return a pull request failure. This - // check is useful for enforcing that PRs are rebased on top of a given commit. e.g. - // a commit that changes the codeowner ship validation. PRs which are not rebased - // could bypass new codeowner ship rules. - if (requiredBaseSha && !this.git.hasCommit(TEMP_PR_HEAD_BRANCH, requiredBaseSha)) { - return [2 /*return*/, PullRequestFailure.unsatisfiedBaseSha()]; + switch (_a.label) { + case 0: + prNumber = pullRequest.prNumber, targetBranches = pullRequest.targetBranches, requiredBaseSha = pullRequest.requiredBaseSha, needsCommitMessageFixup = pullRequest.needsCommitMessageFixup, githubTargetBranch = pullRequest.githubTargetBranch; + // In case a required base is specified for this pull request, check if the pull + // request contains the given commit. If not, return a pull request failure. This + // check is useful for enforcing that PRs are rebased on top of a given commit. e.g. + // a commit that changes the codeowner ship validation. PRs which are not rebased + // could bypass new codeowner ship rules. + if (requiredBaseSha && !this.git.hasCommit(TEMP_PR_HEAD_BRANCH, requiredBaseSha)) { + return [2 /*return*/, PullRequestFailure.unsatisfiedBaseSha()]; + } + baseSha = this.git.run(['rev-parse', this.getPullRequestBaseRevision(pullRequest)]).stdout.trim(); + revisionRange = baseSha + ".." + TEMP_PR_HEAD_BRANCH; + branchOrRevisionBeforeRebase = this.git.getCurrentBranchOrRevision(); + rebaseEnv = needsCommitMessageFixup ? undefined : tslib.__assign(tslib.__assign({}, process.env), { GIT_SEQUENCE_EDITOR: 'true' }); + this.git.run(['rebase', '--interactive', '--autosquash', baseSha, TEMP_PR_HEAD_BRANCH], { stdio: 'inherit', env: rebaseEnv }); + // Update pull requests commits to reference the pull request. This matches what + // Github does when pull requests are merged through the Web UI. The motivation is + // that it should be easy to determine which pull request contained a given commit. + // Note: The filter-branch command relies on the working tree, so we want to make sure + // that we are on the initial branch or revision where the merge script has been invoked. + this.git.run(['checkout', '-f', branchOrRevisionBeforeRebase]); + this.git.run(['filter-branch', '-f', '--msg-filter', MSG_FILTER_SCRIPT + " " + prNumber, revisionRange]); + failedBranches = this.cherryPickIntoTargetBranches(revisionRange, targetBranches); + if (failedBranches.length) { + return [2 /*return*/, PullRequestFailure.mergeConflicts(failedBranches)]; + } + this.pushTargetBranchesUpstream(targetBranches); + if (!(githubTargetBranch !== 'master')) return [3 /*break*/, 3]; + localBranch = this.getLocalTargetBranchName(githubTargetBranch); + sha = this.git.run(['rev-parse', localBranch]).stdout.trim(); + // Create a comment saying the PR was closed by the SHA. + return [4 /*yield*/, this.git.github.issues.createComment(tslib.__assign(tslib.__assign({}, this.git.remoteParams), { issue_number: pullRequest.prNumber, body: "Closed by commit " + sha }))]; + case 1: + // Create a comment saying the PR was closed by the SHA. + _a.sent(); + // Actually close the PR. + return [4 /*yield*/, this.git.github.pulls.update(tslib.__assign(tslib.__assign({}, this.git.remoteParams), { pull_number: pullRequest.prNumber, state: 'closed' }))]; + case 2: + // Actually close the PR. + _a.sent(); + _a.label = 3; + case 3: return [2 /*return*/, null]; } - baseSha = this.git.run(['rev-parse', this.getPullRequestBaseRevision(pullRequest)]).stdout.trim(); - revisionRange = baseSha + ".." + TEMP_PR_HEAD_BRANCH; - branchOrRevisionBeforeRebase = this.git.getCurrentBranchOrRevision(); - rebaseEnv = needsCommitMessageFixup ? undefined : tslib.__assign(tslib.__assign({}, process.env), { GIT_SEQUENCE_EDITOR: 'true' }); - this.git.run(['rebase', '--interactive', '--autosquash', baseSha, TEMP_PR_HEAD_BRANCH], { stdio: 'inherit', env: rebaseEnv }); - // Update pull requests commits to reference the pull request. This matches what - // Github does when pull requests are merged through the Web UI. The motivation is - // that it should be easy to determine which pull request contained a given commit. - // Note: The filter-branch command relies on the working tree, so we want to make sure - // that we are on the initial branch or revision where the merge script has been invoked. - this.git.run(['checkout', '-f', branchOrRevisionBeforeRebase]); - this.git.run(['filter-branch', '-f', '--msg-filter', MSG_FILTER_SCRIPT + " " + prNumber, revisionRange]); - failedBranches = this.cherryPickIntoTargetBranches(revisionRange, targetBranches); - if (failedBranches.length) { - return [2 /*return*/, PullRequestFailure.mergeConflicts(failedBranches)]; - } - this.pushTargetBranchesUpstream(targetBranches); - return [2 /*return*/, null]; }); }); }; @@ -3942,15 +3890,20 @@ var AutosquashMergeStrategy = /** @class */ (function (_super) { * 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 */ +var defaultPullRequestMergeTaskFlags = { + branchPrompt: true, +}; /** * Class that accepts a merge script configuration and Github token. It provides * a programmatic interface for merging multiple pull requests based on their * labels that have been resolved through the merge script configuration. */ var PullRequestMergeTask = /** @class */ (function () { - function PullRequestMergeTask(config, git) { + function PullRequestMergeTask(config, git, flags) { this.config = config; this.git = git; + // Update flags property with the provided flags values as patches to the default flag values. + this.flags = tslib.__assign(tslib.__assign({}, defaultPullRequestMergeTaskFlags), flags); } /** * Merges the given pull request and pushes it upstream. @@ -3960,10 +3913,10 @@ var PullRequestMergeTask = /** @class */ (function () { PullRequestMergeTask.prototype.merge = function (prNumber, force) { if (force === void 0) { force = false; } return tslib.__awaiter(this, void 0, void 0, function () { - var hasOauthScopes, pullRequest, _a, strategy, previousBranchOrRevision, failure, e_1; + var hasOauthScopes, pullRequest, _a, _b, strategy, previousBranchOrRevision, failure, e_1; var _this = this; - return tslib.__generator(this, function (_b) { - switch (_b.label) { + return tslib.__generator(this, function (_c) { + switch (_c.label) { case 0: return [4 /*yield*/, this.git.hasOauthScopes(function (scopes, missing) { if (!scopes.includes('repo')) { if (_this.config.remote.private) { @@ -3975,7 +3928,7 @@ var PullRequestMergeTask = /** @class */ (function () { } })]; case 1: - hasOauthScopes = _b.sent(); + hasOauthScopes = _c.sent(); if (hasOauthScopes !== true) { return [2 /*return*/, { status: 5 /* GITHUB_ERROR */, @@ -3987,43 +3940,48 @@ var PullRequestMergeTask = /** @class */ (function () { } return [4 /*yield*/, loadAndValidatePullRequest(this, prNumber, force)]; case 2: - pullRequest = _b.sent(); + pullRequest = _c.sent(); if (!isPullRequest(pullRequest)) { return [2 /*return*/, { status: 3 /* FAILED */, failure: pullRequest }]; } + _a = this.flags.branchPrompt; + if (!_a) return [3 /*break*/, 4]; return [4 /*yield*/, promptConfirm(getTargettedBranchesConfirmationPromptMessage(pullRequest))]; case 3: - if (!(_b.sent())) { + _a = !(_c.sent()); + _c.label = 4; + case 4: + if (_a) { return [2 /*return*/, { status: 4 /* USER_ABORTED */ }]; } - _a = pullRequest.hasCaretakerNote; - if (!_a) return [3 /*break*/, 5]; + _b = pullRequest.hasCaretakerNote; + if (!_b) return [3 /*break*/, 6]; return [4 /*yield*/, promptConfirm(getCaretakerNotePromptMessage(pullRequest))]; - case 4: - _a = !(_b.sent()); - _b.label = 5; case 5: + _b = !(_c.sent()); + _c.label = 6; + case 6: // If the pull request has a caretaker note applied, raise awareness by prompting // the caretaker. The caretaker can then decide to proceed or abort the merge. - if (_a) { + if (_b) { return [2 /*return*/, { status: 4 /* USER_ABORTED */ }]; } strategy = this.config.githubApiMerge ? new GithubApiMergeStrategy(this.git, this.config.githubApiMerge) : new AutosquashMergeStrategy(this.git); previousBranchOrRevision = null; - _b.label = 6; - case 6: - _b.trys.push([6, 10, 11, 12]); + _c.label = 7; + case 7: + _c.trys.push([7, 11, 12, 13]); previousBranchOrRevision = this.git.getCurrentBranchOrRevision(); // Run preparations for the merge (e.g. fetching branches). return [4 /*yield*/, strategy.prepare(pullRequest)]; - case 7: - // Run preparations for the merge (e.g. fetching branches). - _b.sent(); - return [4 /*yield*/, strategy.merge(pullRequest)]; case 8: - failure = _b.sent(); + // Run preparations for the merge (e.g. fetching branches). + _c.sent(); + return [4 /*yield*/, strategy.merge(pullRequest)]; + case 9: + failure = _c.sent(); if (failure !== null) { return [2 /*return*/, { status: 3 /* FAILED */, failure: failure }]; } @@ -4031,26 +3989,26 @@ var PullRequestMergeTask = /** @class */ (function () { // branches because we cannot delete branches which are currently checked out. this.git.run(['checkout', '-f', previousBranchOrRevision]); return [4 /*yield*/, strategy.cleanup(pullRequest)]; - case 9: - _b.sent(); + case 10: + _c.sent(); // Return a successful merge status. return [2 /*return*/, { status: 2 /* SUCCESS */ }]; - case 10: - e_1 = _b.sent(); + case 11: + e_1 = _c.sent(); // Catch all git command errors and return a merge result w/ git error status code. // Other unknown errors which aren't caused by a git command are re-thrown. if (e_1 instanceof GitCommandError) { return [2 /*return*/, { status: 0 /* UNKNOWN_GIT_ERROR */ }]; } throw e_1; - case 11: + case 12: // Always try to restore the branch if possible. We don't want to leave // the repository in a different state than before. if (previousBranchOrRevision !== null) { this.git.runGraceful(['checkout', '-f', previousBranchOrRevision]); } return [7 /*endfinally*/]; - case 12: return [2 /*return*/]; + case 13: return [2 /*return*/]; } }); }); @@ -4078,8 +4036,7 @@ var PullRequestMergeTask = /** @class */ (function () { * @param projectRoot Path to the local Git project that is used for merging. * @param config Configuration for merging pull requests. */ -function mergePullRequest(prNumber, githubToken, projectRoot, config) { - if (projectRoot === void 0) { projectRoot = getRepoBaseDir(); } +function mergePullRequest(prNumber, githubToken, flags) { return tslib.__awaiter(this, void 0, void 0, function () { /** Performs the merge and returns whether it was successful or not. */ function performMerge(ignoreFatalErrors) { @@ -4194,8 +4151,8 @@ function mergePullRequest(prNumber, githubToken, projectRoot, config) { case 0: // Set the environment variable to skip all git commit hooks triggered by husky. We are unable to // rely on `--no-verify` as some hooks still run, notably the `prepare-commit-msg` hook. - process.env['HUSKY_SKIP_HOOKS'] = '1'; - return [4 /*yield*/, createPullRequestMergeTask(githubToken, projectRoot, config)]; + process.env['HUSKY'] = '0'; + return [4 /*yield*/, createPullRequestMergeTask(githubToken, flags)]; case 1: api = _a.sent(); return [4 /*yield*/, performMerge(false)]; @@ -4216,16 +4173,13 @@ function mergePullRequest(prNumber, githubToken, projectRoot, config) { * and optional explicit configuration. An explicit configuration can be specified * when the merge script is used outside of a `ng-dev` configured repository. */ -function createPullRequestMergeTask(githubToken, projectRoot, explicitConfig) { +function createPullRequestMergeTask(githubToken, flags) { return tslib.__awaiter(this, void 0, void 0, function () { - var git_1, devInfraConfig, git, _a, config, errors; + var projectRoot, devInfraConfig, git, _a, config, errors; return tslib.__generator(this, function (_b) { switch (_b.label) { case 0: - if (explicitConfig !== undefined) { - git_1 = new GitClient(githubToken, { github: explicitConfig.remote }, projectRoot); - return [2 /*return*/, new PullRequestMergeTask(explicitConfig, git_1)]; - } + projectRoot = getRepoBaseDir(); devInfraConfig = getConfig(); git = new GitClient(githubToken, devInfraConfig, projectRoot); return [4 /*yield*/, loadAndValidateConfig(devInfraConfig, git.github)]; @@ -4241,7 +4195,7 @@ function createPullRequestMergeTask(githubToken, projectRoot, explicitConfig) { config.remote = devInfraConfig.github; // We can cast this to a merge config with remote because we always set the // remote above. - return [2 /*return*/, new PullRequestMergeTask(config, git)]; + return [2 /*return*/, new PullRequestMergeTask(config, git, flags)]; } }); }); @@ -4254,17 +4208,29 @@ function createPullRequestMergeTask(githubToken, projectRoot, explicitConfig) { * 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 */ -/** Builds the options for the merge command. */ -function buildMergeCommand(yargs) { - return addGithubTokenOption(yargs).help().strict().positional('pr-number', { demandOption: true, type: 'number' }); +/** Builds the command. */ +function builder$6(yargs) { + return addGithubTokenOption(yargs) + .help() + .strict() + .positional('pr', { + demandOption: true, + type: 'number', + description: 'The PR to be merged.', + }) + .option('branch-prompt', { + type: 'boolean', + default: true, + description: 'Whether to prompt to confirm the branches a PR will merge into.', + }); } -/** Handles the merge command. i.e. performs the merge of a specified pull request. */ -function handleMergeCommand(_a) { - var pr = _a["pr-number"], githubToken = _a.githubToken; +/** Handles the command. */ +function handler$6(_a) { + var pr = _a.pr, githubToken = _a.githubToken, branchPrompt = _a.branchPrompt; return tslib.__awaiter(this, void 0, void 0, function () { return tslib.__generator(this, function (_b) { switch (_b.label) { - case 0: return [4 /*yield*/, mergePullRequest(pr, githubToken)]; + case 0: return [4 /*yield*/, mergePullRequest(pr, githubToken, { branchPrompt: branchPrompt })]; case 1: _b.sent(); return [2 /*return*/]; @@ -4272,6 +4238,13 @@ function handleMergeCommand(_a) { }); }); } +/** yargs command module describing the command. */ +var MergeCommandModule = { + handler: handler$6, + builder: builder$6, + command: 'merge ', + describe: 'Merge a PR into its targeted branches.', +}; /** * @license @@ -4349,7 +4322,7 @@ function rebasePr(prNumber, githubToken, config = getConfig()) { info(`Fetching ${fullBaseRef} to rebase #${prNumber} on`); git.run(['fetch', '-q', baseRefUrl, baseRefName]); const commonAncestorSha = git.run(['merge-base', 'HEAD', 'FETCH_HEAD']).stdout.trim(); - const commits = parseCommitMessagesForRange(`${commonAncestorSha}..HEAD`); + const commits = yield getCommitsInRange(commonAncestorSha, 'HEAD'); let squashFixups = commits.filter((commit) => commit.isFixup).length === 0 ? false : yield promptConfirm(`PR #${prNumber} contains fixup commits, would you like to squash them during rebase?`, true); @@ -4431,9 +4404,9 @@ function buildPrParser(localYargs) { return localYargs.help() .strict() .demandCommand() - .command('merge ', 'Merge pull requests', buildMergeCommand, handleMergeCommand) .command('discover-new-conflicts ', 'Check if a pending PR causes new conflicts for other pending PRs', buildDiscoverNewConflictsCommand, handleDiscoverNewConflictsCommand) .command('rebase ', 'Rebase a pending PR and push the rebased commits back to Github', buildRebaseCommand, handleRebaseCommand) + .command(MergeCommandModule) .command(CheckoutCommandModule) .command(CheckTargetBranchesModule); } @@ -4951,6 +4924,159 @@ const ReleaseBuildCommandModule = { describe: 'Builds the release output for the current branch.', }; +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +/** + * Spawns a given command with the specified arguments inside a shell. All process stdout + * output is captured and returned as resolution on completion. Depending on the chosen + * output mode, stdout/stderr output is also printed to the console, or only on error. + * + * @returns a Promise resolving with captured stdout on success. The promise + * rejects on command failure. + */ +function spawnWithDebugOutput(command, args, options) { + if (options === void 0) { options = {}; } + return new Promise(function (resolve, reject) { + var commandText = command + " " + args.join(' '); + var outputMode = options.mode; + debug("Executing command: " + commandText); + var childProcess = child_process.spawn(command, args, tslib.__assign(tslib.__assign({}, options), { shell: true, stdio: ['inherit', 'pipe', 'pipe'] })); + var logOutput = ''; + var stdout = ''; + // Capture the stdout separately so that it can be passed as resolve value. + // This is useful if commands return parsable stdout. + childProcess.stderr.on('data', function (message) { + logOutput += message; + // If console output is enabled, print the message directly to the stderr. Note that + // we intentionally print all output to stderr as stdout should not be polluted. + if (outputMode === undefined || outputMode === 'enabled') { + process.stderr.write(message); + } + }); + childProcess.stdout.on('data', function (message) { + stdout += message; + logOutput += message; + // If console output is enabled, print the message directly to the stderr. Note that + // we intentionally print all output to stderr as stdout should not be polluted. + if (outputMode === undefined || outputMode === 'enabled') { + process.stderr.write(message); + } + }); + childProcess.on('exit', function (status, signal) { + var exitDescription = status !== null ? "exit code \"" + status + "\"" : "signal \"" + signal + "\""; + var printFn = outputMode === 'on-error' ? error : debug; + printFn("Command \"" + commandText + "\" completed with " + exitDescription + "."); + printFn("Process output: \n" + logOutput); + // On success, resolve the promise. Otherwise reject with the captured stderr + // and stdout log output if the output mode was set to `silent`. + if (status === 0) { + resolve({ stdout: stdout }); + } + else { + reject(outputMode === 'silent' ? logOutput : undefined); + } + }); + }); +} + +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +/** + * Runs NPM publish within a specified package directory. + * @throws With the process log output if the publish failed. + */ +function runNpmPublish(packagePath, distTag, registryUrl) { + return tslib.__awaiter(this, void 0, void 0, function* () { + const args = ['publish', '--access', 'public', '--tag', distTag]; + // If a custom registry URL has been specified, add the `--registry` flag. + if (registryUrl !== undefined) { + args.push('--registry', registryUrl); + } + yield spawnWithDebugOutput('npm', args, { cwd: packagePath, mode: 'silent' }); + }); +} +/** + * Sets the NPM tag to the specified version for the given package. + * @throws With the process log output if the tagging failed. + */ +function setNpmTagForPackage(packageName, distTag, version, registryUrl) { + return tslib.__awaiter(this, void 0, void 0, function* () { + const args = ['dist-tag', 'add', `${packageName}@${version}`, distTag]; + // If a custom registry URL has been specified, add the `--registry` flag. + if (registryUrl !== undefined) { + args.push('--registry', registryUrl); + } + yield spawnWithDebugOutput('npm', args, { mode: 'silent' }); + }); +} +/** + * Checks whether the user is currently logged into NPM. + * @returns Whether the user is currently logged into NPM. + */ +function npmIsLoggedIn(registryUrl) { + return tslib.__awaiter(this, void 0, void 0, function* () { + const args = ['whoami']; + // If a custom registry URL has been specified, add the `--registry` flag. + if (registryUrl !== undefined) { + args.push('--registry', registryUrl); + } + try { + yield spawnWithDebugOutput('npm', args, { mode: 'silent' }); + } + catch (e) { + return false; + } + return true; + }); +} +/** + * Log into NPM at a provided registry. + * @throws With the process log output if the login fails. + */ +function npmLogin(registryUrl) { + return tslib.__awaiter(this, void 0, void 0, function* () { + const args = ['login', '--no-browser']; + // If a custom registry URL has been specified, add the `--registry` flag. The `--registry` flag + // must be spliced into the correct place in the command as npm expects it to be the flag + // immediately following the login subcommand. + if (registryUrl !== undefined) { + args.splice(1, 0, '--registry', registryUrl); + } + yield spawnWithDebugOutput('npm', args); + }); +} +/** + * Log out of NPM at a provided registry. + * @returns Whether the user was logged out of NPM. + */ +function npmLogout(registryUrl) { + return tslib.__awaiter(this, void 0, void 0, function* () { + const args = ['logout']; + // If a custom registry URL has been specified, add the `--registry` flag. The `--registry` flag + // must be spliced into the correct place in the command as npm expects it to be the flag + // immediately following the logout subcommand. + if (registryUrl !== undefined) { + args.splice(1, 0, '--registry', registryUrl); + } + try { + yield spawnWithDebugOutput('npm', args, { mode: 'silent' }); + } + finally { + return npmIsLoggedIn(registryUrl); + } + }); +} + /** * @license * Copyright Google LLC All Rights Reserved. @@ -5058,102 +5184,6 @@ function semverInc(version, release, identifier) { return clone.inc(release, identifier); } -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -/** - * Spawns a given command with the specified arguments inside a shell. All process stdout - * output is captured and returned as resolution on completion. Depending on the chosen - * output mode, stdout/stderr output is also printed to the console, or only on error. - * - * @returns a Promise resolving with captured stdout on success. The promise - * rejects on command failure. - */ -function spawnWithDebugOutput(command, args, options) { - if (options === void 0) { options = {}; } - return new Promise(function (resolve, reject) { - var commandText = command + " " + args.join(' '); - var outputMode = options.mode; - debug("Executing command: " + commandText); - var childProcess = child_process.spawn(command, args, tslib.__assign(tslib.__assign({}, options), { shell: true, stdio: ['inherit', 'pipe', 'pipe'] })); - var logOutput = ''; - var stdout = ''; - // Capture the stdout separately so that it can be passed as resolve value. - // This is useful if commands return parsable stdout. - childProcess.stderr.on('data', function (message) { - logOutput += message; - // If console output is enabled, print the message directly to the stderr. Note that - // we intentionally print all output to stderr as stdout should not be polluted. - if (outputMode === undefined || outputMode === 'enabled') { - process.stderr.write(message); - } - }); - childProcess.stdout.on('data', function (message) { - stdout += message; - logOutput += message; - // If console output is enabled, print the message directly to the stderr. Note that - // we intentionally print all output to stderr as stdout should not be polluted. - if (outputMode === undefined || outputMode === 'enabled') { - process.stderr.write(message); - } - }); - childProcess.on('exit', function (status, signal) { - var exitDescription = status !== null ? "exit code \"" + status + "\"" : "signal \"" + signal + "\""; - var printFn = outputMode === 'on-error' ? error : debug; - printFn("Command \"" + commandText + "\" completed with " + exitDescription + "."); - printFn("Process output: \n" + logOutput); - // On success, resolve the promise. Otherwise reject with the captured stderr - // and stdout log output if the output mode was set to `silent`. - if (status === 0) { - resolve({ stdout: stdout }); - } - else { - reject(outputMode === 'silent' ? logOutput : undefined); - } - }); - }); -} - -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -/** - * Runs NPM publish within a specified package directory. - * @throws With the process log output if the publish failed. - */ -function runNpmPublish(packagePath, distTag, registryUrl) { - return tslib.__awaiter(this, void 0, void 0, function* () { - const args = ['publish', '--access', 'public', '--tag', distTag]; - // If a custom registry URL has been specified, add the `--registry` flag. - if (registryUrl !== undefined) { - args.push('--registry', registryUrl); - } - yield spawnWithDebugOutput('npm', args, { cwd: packagePath, mode: 'silent' }); - }); -} -/** - * Sets the NPM tag to the specified version for the given package. - * @throws With the process log output if the tagging failed. - */ -function setNpmTagForPackage(packageName, distTag, version, registryUrl) { - return tslib.__awaiter(this, void 0, void 0, function* () { - const args = ['dist-tag', 'add', `${packageName}@${version}`, distTag]; - // If a custom registry URL has been specified, add the `--registry` flag. - if (registryUrl !== undefined) { - args.push('--registry', registryUrl); - } - yield spawnWithDebugOutput('npm', args, { mode: 'silent' }); - }); -} - /** * @license * Copyright Google LLC All Rights Reserved. @@ -5278,6 +5308,25 @@ function invokeYarnInstallCommand(projectDir) { } }); } +/** + * Invokes the `yarn bazel clean` command in order to clean the output tree and ensure new artifacts + * are created for builds. + */ +function invokeBazelCleanCommand(projectDir) { + return tslib.__awaiter(this, void 0, void 0, function* () { + try { + // Note: No progress indicator needed as that is the responsibility of the command. + // TODO: Consider using an Ora spinner instead to ensure minimal console output. + yield spawnWithDebugOutput('yarn', ['bazel', 'clean'], { cwd: projectDir }); + info(green(' ✓ Cleaned bazel output tree.')); + } + catch (e) { + error(e); + error(red(' ✘ An error occurred while cleaning the bazel output tree.')); + throw new FatalReleaseActionError(); + } + }); +} /** * @license @@ -5313,6 +5362,8 @@ const findOwnedForksOfRepoQuery = typedGraphqlify.params({ * 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 */ +/** Thirty seconds in milliseconds. */ +const THIRTY_SECONDS_IN_MS = 30000; /** Gets whether a given pull request has been merged. */ function getPullRequestState(api, id) { return tslib.__awaiter(this, void 0, void 0, function* () { @@ -5320,12 +5371,15 @@ function getPullRequestState(api, id) { if (data.merged) { return 'merged'; } - else if (data.closed_at !== null) { + // Check if the PR was closed more than 30 seconds ago, this extra time gives Github time to + // update the closed pull request to be associated with the closing commit. + // Note: a Date constructed with `null` creates an object at 0 time, which will never be greater + // than the current date time. + if (data.closed_at !== null && + (new Date(data.closed_at).getTime() < Date.now() - THIRTY_SECONDS_IN_MS)) { return (yield isPullRequestClosedWithAssociatedCommit(api, id)) ? 'merged' : 'closed'; } - else { - return 'open'; - } + return 'open'; }); } /** @@ -5614,6 +5668,10 @@ class ReleaseAction { const { fork, branchName } = yield this._pushHeadToFork(proposedForkBranchName, true); const { data } = yield this.git.github.pulls.create(Object.assign(Object.assign({}, this.git.remoteParams), { head: `${fork.owner}:${branchName}`, base: targetBranch, body, title })); + // Add labels to the newly created PR if provided in the configuration. + if (this.config.releasePrLabels !== undefined) { + yield this.git.github.issues.addLabels(Object.assign(Object.assign({}, this.git.remoteParams), { issue_number: data.number, labels: this.config.releasePrLabels })); + } info(green(` ✓ Created pull request #${data.number} in ${repoSlug}.`)); return { id: data.number, @@ -5762,11 +5820,13 @@ class ReleaseAction { return false; } // Create a cherry-pick pull request that should be merged by the caretaker. - const { url } = yield this.pushChangesToForkAndCreatePullRequest(nextBranch, `changelog-cherry-pick-${newVersion}`, commitMessage, `Cherry-picks the changelog from the "${stagingBranch}" branch to the next ` + + const { url, id } = yield this.pushChangesToForkAndCreatePullRequest(nextBranch, `changelog-cherry-pick-${newVersion}`, commitMessage, `Cherry-picks the changelog from the "${stagingBranch}" branch to the next ` + `branch (${nextBranch}).`); info(green(` ✓ Pull request for cherry-picking the changelog into "${nextBranch}" ` + 'has been created.')); info(yellow(` Please ask team members to review: ${url}.`)); + // Wait for the Pull Request to be merged. + yield this.waitForPullRequestToBeMerged(id); return true; }); } @@ -5774,12 +5834,12 @@ class ReleaseAction { * Creates a Github release for the specified version in the configured project. * The release is created by tagging the specified commit SHA. */ - _createGithubReleaseForVersion(newVersion, versionBumpCommitSha) { + _createGithubReleaseForVersion(newVersion, versionBumpCommitSha, prerelease) { return tslib.__awaiter(this, void 0, void 0, function* () { const tagName = newVersion.format(); yield this.git.github.git.createRef(Object.assign(Object.assign({}, this.git.remoteParams), { ref: `refs/tags/${tagName}`, sha: versionBumpCommitSha })); info(green(` ✓ Tagged v${newVersion} release upstream.`)); - yield this.git.github.repos.createRelease(Object.assign(Object.assign({}, this.git.remoteParams), { name: `v${newVersion}`, tag_name: tagName })); + yield this.git.github.repos.createRelease(Object.assign(Object.assign({}, this.git.remoteParams), { name: `v${newVersion}`, tag_name: tagName, prerelease })); info(green(` ✓ Created v${newVersion} release in Github.`)); }); } @@ -5806,11 +5866,12 @@ class ReleaseAction { // created in the `next` branch. The new package would not be part of the patch branch, // so we cannot build and publish it. yield invokeYarnInstallCommand(this.projectDir); + yield invokeBazelCleanCommand(this.projectDir); const builtPackages = yield invokeReleaseBuildCommand(); // Verify the packages built are the correct version. yield this._verifyPackageVersions(newVersion, builtPackages); // Create a Github release for the new version. - yield this._createGithubReleaseForVersion(newVersion, versionBumpCommitSha); + yield this._createGithubReleaseForVersion(newVersion, versionBumpCommitSha, npmDistTag === 'next'); // Walk through all built packages and publish them to NPM. for (const builtPackage of builtPackages) { yield this._publishBuiltPackageToNpm(builtPackage, npmDistTag); @@ -6317,6 +6378,8 @@ class ReleaseTool { this._projectRoot = _projectRoot; /** Client for interacting with the Github API and the local Git command. */ this._git = new GitClient(this._githubToken, { github: this._github }, this._projectRoot); + /** The previous git commit to return back to after the release tool runs. */ + this.previousGitBranchOrRevision = this._git.getCurrentBranchOrRevision(); } /** Runs the interactive release tool. */ run() { @@ -6329,6 +6392,9 @@ class ReleaseTool { if (!(yield this._verifyNoUncommittedChanges()) || !(yield this._verifyRunningFromNextBranch())) { return CompletionState.FATAL_ERROR; } + if (!(yield this._verifyNpmLoginState())) { + return CompletionState.MANUALLY_ABORTED; + } const { owner, name } = this._github; const repo = { owner, name, api: this._git.github }; const releaseTrains = yield fetchActiveReleaseTrains(repo); @@ -6336,7 +6402,6 @@ class ReleaseTool { // the current project branching state without switching context. yield printActiveReleaseTrains(releaseTrains, this._config); const action = yield this._promptForReleaseAction(releaseTrains); - const previousGitBranchOrRevision = this._git.getCurrentBranchOrRevision(); try { yield action.perform(); } @@ -6352,11 +6417,20 @@ class ReleaseTool { return CompletionState.FATAL_ERROR; } finally { - this._git.checkout(previousGitBranchOrRevision, true); + yield this.cleanup(); } return CompletionState.SUCCESS; }); } + /** Run post release tool cleanups. */ + cleanup() { + return tslib.__awaiter(this, void 0, void 0, function* () { + // Return back to the git state from before the release tool ran. + this._git.checkout(this.previousGitBranchOrRevision, true); + // Ensure log out of NPM. + yield npmLogout(this._config.publishRegistry); + }); + } /** Prompts the caretaker for a release action that should be performed. */ _promptForReleaseAction(activeTrains) { return tslib.__awaiter(this, void 0, void 0, function* () { @@ -6407,6 +6481,45 @@ class ReleaseTool { return true; }); } + /** + * Verifies that the user is logged into NPM at the correct registry, if defined for the release. + * @returns a boolean indicating whether the user is logged into NPM. + */ + _verifyNpmLoginState() { + var _a, _b; + return tslib.__awaiter(this, void 0, void 0, function* () { + const registry = `NPM at the ${(_a = this._config.publishRegistry) !== null && _a !== void 0 ? _a : 'default NPM'} registry`; + // TODO(josephperrott): remove wombat specific block once wombot allows `npm whoami` check to + // check the status of the local token in the .npmrc file. + if ((_b = this._config.publishRegistry) === null || _b === void 0 ? void 0 : _b.includes('wombat-dressing-room.appspot.com')) { + info('Unable to determine NPM login state for wombat proxy, requiring login now.'); + try { + yield npmLogin(this._config.publishRegistry); + } + catch (_c) { + return false; + } + return true; + } + if (yield npmIsLoggedIn(this._config.publishRegistry)) { + debug(`Already logged into ${registry}.`); + return true; + } + error(red(` ✘ Not currently logged into ${registry}.`)); + const shouldLogin = yield promptConfirm('Would you like to log into NPM now?'); + if (shouldLogin) { + debug('Starting NPM login.'); + try { + yield npmLogin(this._config.publishRegistry); + } + catch (_d) { + return false; + } + return true; + } + return false; + }); + } } /** @@ -6431,10 +6544,11 @@ function handler$8(args) { switch (result) { case CompletionState.FATAL_ERROR: error(red(`Release action has been aborted due to fatal errors. See above.`)); - process.exitCode = 1; + process.exitCode = 2; break; case CompletionState.MANUALLY_ABORTED: info(yellow(`Release action has been manually aborted.`)); + process.exitCode = 1; break; case CompletionState.SUCCESS: info(green(`Release action has completed successfully.`)); @@ -6526,13 +6640,13 @@ const ReleaseSetDistTagCommand = { * Note: git operations, especially git status, take a long time inside mounted docker volumes * in Windows or OSX hosts (https://github.com/docker/for-win/issues/188). */ -function buildEnvStamp() { +function buildEnvStamp(mode) { console.info(`BUILD_SCM_BRANCH ${getCurrentBranch()}`); console.info(`BUILD_SCM_COMMIT_SHA ${getCurrentSha()}`); console.info(`BUILD_SCM_HASH ${getCurrentSha()}`); console.info(`BUILD_SCM_LOCAL_CHANGES ${hasLocalChanges()}`); console.info(`BUILD_SCM_USER ${getCurrentGitUser()}`); - console.info(`BUILD_SCM_VERSION ${getSCMVersion()}`); + console.info(`BUILD_SCM_VERSION ${getSCMVersion(mode)}`); process.exit(0); } /** Run the exec command and return the stdout as a trimmed string. */ @@ -6543,10 +6657,23 @@ function exec$1(cmd) { function hasLocalChanges() { return !!exec$1(`git status --untracked-files=no --porcelain`); } -/** Get the version based on the most recent semver tag. */ -function getSCMVersion() { - const version = exec$1(`git describe --match [0-9]*.[0-9]*.[0-9]* --abbrev=7 --tags HEAD`); - return `${version.replace(/-([0-9]+)-g/, '+$1.sha-')}${(hasLocalChanges() ? '.with-local-changes' : '')}`; +/** + * Get the version for generated packages. + * + * In snapshot mode, the version is based on the most recent semver tag. + * In release mode, the version is based on the base package.json version. + */ +function getSCMVersion(mode) { + if (mode === 'release') { + const packageJsonPath = path.join(getRepoBaseDir(), 'package.json'); + const { version } = require(packageJsonPath); + return version; + } + if (mode === 'snapshot') { + const version = exec$1(`git describe --match [0-9]*.[0-9]*.[0-9]* --abbrev=7 --tags HEAD`); + return `${version.replace(/-([0-9]+)-g/, '+$1.sha-')}${(hasLocalChanges() ? '.with-local-changes' : '')}`; + } + return '0.0.0'; } /** Get the current SHA of HEAD. */ function getCurrentSha() { @@ -6563,6 +6690,33 @@ function getCurrentGitUser() { return `${userName} <${userEmail}>`; } +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +function builder$a(args) { + return args.option('mode', { + demandOption: true, + description: 'Whether the env-stamp should be built for a snapshot or release', + choices: ['snapshot', 'release'] + }); +} +function handler$a({ mode }) { + return tslib.__awaiter(this, void 0, void 0, function* () { + buildEnvStamp(mode); + }); +} +/** CLI command module for building the environment stamp. */ +const BuildEnvStampCommand = { + builder: builder$a, + handler: handler$a, + command: 'build-env-stamp', + describe: 'Build the environment stamping information', +}; + /** Build the parser for the release commands. */ function buildReleaseParser(localYargs) { return localYargs.help() @@ -6571,7 +6725,7 @@ function buildReleaseParser(localYargs) { .command(ReleasePublishCommandModule) .command(ReleaseBuildCommandModule) .command(ReleaseSetDistTagCommand) - .command('build-env-stamp', 'Build the environment stamping information', {}, () => buildEnvStamp()); + .command(BuildEnvStampCommand); } /** @@ -6914,7 +7068,7 @@ function main(approve, config, printWarnings) { const analyzer = new Analyzer(resolveModule); const cycles = []; const checkedNodes = new WeakSet(); - glob.sync(glob$1, { absolute: true }).forEach(filePath => { + glob.sync(glob$1, { absolute: true, ignore: ['**/node_modules/**'] }).forEach(filePath => { const sourceFile = analyzer.getSourceFile(filePath); cycles.push(...analyzer.findCycles(sourceFile, checkedNodes)); }); diff --git a/dev-infra/pr/check-target-branches/check-target-branches.ts b/dev-infra/pr/check-target-branches/check-target-branches.ts index 992a4976f2..09b96f435c 100644 --- a/dev-infra/pr/check-target-branches/check-target-branches.ts +++ b/dev-infra/pr/check-target-branches/check-target-branches.ts @@ -9,10 +9,10 @@ import {getConfig} from '../../utils/config'; import {error, info, red} from '../../utils/console'; import {GitClient} from '../../utils/git/index'; -import {loadAndValidateConfig} from '../merge/config'; -import {getBranchesFromTargetLabel, getTargetLabelFromPullRequest} from '../merge/target-label'; +import {loadAndValidateConfig, TargetLabel} from '../merge/config'; +import {getBranchesFromTargetLabel, getTargetLabelFromPullRequest, InvalidTargetLabelError} from '../merge/target-label'; -export async function checkTargetBranchesForPr(prNumber: number, jsonOutput = false) { +export async function getTargetBranchesForPr(prNumber: number) { /** The ng-dev configuration. */ const config = getConfig(); /** Repo owner and name for the github repository. */ @@ -31,21 +31,28 @@ export async function checkTargetBranchesForPr(prNumber: number, jsonOutput = fa /** The branch targetted via the Github UI. */ const githubTargetBranch = prData.base.ref; /** The active label which is being used for targetting the PR. */ - const targetLabel = getTargetLabelFromPullRequest(mergeConfig!, labels); - if (targetLabel === null) { - error(red(`No target label was found on pr #${prNumber}`)); - process.exitCode = 1; - return; + let targetLabel: TargetLabel; + + try { + targetLabel = getTargetLabelFromPullRequest(mergeConfig!, labels); + } catch (e) { + if (e instanceof InvalidTargetLabelError) { + error(red(e.failureMessage)); + process.exitCode = 1; + return; + } + throw e; } /** The target branches based on the target label and branch targetted in the Github UI. */ - const targets = await getBranchesFromTargetLabel(targetLabel, githubTargetBranch); + return await getBranchesFromTargetLabel(targetLabel, githubTargetBranch); +} - // When requested, print a json output to stdout, rather than using standard ng-dev logging. - if (jsonOutput) { - process.stdout.write(JSON.stringify(targets)); + +export async function printTargetBranchesForPr(prNumber: number) { + const targets = await getTargetBranchesForPr(prNumber); + if (targets === undefined) { return; } - info.group(`PR #${prNumber} will merge into:`); targets.forEach(target => info(`- ${target}`)); info.groupEnd(); diff --git a/dev-infra/pr/check-target-branches/cli.ts b/dev-infra/pr/check-target-branches/cli.ts index 2ad937c554..a00300378c 100644 --- a/dev-infra/pr/check-target-branches/cli.ts +++ b/dev-infra/pr/check-target-branches/cli.ts @@ -8,31 +8,24 @@ import {Arguments, Argv, CommandModule} from 'yargs'; -import {checkTargetBranchesForPr} from './check-target-branches'; +import {printTargetBranchesForPr} from './check-target-branches'; export interface CheckTargetBranchesOptions { pr: number; - json: boolean; } /** Builds the command. */ function builder(yargs: Argv) { - return yargs - .positional('pr', { - description: 'The pull request number', - type: 'number', - demandOption: true, - }) - .option('json', { - type: 'boolean', - default: false, - description: 'Print response as json', - }); + return yargs.positional('pr', { + description: 'The pull request number', + type: 'number', + demandOption: true, + }); } /** Handles the command. */ -async function handler({pr, json}: Arguments) { - await checkTargetBranchesForPr(pr, json); +async function handler({pr}: Arguments) { + await printTargetBranchesForPr(pr); } /** yargs command module describing the command. */ diff --git a/dev-infra/pr/cli.ts b/dev-infra/pr/cli.ts index c9e6ccfbca..df57551dfa 100644 --- a/dev-infra/pr/cli.ts +++ b/dev-infra/pr/cli.ts @@ -11,7 +11,7 @@ import * as yargs from 'yargs'; import {CheckTargetBranchesModule} from './check-target-branches/cli'; import {CheckoutCommandModule} from './checkout/cli'; import {buildDiscoverNewConflictsCommand, handleDiscoverNewConflictsCommand} from './discover-new-conflicts/cli'; -import {buildMergeCommand, handleMergeCommand} from './merge/cli'; +import {MergeCommandModule} from './merge/cli'; import {buildRebaseCommand, handleRebaseCommand} from './rebase/cli'; /** Build the parser for pull request commands. */ @@ -19,7 +19,6 @@ export function buildPrParser(localYargs: yargs.Argv) { return localYargs.help() .strict() .demandCommand() - .command('merge ', 'Merge pull requests', buildMergeCommand, handleMergeCommand) .command( 'discover-new-conflicts ', 'Check if a pending PR causes new conflicts for other pending PRs', @@ -27,6 +26,7 @@ export function buildPrParser(localYargs: yargs.Argv) { .command( 'rebase ', 'Rebase a pending PR and push the rebased commits back to Github', buildRebaseCommand, handleRebaseCommand) + .command(MergeCommandModule) .command(CheckoutCommandModule) .command(CheckTargetBranchesModule); } diff --git a/dev-infra/pr/merge/cli.ts b/dev-infra/pr/merge/cli.ts index 0c2600351a..0f1a13c74f 100644 --- a/dev-infra/pr/merge/cli.ts +++ b/dev-infra/pr/merge/cli.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Arguments, Argv} from 'yargs'; +import {Arguments, Argv, CommandModule} from 'yargs'; import {addGithubTokenOption} from '../../utils/git/github-yargs'; @@ -15,17 +15,36 @@ import {mergePullRequest} from './index'; /** The options available to the merge command via CLI. */ export interface MergeCommandOptions { githubToken: string; - 'pr-number': number; + pr: number; + branchPrompt: boolean; } -/** Builds the options for the merge command. */ -export function buildMergeCommand(yargs: Argv): Argv { - return addGithubTokenOption(yargs).help().strict().positional( - 'pr-number', {demandOption: true, type: 'number'}); +/** Builds the command. */ +function builder(yargs: Argv) { + return addGithubTokenOption(yargs) + .help() + .strict() + .positional('pr', { + demandOption: true, + type: 'number', + description: 'The PR to be merged.', + }) + .option('branch-prompt' as 'branchPrompt', { + type: 'boolean', + default: true, + description: 'Whether to prompt to confirm the branches a PR will merge into.', + }); } -/** Handles the merge command. i.e. performs the merge of a specified pull request. */ -export async function handleMergeCommand( - {'pr-number': pr, githubToken}: Arguments) { - await mergePullRequest(pr, githubToken); +/** Handles the command. */ +async function handler({pr, githubToken, branchPrompt}: Arguments) { + await mergePullRequest(pr, githubToken, {branchPrompt}); } + +/** yargs command module describing the command. */ +export const MergeCommandModule: CommandModule<{}, MergeCommandOptions> = { + handler, + builder, + command: 'merge ', + describe: 'Merge a PR into its targeted branches.', +}; diff --git a/dev-infra/pr/merge/defaults/integration.spec.ts b/dev-infra/pr/merge/defaults/integration.spec.ts index 6db2cfc12f..41fa636f57 100644 --- a/dev-infra/pr/merge/defaults/integration.spec.ts +++ b/dev-infra/pr/merge/defaults/integration.spec.ts @@ -87,8 +87,10 @@ describe('default target labels', () => { if (labels === undefined) { labels = await computeTargetLabels(); } - const label = getTargetLabelFromPullRequest({labels}, [name]); - if (label === null) { + let label: TargetLabel; + try { + label = getTargetLabelFromPullRequest({labels}, [name]); + } catch (error) { return null; } return await getBranchesFromTargetLabel(label, githubTargetBranch); diff --git a/dev-infra/pr/merge/failures.ts b/dev-infra/pr/merge/failures.ts index b1ae0b70a5..043fcaf126 100644 --- a/dev-infra/pr/merge/failures.ts +++ b/dev-infra/pr/merge/failures.ts @@ -34,10 +34,6 @@ export class PullRequestFailure { return new this(`Not marked as merge ready.`); } - static noTargetLabel() { - return new this(`No target branch could be determined. Please ensure a target label is set.`); - } - static mismatchingTargetBranch(allowedBranches: string[]) { return new this( `Pull request is set to wrong base branch. Please update the PR in the Github UI ` + diff --git a/dev-infra/pr/merge/index.ts b/dev-infra/pr/merge/index.ts index f1dff5031e..c2f5e1a193 100644 --- a/dev-infra/pr/merge/index.ts +++ b/dev-infra/pr/merge/index.ts @@ -14,7 +14,7 @@ import {GITHUB_TOKEN_GENERATE_URL} from '../../utils/git/github-urls'; import {GitClient} from '../../utils/git/index'; import {loadAndValidateConfig, MergeConfigWithRemote} from './config'; -import {MergeResult, MergeStatus, PullRequestMergeTask} from './task'; +import {MergeResult, MergeStatus, PullRequestMergeTask, PullRequestMergeTaskFlags} from './task'; /** * Merges a given pull request based on labels configured in the given merge configuration. @@ -30,13 +30,12 @@ import {MergeResult, MergeStatus, PullRequestMergeTask} from './task'; * @param config Configuration for merging pull requests. */ export async function mergePullRequest( - prNumber: number, githubToken: string, projectRoot: string = getRepoBaseDir(), - config?: MergeConfigWithRemote) { + prNumber: number, githubToken: string, flags: PullRequestMergeTaskFlags) { // Set the environment variable to skip all git commit hooks triggered by husky. We are unable to // rely on `--no-verify` as some hooks still run, notably the `prepare-commit-msg` hook. - process.env['HUSKY_SKIP_HOOKS'] = '1'; + process.env['HUSKY'] = '0'; - const api = await createPullRequestMergeTask(githubToken, projectRoot, config); + const api = await createPullRequestMergeTask(githubToken, flags); // Perform the merge. Force mode can be activated through a command line flag. // Alternatively, if the merge fails with non-fatal failures, the script @@ -128,13 +127,8 @@ export async function mergePullRequest( * and optional explicit configuration. An explicit configuration can be specified * when the merge script is used outside of a `ng-dev` configured repository. */ -async function createPullRequestMergeTask( - githubToken: string, projectRoot: string, explicitConfig?: MergeConfigWithRemote) { - if (explicitConfig !== undefined) { - const git = new GitClient(githubToken, {github: explicitConfig.remote}, projectRoot); - return new PullRequestMergeTask(explicitConfig, git); - } - +async function createPullRequestMergeTask(githubToken: string, flags: PullRequestMergeTaskFlags) { + const projectRoot = getRepoBaseDir(); const devInfraConfig = getConfig(); const git = new GitClient(githubToken, devInfraConfig, projectRoot); const {config, errors} = await loadAndValidateConfig(devInfraConfig, git.github); @@ -150,5 +144,5 @@ async function createPullRequestMergeTask( config!.remote = devInfraConfig.github; // We can cast this to a merge config with remote because we always set the // remote above. - return new PullRequestMergeTask(config! as MergeConfigWithRemote, git); + return new PullRequestMergeTask(config! as MergeConfigWithRemote, git, flags); } diff --git a/dev-infra/pr/merge/pull-request.ts b/dev-infra/pr/merge/pull-request.ts index 8d8e667969..222df0007a 100644 --- a/dev-infra/pr/merge/pull-request.ts +++ b/dev-infra/pr/merge/pull-request.ts @@ -9,6 +9,7 @@ import * as Octokit from '@octokit/rest'; import {GitClient} from '../../utils/git/index'; +import {TargetLabel} from './config'; import {PullRequestFailure} from './failures'; import {matchesPattern} from './string-pattern'; @@ -61,9 +62,14 @@ export async function loadAndValidatePullRequest( return PullRequestFailure.claUnsigned(); } - const targetLabel = getTargetLabelFromPullRequest(config, labels); - if (targetLabel === null) { - return PullRequestFailure.noTargetLabel(); + let targetLabel: TargetLabel; + try { + targetLabel = getTargetLabelFromPullRequest(config, labels); + } catch (error) { + if (error instanceof InvalidTargetLabelError) { + return new PullRequestFailure(error.failureMessage); + } + throw error; } const {data: {state}} = diff --git a/dev-infra/pr/merge/strategies/autosquash-merge.ts b/dev-infra/pr/merge/strategies/autosquash-merge.ts index e304ed98bd..f4d72c4680 100644 --- a/dev-infra/pr/merge/strategies/autosquash-merge.ts +++ b/dev-infra/pr/merge/strategies/autosquash-merge.ts @@ -32,7 +32,8 @@ export class AutosquashMergeStrategy extends MergeStrategy { * @returns A pull request failure or null in case of success. */ async merge(pullRequest: PullRequest): Promise { - const {prNumber, targetBranches, requiredBaseSha, needsCommitMessageFixup} = pullRequest; + const {prNumber, targetBranches, requiredBaseSha, needsCommitMessageFixup, githubTargetBranch} = + pullRequest; // In case a required base is specified for this pull request, check if the pull // request contains the given commit. If not, return a pull request failure. This // check is useful for enforcing that PRs are rebased on top of a given commit. e.g. @@ -83,6 +84,30 @@ export class AutosquashMergeStrategy extends MergeStrategy { } this.pushTargetBranchesUpstream(targetBranches); + + // For PRs which do not target the `master` branch on Github, Github does not automatically + // close the PR when its commit is pushed into the repository. To ensure these PRs are + // correctly marked as closed, we must detect this situation and close the PR via the API after + // the upstream pushes are completed. + if (githubTargetBranch !== 'master') { + /** The local branch name of the github targeted branch. */ + const localBranch = this.getLocalTargetBranchName(githubTargetBranch); + /** The SHA of the commit pushed to github which represents closing the PR. */ + const sha = this.git.run(['rev-parse', localBranch]).stdout.trim(); + // Create a comment saying the PR was closed by the SHA. + await this.git.github.issues.createComment({ + ...this.git.remoteParams, + issue_number: pullRequest.prNumber, + body: `Closed by commit ${sha}` + }); + // Actually close the PR. + await this.git.github.pulls.update({ + ...this.git.remoteParams, + pull_number: pullRequest.prNumber, + state: 'closed', + }); + } + return null; } } diff --git a/dev-infra/pr/merge/target-label.ts b/dev-infra/pr/merge/target-label.ts index 3a83fc4e89..707676306a 100644 --- a/dev-infra/pr/merge/target-label.ts +++ b/dev-infra/pr/merge/target-label.ts @@ -27,14 +27,24 @@ export class InvalidTargetLabelError { /** Gets the target label from the specified pull request labels. */ export function getTargetLabelFromPullRequest( - config: Pick, labels: string[]): TargetLabel|null { + config: Pick, labels: string[]): TargetLabel { + /** List of discovered target labels for the PR. */ + const matches = []; for (const label of labels) { const match = config.labels.find(({pattern}) => matchesPattern(label, pattern)); if (match !== undefined) { - return match; + matches.push(match); } } - return null; + if (matches.length === 1) { + return matches[0]; + } + if (matches.length === 0) { + throw new InvalidTargetLabelError( + 'Unable to determine target for the PR as it has no target label.'); + } + throw new InvalidTargetLabelError( + 'Unable to determine target for the PR as it has multiple target labels.'); } /** diff --git a/dev-infra/pr/merge/task.ts b/dev-infra/pr/merge/task.ts index b42f532b86..19e2fd7dd9 100644 --- a/dev-infra/pr/merge/task.ts +++ b/dev-infra/pr/merge/task.ts @@ -34,13 +34,28 @@ export interface MergeResult { failure?: PullRequestFailure; } +export interface PullRequestMergeTaskFlags { + branchPrompt: boolean; +} + +const defaultPullRequestMergeTaskFlags: PullRequestMergeTaskFlags = { + branchPrompt: true, +}; + /** * Class that accepts a merge script configuration and Github token. It provides * a programmatic interface for merging multiple pull requests based on their * labels that have been resolved through the merge script configuration. */ export class PullRequestMergeTask { - constructor(public config: MergeConfigWithRemote, public git: GitClient) {} + private flags: PullRequestMergeTaskFlags; + + constructor( + public config: MergeConfigWithRemote, public git: GitClient, + flags: Partial) { + // Update flags property with the provided flags values as patches to the default flag values. + this.flags = {...defaultPullRequestMergeTaskFlags, ...flags}; + } /** * Merges the given pull request and pushes it upstream. @@ -79,7 +94,8 @@ export class PullRequestMergeTask { } - if (!await promptConfirm(getTargettedBranchesConfirmationPromptMessage(pullRequest))) { + if (this.flags.branchPrompt && + !await promptConfirm(getTargettedBranchesConfirmationPromptMessage(pullRequest))) { return {status: MergeStatus.USER_ABORTED}; } diff --git a/dev-infra/pr/rebase/BUILD.bazel b/dev-infra/pr/rebase/BUILD.bazel index 6b420d8f6f..13089d1cbf 100644 --- a/dev-infra/pr/rebase/BUILD.bazel +++ b/dev-infra/pr/rebase/BUILD.bazel @@ -11,9 +11,11 @@ ts_library( deps = [ "//dev-infra/commit-message", "//dev-infra/utils", + "@npm//@types/conventional-commits-parser", "@npm//@types/inquirer", "@npm//@types/node", "@npm//@types/yargs", + "@npm//conventional-commits-parser", "@npm//inquirer", "@npm//typed-graphqlify", "@npm//yargs", diff --git a/dev-infra/pr/rebase/index.ts b/dev-infra/pr/rebase/index.ts index 48aecbbc37..5dac748e06 100644 --- a/dev-infra/pr/rebase/index.ts +++ b/dev-infra/pr/rebase/index.ts @@ -7,8 +7,9 @@ */ import {types as graphQLTypes} from 'typed-graphqlify'; -import {parseCommitMessagesForRange, ParsedCommitMessage} from '../../commit-message/parse'; +import {Commit} from '../../commit-message/parse'; +import {getCommitsInRange} from '../../commit-message/utils'; import {getConfig, NgDevConfig} from '../../utils/config'; import {error, info, promptConfirm} from '../../utils/console'; import {addTokenToGitHttpsUrl} from '../../utils/git/github-urls'; @@ -93,10 +94,9 @@ export async function rebasePr( const commonAncestorSha = git.run(['merge-base', 'HEAD', 'FETCH_HEAD']).stdout.trim(); - const commits = parseCommitMessagesForRange(`${commonAncestorSha}..HEAD`); + const commits = await getCommitsInRange(commonAncestorSha, 'HEAD'); - let squashFixups = - commits.filter((commit: ParsedCommitMessage) => commit.isFixup).length === 0 ? + let squashFixups = commits.filter((commit: Commit) => commit.isFixup).length === 0 ? false : await promptConfirm( `PR #${prNumber} contains fixup commits, would you like to squash them during rebase?`, diff --git a/dev-infra/release/BUILD.bazel b/dev-infra/release/BUILD.bazel index 16e0a5146a..316d39a627 100644 --- a/dev-infra/release/BUILD.bazel +++ b/dev-infra/release/BUILD.bazel @@ -12,6 +12,7 @@ ts_library( "//dev-infra/release/publish", "//dev-infra/release/set-dist-tag", "//dev-infra/utils", + "@npm//@types/node", "@npm//@types/yargs", "@npm//yargs", ], diff --git a/dev-infra/release/build/BUILD.bazel b/dev-infra/release/build/BUILD.bazel index 679c0f2c1e..75136639ab 100644 --- a/dev-infra/release/build/BUILD.bazel +++ b/dev-infra/release/build/BUILD.bazel @@ -1,6 +1,10 @@ load("@npm//@bazel/typescript:index.bzl", "ts_library") load("//tools:defaults.bzl", "jasmine_node_test") +exports_files([ + "build-worker.ts", +]) + ts_library( name = "build", srcs = glob( diff --git a/dev-infra/release/build/build.spec.ts b/dev-infra/release/build/build.spec.ts index 9f6ca0fb80..16cb0d5b66 100644 --- a/dev-infra/release/build/build.spec.ts +++ b/dev-infra/release/build/build.spec.ts @@ -50,7 +50,7 @@ describe('ng-dev release build', () => { expect(writeSpy).toHaveBeenCalledTimes(1); const jsonText = writeSpy.calls.mostRecent().args[0] as string; - const parsed = JSON.parse(jsonText); + const parsed = JSON.parse(jsonText) as releaseConfig.BuiltPackage[]; expect(parsed).toEqual([ {name: '@angular/pkg1', outputPath: 'dist/pkg1'}, diff --git a/dev-infra/release/cli.ts b/dev-infra/release/cli.ts index 6281e4ab88..4e8531cf3b 100644 --- a/dev-infra/release/cli.ts +++ b/dev-infra/release/cli.ts @@ -10,7 +10,7 @@ import * as yargs from 'yargs'; import {ReleaseBuildCommandModule} from './build/cli'; import {ReleasePublishCommandModule} from './publish/cli'; import {ReleaseSetDistTagCommand} from './set-dist-tag/cli'; -import {buildEnvStamp} from './stamping/env-stamp'; +import {BuildEnvStampCommand} from './stamping/cli'; /** Build the parser for the release commands. */ export function buildReleaseParser(localYargs: yargs.Argv) { @@ -20,7 +20,5 @@ export function buildReleaseParser(localYargs: yargs.Argv) { .command(ReleasePublishCommandModule) .command(ReleaseBuildCommandModule) .command(ReleaseSetDistTagCommand) - .command( - 'build-env-stamp', 'Build the environment stamping information', {}, - () => buildEnvStamp()); + .command(BuildEnvStampCommand); } diff --git a/dev-infra/release/config/index.ts b/dev-infra/release/config/index.ts index da1f759116..e8fb95909a 100644 --- a/dev-infra/release/config/index.ts +++ b/dev-infra/release/config/index.ts @@ -34,6 +34,8 @@ export interface ReleaseConfig { */ // TODO: Remove this in favor of a canonical changelog format across the Angular organization. extractReleaseNotesPattern?: (version: semver.SemVer) => RegExp; + /** The list of github labels to add to the release PRs. */ + releasePrLabels?: string[]; } /** Configuration for releases in the dev-infra configuration. */ diff --git a/dev-infra/release/publish/actions.ts b/dev-infra/release/publish/actions.ts index 3b22e81feb..af50f41a11 100644 --- a/dev-infra/release/publish/actions.ts +++ b/dev-infra/release/publish/actions.ts @@ -21,7 +21,7 @@ import {runNpmPublish} from '../versioning/npm-publish'; import {FatalReleaseActionError, UserAbortedReleaseActionError} from './actions-error'; import {getCommitMessageForRelease, getReleaseNoteCherryPickCommitMessage} from './commit-message'; import {changelogPath, packageJsonPath, waitForPullRequestInterval} from './constants'; -import {invokeReleaseBuildCommand, invokeYarnInstallCommand} from './external-commands'; +import {invokeBazelCleanCommand, invokeReleaseBuildCommand, invokeYarnInstallCommand} from './external-commands'; import {findOwnedForksOfRepoQuery} from './graphql-queries'; import {getPullRequestState} from './pull-request-state'; import {getDefaultExtractReleaseNotesPattern, getLocalChangelogFilePath} from './release-notes'; @@ -82,7 +82,8 @@ export abstract class ReleaseAction { /** Updates the version in the project top-level `package.json` file. */ protected async updateProjectVersion(newVersion: semver.SemVer) { const pkgJsonPath = join(this.projectDir, packageJsonPath); - const pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, 'utf8')); + const pkgJson = + JSON.parse(await fs.readFile(pkgJsonPath, 'utf8')) as {version: string, [key: string]: any}; pkgJson.version = newVersion.format(); // Write the `package.json` file. Note that we add a trailing new line // to avoid unnecessary diff. IDEs usually add a trailing new line. @@ -282,6 +283,15 @@ export abstract class ReleaseAction { title, }); + // Add labels to the newly created PR if provided in the configuration. + if (this.config.releasePrLabels !== undefined) { + await this.git.github.issues.addLabels({ + ...this.git.remoteParams, + issue_number: data.number, + labels: this.config.releasePrLabels, + }); + } + info(green(` ✓ Created pull request #${data.number} in ${repoSlug}.`)); return { id: data.number, @@ -441,7 +451,7 @@ export abstract class ReleaseAction { } // Create a cherry-pick pull request that should be merged by the caretaker. - const {url} = await this.pushChangesToForkAndCreatePullRequest( + const {url, id} = await this.pushChangesToForkAndCreatePullRequest( nextBranch, `changelog-cherry-pick-${newVersion}`, commitMessage, `Cherry-picks the changelog from the "${stagingBranch}" branch to the next ` + `branch (${nextBranch}).`); @@ -450,6 +460,10 @@ export abstract class ReleaseAction { ` ✓ Pull request for cherry-picking the changelog into "${nextBranch}" ` + 'has been created.')); info(yellow(` Please ask team members to review: ${url}.`)); + + // Wait for the Pull Request to be merged. + await this.waitForPullRequestToBeMerged(id); + return true; } @@ -458,7 +472,7 @@ export abstract class ReleaseAction { * The release is created by tagging the specified commit SHA. */ private async _createGithubReleaseForVersion( - newVersion: semver.SemVer, versionBumpCommitSha: string) { + newVersion: semver.SemVer, versionBumpCommitSha: string, prerelease: boolean) { const tagName = newVersion.format(); await this.git.github.git.createRef({ ...this.git.remoteParams, @@ -471,6 +485,8 @@ export abstract class ReleaseAction { ...this.git.remoteParams, name: `v${newVersion}`, tag_name: tagName, + prerelease, + }); info(green(` ✓ Created v${newVersion} release in Github.`)); } @@ -501,13 +517,15 @@ export abstract class ReleaseAction { // created in the `next` branch. The new package would not be part of the patch branch, // so we cannot build and publish it. await invokeYarnInstallCommand(this.projectDir); + await invokeBazelCleanCommand(this.projectDir); const builtPackages = await invokeReleaseBuildCommand(); // Verify the packages built are the correct version. await this._verifyPackageVersions(newVersion, builtPackages); // Create a Github release for the new version. - await this._createGithubReleaseForVersion(newVersion, versionBumpCommitSha); + await this._createGithubReleaseForVersion( + newVersion, versionBumpCommitSha, npmDistTag === 'next'); // Walk through all built packages and publish them to NPM. for (const builtPackage of builtPackages) { @@ -545,7 +563,8 @@ export abstract class ReleaseAction { private async _verifyPackageVersions(version: semver.SemVer, packages: BuiltPackage[]) { for (const pkg of packages) { const {version: packageJsonVersion} = - JSON.parse(await fs.readFile(join(pkg.outputPath, 'package.json'), 'utf8')); + JSON.parse(await fs.readFile(join(pkg.outputPath, 'package.json'), 'utf8')) as + {version: string, [key: string]: any}; if (version.compare(packageJsonVersion) !== 0) { error(red('The built package version does not match the version being released.')); error(` Release Version: ${version.version}`); diff --git a/dev-infra/release/publish/cli.ts b/dev-infra/release/publish/cli.ts index 171f2edb28..068f2cf93e 100644 --- a/dev-infra/release/publish/cli.ts +++ b/dev-infra/release/publish/cli.ts @@ -36,10 +36,11 @@ async function handler(args: Arguments) { switch (result) { case CompletionState.FATAL_ERROR: error(red(`Release action has been aborted due to fatal errors. See above.`)); - process.exitCode = 1; + process.exitCode = 2; break; case CompletionState.MANUALLY_ABORTED: info(yellow(`Release action has been manually aborted.`)); + process.exitCode = 1; break; case CompletionState.SUCCESS: info(green(`Release action has completed successfully.`)); diff --git a/dev-infra/release/publish/external-commands.ts b/dev-infra/release/publish/external-commands.ts index 49698607e6..2892bc47da 100644 --- a/dev-infra/release/publish/external-commands.ts +++ b/dev-infra/release/publish/external-commands.ts @@ -64,7 +64,7 @@ export async function invokeReleaseBuildCommand(): Promise { info(green(' ✓ Built release output for all packages.')); // The `ng-dev release build` command prints a JSON array to stdout // that represents the built release packages and their output paths. - return JSON.parse(stdout.trim()); + return JSON.parse(stdout.trim()) as BuiltPackage[]; } catch (e) { spinner.stop(); error(e); @@ -90,3 +90,20 @@ export async function invokeYarnInstallCommand(projectDir: string): Promise { + try { + // Note: No progress indicator needed as that is the responsibility of the command. + // TODO: Consider using an Ora spinner instead to ensure minimal console output. + await spawnWithDebugOutput('yarn', ['bazel', 'clean'], {cwd: projectDir}); + info(green(' ✓ Cleaned bazel output tree.')); + } catch (e) { + error(e); + error(red(' ✘ An error occurred while cleaning the bazel output tree.')); + throw new FatalReleaseActionError(); + } +} diff --git a/dev-infra/release/publish/index.ts b/dev-infra/release/publish/index.ts index 99c999c167..ea978ded8a 100644 --- a/dev-infra/release/publish/index.ts +++ b/dev-infra/release/publish/index.ts @@ -9,10 +9,11 @@ import {ListChoiceOptions, prompt} from 'inquirer'; import {GithubConfig} from '../../utils/config'; -import {error, info, log, red, yellow} from '../../utils/console'; +import {debug, error, info, log, promptConfirm, red, yellow} from '../../utils/console'; import {GitClient} from '../../utils/git/index'; import {ReleaseConfig} from '../config'; import {ActiveReleaseTrains, fetchActiveReleaseTrains, nextBranchName} from '../versioning/active-release-trains'; +import {npmIsLoggedIn, npmLogin, npmLogout} from '../versioning/npm-publish'; import {printActiveReleaseTrains} from '../versioning/print-active-trains'; import {GithubRepoWithApi} from '../versioning/version-branches'; @@ -29,6 +30,8 @@ export enum CompletionState { export class ReleaseTool { /** Client for interacting with the Github API and the local Git command. */ private _git = new GitClient(this._githubToken, {github: this._github}, this._projectRoot); + /** The previous git commit to return back to after the release tool runs. */ + private previousGitBranchOrRevision = this._git.getCurrentBranchOrRevision(); constructor( protected _config: ReleaseConfig, protected _github: GithubConfig, @@ -46,6 +49,10 @@ export class ReleaseTool { return CompletionState.FATAL_ERROR; } + if (!await this._verifyNpmLoginState()) { + return CompletionState.MANUALLY_ABORTED; + } + const {owner, name} = this._github; const repo: GithubRepoWithApi = {owner, name, api: this._git.github}; const releaseTrains = await fetchActiveReleaseTrains(repo); @@ -55,7 +62,6 @@ export class ReleaseTool { await printActiveReleaseTrains(releaseTrains, this._config); const action = await this._promptForReleaseAction(releaseTrains); - const previousGitBranchOrRevision = this._git.getCurrentBranchOrRevision(); try { await action.perform(); @@ -70,12 +76,20 @@ export class ReleaseTool { } return CompletionState.FATAL_ERROR; } finally { - this._git.checkout(previousGitBranchOrRevision, true); + await this.cleanup(); } return CompletionState.SUCCESS; } + /** Run post release tool cleanups. */ + private async cleanup(): Promise { + // Return back to the git state from before the release tool ran. + this._git.checkout(this.previousGitBranchOrRevision, true); + // Ensure log out of NPM. + await npmLogout(this._config.publishRegistry); + } + /** Prompts the caretaker for a release action that should be performed. */ private async _promptForReleaseAction(activeTrains: ActiveReleaseTrains) { const choices: ListChoiceOptions[] = []; @@ -129,4 +143,39 @@ export class ReleaseTool { } return true; } + + /** + * Verifies that the user is logged into NPM at the correct registry, if defined for the release. + * @returns a boolean indicating whether the user is logged into NPM. + */ + private async _verifyNpmLoginState(): Promise { + const registry = `NPM at the ${this._config.publishRegistry ?? 'default NPM'} registry`; + // TODO(josephperrott): remove wombat specific block once wombot allows `npm whoami` check to + // check the status of the local token in the .npmrc file. + if (this._config.publishRegistry?.includes('wombat-dressing-room.appspot.com')) { + info('Unable to determine NPM login state for wombat proxy, requiring login now.'); + try { + await npmLogin(this._config.publishRegistry); + } catch { + return false; + } + return true; + } + if (await npmIsLoggedIn(this._config.publishRegistry)) { + debug(`Already logged into ${registry}.`); + return true; + } + error(red(` ✘ Not currently logged into ${registry}.`)); + const shouldLogin = await promptConfirm('Would you like to log into NPM now?'); + if (shouldLogin) { + debug('Starting NPM login.'); + try { + await npmLogin(this._config.publishRegistry); + } catch { + return false; + } + return true; + } + return false; + } } diff --git a/dev-infra/release/publish/pull-request-state.ts b/dev-infra/release/publish/pull-request-state.ts index 9f318ee406..129ef2dba5 100644 --- a/dev-infra/release/publish/pull-request-state.ts +++ b/dev-infra/release/publish/pull-request-state.ts @@ -9,6 +9,9 @@ import * as Octokit from '@octokit/rest'; import {GitClient} from '../../utils/git/index'; +/** Thirty seconds in milliseconds. */ +const THIRTY_SECONDS_IN_MS = 30000; + /** State of a pull request in Github. */ export type PullRequestState = 'merged'|'closed'|'open'; @@ -17,11 +20,16 @@ export async function getPullRequestState(api: GitClient, id: number): Promise

    { }; it('should not modify release train versions and cause invalid other actions', async () => { + // The cached npm package information needs to be deleted as depending on the test order + // their may or may not be packages in the cache, causing the number of active LTS branches + // in this test to be 2 instead of 0. + for (const packageName in _npmPackageInfoCache) { + delete _npmPackageInfoCache[packageName]; + } + const {releaseConfig, gitClient} = getTestingMocksForReleaseAction(); const descriptions: string[] = []; @@ -93,7 +101,8 @@ describe('common release action logic', () => { // it is properly appended. Also expect a pull request to be created in the fork. repo.expectChangelogFetch(branchName, fakeReleaseNotes) .expectFindForkRequest(fork) - .expectPullRequestToBeCreated('master', fork, forkBranchName, 200); + .expectPullRequestToBeCreated('master', fork, forkBranchName, 200) + .expectPullRequestWait(200); // Simulate that the fork branch name is available. fork.expectBranchRequest(forkBranchName, null); @@ -119,7 +128,8 @@ describe('common release action logic', () => { // it is properly appended. Also expect a pull request to be created in the fork. repo.expectChangelogFetch(branchName, customReleaseNotes) .expectFindForkRequest(fork) - .expectPullRequestToBeCreated('master', fork, forkBranchName, 200); + .expectPullRequestToBeCreated('master', fork, forkBranchName, 200) + .expectPullRequestWait(200); // Simulate that the fork branch name is available. fork.expectBranchRequest(forkBranchName, null); @@ -138,7 +148,8 @@ describe('common release action logic', () => { // it is properly appended. Also expect a pull request to be created in the fork. repo.expectChangelogFetch(branchName, `non analyzable changelog`) .expectFindForkRequest(fork) - .expectPullRequestToBeCreated('master', fork, forkBranchName, 200); + .expectPullRequestToBeCreated('master', fork, forkBranchName, 200) + .expectPullRequestWait(200); // Simulate that the fork branch name is available. fork.expectBranchRequest(forkBranchName, null); @@ -166,7 +177,8 @@ describe('common release action logic', () => { // it is properly appended. Also expect a pull request to be created in the fork. repo.expectChangelogFetch(branchName, fakeReleaseNotes) .expectFindForkRequest(fork) - .expectPullRequestToBeCreated('master', fork, forkBranchName, 200); + .expectPullRequestToBeCreated('master', fork, forkBranchName, 200) + .expectPullRequestWait(200); // Simulate that the fork branch name is available. fork.expectBranchRequest(forkBranchName, null); diff --git a/dev-infra/release/publish/test/cut-next-prerelease.spec.ts b/dev-infra/release/publish/test/cut-next-prerelease.spec.ts index ab37350788..3802d6653e 100644 --- a/dev-infra/release/publish/test/cut-next-prerelease.spec.ts +++ b/dev-infra/release/publish/test/cut-next-prerelease.spec.ts @@ -49,7 +49,7 @@ describe('cut next pre-release action', () => { await expectStagingAndPublishWithoutCherryPick(action, 'master', '10.2.0-next.0', 'next'); const pkgJsonContents = readFileSync(join(action.testTmpDir, packageJsonPath), 'utf8'); - const pkgJson = JSON.parse(pkgJsonContents); + const pkgJson = JSON.parse(pkgJsonContents) as {version: string, [key: string]: any}; expect(pkgJson.version).toBe('10.2.0-next.0', 'Expected version to not have changed.'); }); diff --git a/dev-infra/release/publish/test/test-utils.ts b/dev-infra/release/publish/test/test-utils.ts index 8225a30980..3b36e0f6de 100644 --- a/dev-infra/release/publish/test/test-utils.ts +++ b/dev-infra/release/publish/test/test-utils.ts @@ -92,6 +92,7 @@ export function setupReleaseActionForTesting( spyOn(npm, 'runNpmPublish').and.resolveTo(); spyOn(externalCommands, 'invokeSetNpmDistCommand').and.resolveTo(); spyOn(externalCommands, 'invokeYarnInstallCommand').and.resolveTo(); + spyOn(externalCommands, 'invokeBazelCleanCommand').and.resolveTo(); spyOn(externalCommands, 'invokeReleaseBuildCommand').and.resolveTo([ {name: '@angular/pkg1', outputPath: `${testTmpDir}/dist/pkg1`}, {name: '@angular/pkg2', outputPath: `${testTmpDir}/dist/pkg2`} @@ -194,7 +195,8 @@ export async function expectStagingAndPublishWithCherryPick( .expectTagToBeCreated(expectedTagName, 'STAGING_COMMIT_SHA') .expectReleaseToBeCreated(`v${expectedVersion}`, expectedTagName) .expectChangelogFetch(expectedBranch, getChangelogForVersion(expectedVersion)) - .expectPullRequestToBeCreated('master', fork, expectedCherryPickForkBranch, 300); + .expectPullRequestToBeCreated('master', fork, expectedCherryPickForkBranch, 300) + .expectPullRequestWait(300); // In the fork, we make the staging and cherry-pick branches appear as // non-existent, so that the PRs can be created properly without collisions. diff --git a/dev-infra/release/stamping/env-stamp.ts b/dev-infra/release/stamping/env-stamp.ts index 184b63d7d6..5e6e993be0 100644 --- a/dev-infra/release/stamping/env-stamp.ts +++ b/dev-infra/release/stamping/env-stamp.ts @@ -6,8 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ +import {join} from 'path'; + +import {getRepoBaseDir} from '../../utils/config'; import {exec as _exec} from '../../utils/shelljs'; +export type EnvStampMode = 'snapshot'|'release'; + /** * Log the environment variables expected by bazel for stamping. * @@ -18,13 +23,13 @@ import {exec as _exec} from '../../utils/shelljs'; * Note: git operations, especially git status, take a long time inside mounted docker volumes * in Windows or OSX hosts (https://github.com/docker/for-win/issues/188). */ -export function buildEnvStamp() { +export function buildEnvStamp(mode: EnvStampMode) { console.info(`BUILD_SCM_BRANCH ${getCurrentBranch()}`); console.info(`BUILD_SCM_COMMIT_SHA ${getCurrentSha()}`); console.info(`BUILD_SCM_HASH ${getCurrentSha()}`); console.info(`BUILD_SCM_LOCAL_CHANGES ${hasLocalChanges()}`); console.info(`BUILD_SCM_USER ${getCurrentGitUser()}`); - console.info(`BUILD_SCM_VERSION ${getSCMVersion()}`); + console.info(`BUILD_SCM_VERSION ${getSCMVersion(mode)}`); process.exit(0); } @@ -38,11 +43,24 @@ function hasLocalChanges() { return !!exec(`git status --untracked-files=no --porcelain`); } -/** Get the version based on the most recent semver tag. */ -function getSCMVersion() { - const version = exec(`git describe --match [0-9]*.[0-9]*.[0-9]* --abbrev=7 --tags HEAD`); - return `${version.replace(/-([0-9]+)-g/, '+$1.sha-')}${ - (hasLocalChanges() ? '.with-local-changes' : '')}`; +/** + * Get the version for generated packages. + * + * In snapshot mode, the version is based on the most recent semver tag. + * In release mode, the version is based on the base package.json version. + */ +function getSCMVersion(mode: EnvStampMode) { + if (mode === 'release') { + const packageJsonPath = join(getRepoBaseDir(), 'package.json'); + const {version} = require(packageJsonPath); + return version; + } + if (mode === 'snapshot') { + const version = exec(`git describe --match [0-9]*.[0-9]*.[0-9]* --abbrev=7 --tags HEAD`); + return `${version.replace(/-([0-9]+)-g/, '+$1.sha-')}${ + (hasLocalChanges() ? '.with-local-changes' : '')}`; + } + return '0.0.0'; } /** Get the current SHA of HEAD. */ diff --git a/dev-infra/release/versioning/npm-publish.ts b/dev-infra/release/versioning/npm-publish.ts index 7415c5e84b..c163b928e6 100644 --- a/dev-infra/release/versioning/npm-publish.ts +++ b/dev-infra/release/versioning/npm-publish.ts @@ -36,3 +36,55 @@ export async function setNpmTagForPackage( } await spawnWithDebugOutput('npm', args, {mode: 'silent'}); } + +/** + * Checks whether the user is currently logged into NPM. + * @returns Whether the user is currently logged into NPM. + */ +export async function npmIsLoggedIn(registryUrl: string|undefined): Promise { + const args = ['whoami']; + // If a custom registry URL has been specified, add the `--registry` flag. + if (registryUrl !== undefined) { + args.push('--registry', registryUrl); + } + try { + await spawnWithDebugOutput('npm', args, {mode: 'silent'}); + } catch (e) { + return false; + } + return true; +} + +/** + * Log into NPM at a provided registry. + * @throws With the process log output if the login fails. + */ +export async function npmLogin(registryUrl: string|undefined) { + const args = ['login', '--no-browser']; + // If a custom registry URL has been specified, add the `--registry` flag. The `--registry` flag + // must be spliced into the correct place in the command as npm expects it to be the flag + // immediately following the login subcommand. + if (registryUrl !== undefined) { + args.splice(1, 0, '--registry', registryUrl); + } + await spawnWithDebugOutput('npm', args); +} + +/** + * Log out of NPM at a provided registry. + * @returns Whether the user was logged out of NPM. + */ +export async function npmLogout(registryUrl: string|undefined): Promise { + const args = ['logout']; + // If a custom registry URL has been specified, add the `--registry` flag. The `--registry` flag + // must be spliced into the correct place in the command as npm expects it to be the flag + // immediately following the logout subcommand. + if (registryUrl !== undefined) { + args.splice(1, 0, '--registry', registryUrl); + } + try { + await spawnWithDebugOutput('npm', args, {mode: 'silent'}); + } finally { + return npmIsLoggedIn(registryUrl); + } +} diff --git a/dev-infra/release/versioning/version-branches.ts b/dev-infra/release/versioning/version-branches.ts index 7a934d3a76..5db34f82c3 100644 --- a/dev-infra/release/versioning/version-branches.ts +++ b/dev-infra/release/versioning/version-branches.ts @@ -35,7 +35,8 @@ export async function getVersionOfBranch( repo: GithubRepoWithApi, branchName: string): Promise { const {data} = await repo.api.repos.getContents( {owner: repo.owner, repo: repo.name, path: '/package.json', ref: branchName}); - const {version} = JSON.parse(Buffer.from(data.content, 'base64').toString()); + const {version} = JSON.parse(Buffer.from(data.content, 'base64').toString()) as + {version: string, [key: string]: any}; const parsedVersion = semver.parse(version); if (parsedVersion === null) { throw Error(`Invalid version detected in following branch: ${branchName}.`); diff --git a/dev-infra/tmpl-package.json b/dev-infra/tmpl-package.json index 350f316044..8d89c7228e 100644 --- a/dev-infra/tmpl-package.json +++ b/dev-infra/tmpl-package.json @@ -16,9 +16,10 @@ "brotli": "", "chalk": "", "cli-progress": "", + "conventional-commits-parser": "", + "git-raw-commits": "", "glob": "", "inquirer": "", - "inquirer-autocomplete-prompt": "", "minimatch": "", "multimatch": "", "node-fetch": "", @@ -53,11 +54,5 @@ "ts-node": { "optional": true } - }, - "bazelWorkspaces": { - "npm_angular_dev_infra_private": { - "version": "0.0.0-PLACEHOLDER", - "rootPath": "." - } } } diff --git a/dev-infra/ts-circular-dependencies/index.ts b/dev-infra/ts-circular-dependencies/index.ts index 2800c31ba4..279a281da5 100644 --- a/dev-infra/ts-circular-dependencies/index.ts +++ b/dev-infra/ts-circular-dependencies/index.ts @@ -58,7 +58,7 @@ export function main( const cycles: ReferenceChain[] = []; const checkedNodes = new WeakSet(); - globSync(glob, {absolute: true}).forEach(filePath => { + globSync(glob, {absolute: true, ignore: ['**/node_modules/**']}).forEach(filePath => { const sourceFile = analyzer.getSourceFile(filePath); cycles.push(...analyzer.findCycles(sourceFile, checkedNodes)); }); @@ -93,7 +93,7 @@ export function main( info(yellow(` Please rerun with "--warnings" to inspect unresolved imports.`)); } - const expected: Golden = JSON.parse(readFileSync(goldenFile, 'utf8')); + const expected = JSON.parse(readFileSync(goldenFile, 'utf8')) as Golden; const {fixedCircularDeps, newCircularDeps} = compareGoldens(actual, expected); const isMatching = fixedCircularDeps.length === 0 && newCircularDeps.length === 0; diff --git a/dev-infra/utils/BUILD.bazel b/dev-infra/utils/BUILD.bazel index 122ad9fdf2..2b3404c485 100644 --- a/dev-infra/utils/BUILD.bazel +++ b/dev-infra/utils/BUILD.bazel @@ -21,7 +21,6 @@ ts_library( "@npm//@types/yargs", "@npm//chalk", "@npm//inquirer", - "@npm//inquirer-autocomplete-prompt", "@npm//shelljs", "@npm//tslib", "@npm//typed-graphqlify", diff --git a/dev-infra/utils/config.ts b/dev-infra/utils/config.ts index 358c02d766..75d8a9fbee 100644 --- a/dev-infra/utils/config.ts +++ b/dev-infra/utils/config.ts @@ -100,7 +100,7 @@ function validateCommonConfig(config: Partial) { * configuration file cannot be read. */ function readConfigFile(configPath: string, returnEmptyObjectOnError = false): object { - // If the the `.ts` extension has not been set up already, and a TypeScript based + // If the `.ts` extension has not been set up already, and a TypeScript based // version of the given configuration seems to exist, set up `ts-node` if available. if (require.extensions['.ts'] === undefined && existsSync(`${configPath}.ts`) && isTsNodeAvailable()) { diff --git a/dev-infra/utils/console.ts b/dev-infra/utils/console.ts index 41d2c8a3a5..d07b9e140a 100644 --- a/dev-infra/utils/console.ts +++ b/dev-infra/utils/console.ts @@ -8,8 +8,7 @@ import chalk from 'chalk'; import {writeFileSync} from 'fs'; -import {createPromptModule, ListChoiceOptions, prompt} from 'inquirer'; -import * as inquirerAutocomplete from 'inquirer-autocomplete-prompt'; +import {prompt} from 'inquirer'; import {join} from 'path'; import {Arguments} from 'yargs'; @@ -33,47 +32,6 @@ export async function promptConfirm(message: string, defaultValue = false): Prom .result; } -/** Prompts the user to select an option from a filterable autocomplete list. */ -export async function promptAutocomplete( - message: string, choices: (string|ListChoiceOptions)[]): Promise; -/** - * Prompts the user to select an option from a filterable autocomplete list, with an option to - * choose no value. - */ -export async function promptAutocomplete( - message: string, choices: (string|ListChoiceOptions)[], - noChoiceText?: string): Promise; -export async function promptAutocomplete( - message: string, choices: (string|ListChoiceOptions)[], - noChoiceText?: string): Promise { - // Creates a local prompt module with an autocomplete prompt type. - const prompt = createPromptModule({}).registerPrompt('autocomplete', inquirerAutocomplete); - if (noChoiceText) { - choices = [noChoiceText, ...choices]; - } - // `prompt` must be cast as `any` as the autocomplete typings are not available. - const result = (await (prompt as any)({ - type: 'autocomplete', - name: 'result', - message, - source: (_: any, input: string) => { - if (!input) { - return Promise.resolve(choices); - } - return Promise.resolve(choices.filter(choice => { - if (typeof choice === 'string') { - return choice.includes(input); - } - return choice.name!.includes(input); - })); - } - })).result; - if (result === noChoiceText) { - return false; - } - return result; -} - /** Prompts the user for one line of input. */ export async function promptInput(message: string): Promise { return (await prompt<{result: string}>({type: 'input', name: 'result', message})).result; @@ -193,7 +151,9 @@ export function captureLogOutputForCommand(argv: Arguments) { // On process exit, write the logged output to the appropriate log files process.on('exit', (code: number) => { - LOGGED_TEXT += `Command ran in ${new Date().getTime() - now.getTime()}ms`; + LOGGED_TEXT += `${headerLine}\n`; + LOGGED_TEXT += `Command ran in ${new Date().getTime() - now.getTime()}ms\n`; + LOGGED_TEXT += `Exit Code: ${code}\n`; /** Path to the log file location. */ const logFilePath = join(getRepoBaseDir(), '.ng-dev.log'); @@ -205,7 +165,9 @@ export function captureLogOutputForCommand(argv: Arguments) { // For failure codes greater than 1, the new logged lines should be written to a specific log // file for the command run failure. if (code > 1) { - writeFileSync(join(getRepoBaseDir(), `.ng-dev.err-${now.getTime()}.log`), LOGGED_TEXT); + const logFileName = `.ng-dev.err-${now.getTime()}.log`; + console.error(`Exit code: ${code}. Writing full log to ${logFileName}`); + writeFileSync(join(getRepoBaseDir(), logFileName), LOGGED_TEXT); } }); diff --git a/docs/BAZEL.md b/docs/BAZEL.md index 63d3c98dcb..87092e4c0c 100644 --- a/docs/BAZEL.md +++ b/docs/BAZEL.md @@ -75,7 +75,7 @@ See also: [`//.bazelrc`](https://github.com/angular/angular/blob/master/.bazelrc - Open chrome at: [chrome://inspect](chrome://inspect) -- Click on `Open dedicated DevTools for Node` to launch a debugger. +- Click on `Open dedicated DevTools for Node` to launch a debugger. - Run test: `yarn bazel test packages/core/test:test --config=debug` The process should automatically connect to the debugger. @@ -209,7 +209,7 @@ yarn bazel analyze-profile filename_name.profile --task_tree ".*" ``` To show all tasks that take longer than a certain threshold, use the `--task_tree_threshold` flag. -The default behaviour is to use a 50ms threshold. +The default behavior is to use a 50ms threshold. ``` yarn bazel analyze-profile filename_name.profile --task_tree ".*" --task_tree_threshold 5000 ``` diff --git a/docs/COMMITTER.md b/docs/COMMITTER.md index 91abbb4c66..59a058a63c 100644 --- a/docs/COMMITTER.md +++ b/docs/COMMITTER.md @@ -7,7 +7,7 @@ As a contributor, see the instructions in [CONTRIBUTING.md](../CONTRIBUTING.md). # Change approvals -Change approvals in our monorepo are managed via [PullApprove](https://docs.pullapprove.com/) and are configured via the [`.pullapprove`](../.pullapprove) file. +Change approvals in our monorepo are managed via [PullApprove](https://docs.pullapprove.com/) and are configured via the [`.pullapprove.yml`](../.pullapprove.yml) file. # Merging diff --git a/docs/SAVED_REPLIES.md b/docs/SAVED_REPLIES.md index 4e8e1d0839..6d801b945f 100644 --- a/docs/SAVED_REPLIES.md +++ b/docs/SAVED_REPLIES.md @@ -34,7 +34,7 @@ Thanks for reporting this issue. However this issue is a duplicate of an existin ## Angular: Insufficient Information Provided (v2) ``` -Thanks for reporting this issue. However, you didn't provide sufficient information for us to understand and reproduce the problem. Please check out [our submission guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-submitting-an-issue) to understand why we can't act on issues that are lacking important information. +Thanks for reporting this issue. However, you didn't provide sufficient information for us to understand and reproduce the problem. Please check out [our submission guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#submit-issue) to understand why we can't act on issues that are lacking important information. If the problem still exists in your application, please [open a new issue](https://github.com/angular/angular/issues/new/choose) and follow the instructions in the issue template. @@ -69,5 +69,5 @@ If the problem still exists in your application, please [open a new issue](https ``` Hello, we reviewed this issue and determined that it doesn't fall into the bug report or feature request category. This issue tracker is not suitable for support requests, please repost your issue on [StackOverflow](https://stackoverflow.com/) using tag `angular`. -If you are wondering why we don't resolve support issues via the issue tracker, please [check out this explanation](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-got-a-question-or-problem). +If you are wondering why we don't resolve support issues via the issue tracker, please [check out this explanation](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question). ``` diff --git a/docs/TRIAGE_AND_LABELS.md b/docs/TRIAGE_AND_LABELS.md index c41f469bd2..0f535698a6 100644 --- a/docs/TRIAGE_AND_LABELS.md +++ b/docs/TRIAGE_AND_LABELS.md @@ -53,6 +53,16 @@ Sometimes, especially in the case of cross-cutting issues or PRs, these PRs or i multiple components. In these cases, all applicable component labels should be used to triage the issue or PR. +### Community engagement + +* `help wanted` - Indicates an issue whose complexity/scope makes it suitable for a community contributor to pick up. +* `good first issue` - Indicates an issue that is suitable for first-time contributors. + (This label should be applied _in addition_ to `help wanted` for better discoverability.) + +`help wanted` and `good first issue` are [default GitHub labels] familiar to many developers. + +[default GitHub labels]: https://docs.github.com/en/github/managing-your-work-on-github/managing-labels#about-default-labels + ## Caretaker Triage Process (Initial Triage) The caretaker assigns `comp: *` labels to new issues as they come in. @@ -74,7 +84,7 @@ Detailed triage can be done by anyone familiar with the issue subject matter. Gauge whether the issue has enough information to act upon. This typically includes a test case via StackBlitz or GitHub and steps to reproduce. If the issue may be legitimate but needs more -information, add the "needs clarification" label. These labels can be revisted if the author can +information, add the "needs clarification" label. These labels can be revisited if the author can provide further clarification. If the issue does have enough information, move on to step 2. ### Step 2: Bug, feature, or discussion? @@ -88,7 +98,7 @@ infeasible, close the issue with a comment explaining why. If the issue is an RFC or discussion, apply the "discussion" label. Use your judgement to determine whether this discussion belongs on GitHub. Discussions here should pertain to the technical implementation details of Angular. Redirect requests for debugging help or advice to a more -appropriate channel unless they're capturing a legitimate bug. +appropriate channel unless they're capturing a legitimate bug. ### Step 3: Set a Priority @@ -104,17 +114,17 @@ For bug reports, set a priority label. | P5 | The team acknowledges the request but (due to any number of reasons) does not plan to work on or accept contributions for this request. The issue remains open for discussion. | -Issues marked with "feature" or "discussion" don't require a priority. +Issues marked with "feature" or "discussion" don't require a priority. ### Step 4: Apply additional information labels Many optional labels provide additional context for issues. Consider adding any of the following if they apply to the issue: -* Browser or operating system labels (`windows`, `ie11`, etc.) +* Browser or operating system labels (`windows`, `browser: ie 11`, etc.) * Labels that inform the severity (`regression`, `has workaround`, `no workaround`) * Labels that categorize the bug (`performance`, `refactoring`, `memory leak`) -* Community engagement labels (`good first issue`) +* Community engagement labels (`help wanted`, `good first issue`) Once this triage is done, the ng-bot automatically changes the milestone from `needs triage` to `Backlog`. @@ -168,7 +178,7 @@ Targeting an active release train: Special Cases: * `target: rc`: A critical fix for an active release-train while it is in a feature freeze or RC phase -* `target: lts`: A criticial fix for a specific release-train that is still within the long term support phase +* `target: lts`: A critical fix for a specific release-train that is still within the long term support phase Notes: diff --git a/goldens/circular-deps/packages.json b/goldens/circular-deps/packages.json index 3e9bf12faa..cae92bc971 100644 --- a/goldens/circular-deps/packages.json +++ b/goldens/circular-deps/packages.json @@ -108,45 +108,6 @@ "packages/compiler/src/render3/view/styling_builder.ts", "packages/compiler/src/render3/view/template.ts" ], - [ - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_ref.ts", - "packages/core/src/linker/view_container_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_ref.ts", - "packages/core/src/linker/view_container_ref.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/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_ref.ts", - "packages/core/src/linker/view_container_ref.ts", - "packages/core/src/linker/template_ref.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/types.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_ref.ts", - "packages/core/src/linker/view_container_ref.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/types.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", @@ -164,8 +125,6 @@ [ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_ref.ts", - "packages/core/src/linker/view_container_ref.ts", - "packages/core/src/linker/template_ref.ts", "packages/core/src/linker/view_ref.ts" ], [ @@ -205,18 +164,28 @@ "packages/core/src/errors.ts", "packages/core/src/view/types.ts" ], + [ + "packages/core/src/error_handler.ts", + "packages/core/src/errors.ts", + "packages/core/src/view/types.ts", + "packages/core/src/linker/template_ref.ts", + "packages/core/src/render3/instructions/shared.ts" + ], + [ + "packages/core/src/error_handler.ts", + "packages/core/src/errors.ts", + "packages/core/src/view/types.ts", + "packages/core/src/linker/view_container_ref.ts", + "packages/core/src/render3/instructions/shared.ts" + ], [ "packages/core/src/linker/component_factory_resolver.ts", + "packages/core/src/linker/component_factory.ts", "packages/core/src/linker/ng_module_factory.ts" ], [ - "packages/core/src/linker/template_ref.ts", - "packages/core/src/render3/view_ref.ts", - "packages/core/src/linker/view_container_ref.ts" - ], - [ - "packages/core/src/linker/view_container_ref.ts", - "packages/core/src/render3/view_ref.ts" + "packages/core/src/linker/component_factory_resolver.ts", + "packages/core/src/linker/ng_module_factory.ts" ], [ "packages/core/src/metadata/directives.ts", @@ -413,12 +382,6 @@ "packages/forms/src/directives/validators.ts", "packages/forms/src/validators.ts" ], - [ - "packages/language-service/src/completions.ts", - "packages/language-service/src/template.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" diff --git a/goldens/public-api/common/common.d.ts b/goldens/public-api/common/common.d.ts index de956a4cec..0b5ab5a10d 100644 --- a/goldens/public-api/common/common.d.ts +++ b/goldens/public-api/common/common.d.ts @@ -96,11 +96,12 @@ export declare function getLocaleWeekEndRange(locale: string): [WeekDay, WeekDay export declare function getNumberOfCurrencyDigits(code: string): number; -export declare class HashLocationStrategy extends LocationStrategy { +export declare class HashLocationStrategy extends LocationStrategy implements OnDestroy { constructor(_platformLocation: PlatformLocation, _baseHref?: string); back(): void; forward(): void; getBaseHref(): string; + ngOnDestroy(): void; onPopState(fn: LocationChangeListener): void; path(includeHash?: boolean): string; prepareExternalUrl(internal: string): string; @@ -324,11 +325,12 @@ export declare enum NumberSymbol { CurrencyGroup = 13 } -export declare class PathLocationStrategy extends LocationStrategy { +export declare class PathLocationStrategy extends LocationStrategy implements OnDestroy { constructor(_platformLocation: PlatformLocation, href?: string); back(): void; forward(): void; getBaseHref(): string; + ngOnDestroy(): void; onPopState(fn: LocationChangeListener): void; path(includeHash?: boolean): string; prepareExternalUrl(internal: string): string; @@ -355,8 +357,8 @@ export declare abstract class PlatformLocation { abstract forward(): void; abstract getBaseHrefFromDOM(): string; abstract getState(): unknown; - abstract onHashChange(fn: LocationChangeListener): void; - abstract onPopState(fn: LocationChangeListener): void; + abstract onHashChange(fn: LocationChangeListener): VoidFunction; + abstract onPopState(fn: LocationChangeListener): VoidFunction; abstract pushState(state: any, title: string, url: string): void; abstract replaceState(state: any, title: string, url: string): void; } @@ -419,7 +421,7 @@ export declare abstract class ViewportScroller { abstract scrollToPosition(position: [number, number]): void; abstract setHistoryScrollRestoration(scrollRestoration: 'auto' | 'manual'): void; abstract setOffset(offset: [number, number] | (() => [number, number])): void; - static ɵprov: never; + static ɵprov: unknown; } export declare enum WeekDay { @@ -431,3 +433,7 @@ export declare enum WeekDay { Friday = 5, Saturday = 6 } + +export declare abstract class XhrFactory { + abstract build(): XMLHttpRequest; +} diff --git a/goldens/public-api/common/http/http.d.ts b/goldens/public-api/common/http/http.d.ts index 83b1daa7b8..e32d85bd3d 100644 --- a/goldens/public-api/common/http/http.d.ts +++ b/goldens/public-api/common/http/http.d.ts @@ -10,9 +10,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -22,9 +23,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -34,9 +36,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -47,8 +50,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -59,8 +63,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -71,8 +76,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -83,8 +89,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -95,8 +102,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | (string | number | boolean)[]; }; reportProgress?: boolean; responseType?: 'json'; @@ -107,8 +115,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -119,8 +128,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -131,8 +141,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -143,8 +154,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -155,8 +167,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -166,9 +179,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -178,9 +192,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -190,9 +205,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -202,9 +218,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -214,9 +231,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -227,8 +245,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -239,8 +258,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -251,8 +271,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -263,8 +284,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -275,8 +297,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -287,8 +310,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -299,8 +323,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -311,8 +336,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -323,8 +349,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -335,8 +362,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -346,9 +374,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -358,9 +387,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -370,9 +400,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -382,9 +413,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -394,9 +426,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -407,8 +440,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -419,8 +453,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -431,8 +466,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -443,8 +479,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -455,8 +492,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -467,8 +505,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -479,8 +518,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -491,8 +531,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -503,8 +544,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -515,8 +557,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -526,9 +569,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -538,9 +582,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -552,9 +597,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -564,9 +610,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -576,9 +623,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -589,8 +637,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -601,8 +650,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -613,8 +663,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -625,8 +676,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -637,8 +689,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -649,8 +702,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -661,8 +715,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -673,8 +728,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -685,8 +741,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -697,8 +754,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -708,9 +766,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -720,9 +779,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -732,9 +792,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -744,9 +805,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -756,9 +818,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -769,8 +832,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -781,8 +845,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -793,8 +858,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -805,8 +871,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -817,8 +884,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -829,8 +897,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -841,8 +910,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -853,8 +923,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -865,8 +936,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -877,8 +949,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -888,9 +961,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -900,9 +974,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -912,9 +987,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -924,9 +1000,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -936,9 +1013,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -949,8 +1027,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -961,8 +1040,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -973,8 +1053,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -985,8 +1066,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -997,8 +1079,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -1009,8 +1092,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -1021,8 +1105,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -1033,8 +1118,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -1045,8 +1131,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -1057,8 +1144,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -1068,9 +1156,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -1080,9 +1169,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -1092,9 +1182,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -1104,9 +1195,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -1116,9 +1208,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -1129,8 +1222,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -1141,8 +1235,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -1153,8 +1248,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -1165,8 +1261,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -1177,8 +1274,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -1189,8 +1287,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -1201,8 +1300,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -1213,8 +1313,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -1225,8 +1326,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -1237,8 +1339,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -1248,9 +1351,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -1260,9 +1364,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType?: 'json'; @@ -1274,9 +1379,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -1287,9 +1393,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -1300,9 +1407,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -1313,8 +1421,9 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; observe: 'events'; reportProgress?: boolean; @@ -1327,8 +1436,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -1340,8 +1450,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'events'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -1352,10 +1463,11 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; reportProgress?: boolean; observe: 'events'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; responseType?: 'json'; withCredentials?: boolean; @@ -1365,10 +1477,11 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; reportProgress?: boolean; observe: 'events'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; responseType?: 'json'; withCredentials?: boolean; @@ -1379,8 +1492,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'arraybuffer'; @@ -1392,8 +1506,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'blob'; @@ -1405,8 +1520,9 @@ export declare class HttpClient { [header: string]: string | string[]; }; observe: 'response'; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; reportProgress?: boolean; responseType: 'text'; @@ -1417,10 +1533,11 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; reportProgress?: boolean; observe: 'response'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; responseType?: 'json'; withCredentials?: boolean; @@ -1430,10 +1547,11 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; reportProgress?: boolean; observe: 'response'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; responseType?: 'json'; withCredentials?: boolean; @@ -1443,9 +1561,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; responseType?: 'json'; reportProgress?: boolean; @@ -1456,9 +1575,10 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; observe?: 'body'; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; responseType?: 'json'; reportProgress?: boolean; @@ -1469,10 +1589,11 @@ export declare class HttpClient { headers?: HttpHeaders | { [header: string]: string | string[]; }; + context?: HttpContext; params?: HttpParams | { - [param: string]: string | string[]; + [param: string]: string | number | boolean | ReadonlyArray; }; - observe?: HttpObserve; + observe?: 'body' | 'events' | 'response'; reportProgress?: boolean; responseType?: 'arraybuffer' | 'blob' | 'json' | 'text'; withCredentials?: boolean; @@ -1493,6 +1614,18 @@ export declare class HttpClientXsrfModule { }): ModuleWithProviders; } +export declare class HttpContext { + delete(token: HttpContextToken): HttpContext; + get(token: HttpContextToken): T; + keys(): IterableIterator>; + set(token: HttpContextToken, value: T): HttpContext; +} + +export declare class HttpContextToken { + readonly defaultValue: () => T; + constructor(defaultValue: () => T); +} + export declare interface HttpDownloadProgressEvent extends HttpProgressEvent { partialText?: string; type: HttpEventType.DownloadProgress; @@ -1569,20 +1702,23 @@ export declare interface HttpParameterCodec { export declare class HttpParams { constructor(options?: HttpParamsOptions); - append(param: string, value: string): HttpParams; - delete(param: string, value?: string): HttpParams; + append(param: string, value: string | number | boolean): HttpParams; + appendAll(params: { + [param: string]: string | number | boolean | ReadonlyArray; + }): HttpParams; + delete(param: string, value?: string | number | boolean): HttpParams; get(param: string): string | null; getAll(param: string): string[] | null; has(param: string): boolean; keys(): string[]; - set(param: string, value: string): HttpParams; + set(param: string, value: string | number | boolean): HttpParams; toString(): string; } export declare interface HttpParamsOptions { encoder?: HttpParameterCodec; fromObject?: { - [param: string]: string | ReadonlyArray; + [param: string]: string | number | boolean | ReadonlyArray; }; fromString?: string; } @@ -1595,6 +1731,7 @@ export declare interface HttpProgressEvent { export declare class HttpRequest { readonly body: T | null; + readonly context: HttpContext; readonly headers: HttpHeaders; readonly method: string; readonly params: HttpParams; @@ -1605,6 +1742,7 @@ export declare class HttpRequest { readonly withCredentials: boolean; constructor(method: 'DELETE' | 'GET' | 'HEAD' | 'JSONP' | 'OPTIONS', url: string, init?: { headers?: HttpHeaders; + context?: HttpContext; reportProgress?: boolean; params?: HttpParams; responseType?: 'arraybuffer' | 'blob' | 'json' | 'text'; @@ -1612,6 +1750,7 @@ export declare class HttpRequest { }); constructor(method: 'POST' | 'PUT' | 'PATCH', url: string, body: T | null, init?: { headers?: HttpHeaders; + context?: HttpContext; reportProgress?: boolean; params?: HttpParams; responseType?: 'arraybuffer' | 'blob' | 'json' | 'text'; @@ -1619,6 +1758,7 @@ export declare class HttpRequest { }); constructor(method: string, url: string, body: T | null, init?: { headers?: HttpHeaders; + context?: HttpContext; reportProgress?: boolean; params?: HttpParams; responseType?: 'arraybuffer' | 'blob' | 'json' | 'text'; @@ -1627,6 +1767,7 @@ export declare class HttpRequest { clone(): HttpRequest; clone(update: { headers?: HttpHeaders; + context?: HttpContext; reportProgress?: boolean; params?: HttpParams; responseType?: 'arraybuffer' | 'blob' | 'json' | 'text'; @@ -1643,6 +1784,7 @@ export declare class HttpRequest { }): HttpRequest; clone(update: { headers?: HttpHeaders; + context?: HttpContext; reportProgress?: boolean; params?: HttpParams; responseType?: 'arraybuffer' | 'blob' | 'json' | 'text'; @@ -1706,6 +1848,72 @@ export declare interface HttpSentEvent { type: HttpEventType.Sent; } +export declare const enum HttpStatusCode { + Continue = 100, + SwitchingProtocols = 101, + Processing = 102, + EarlyHints = 103, + Ok = 200, + Created = 201, + Accepted = 202, + NonAuthoritativeInformation = 203, + NoContent = 204, + ResetContent = 205, + PartialContent = 206, + MultiStatus = 207, + AlreadyReported = 208, + ImUsed = 226, + MultipleChoices = 300, + MovedPermanently = 301, + Found = 302, + SeeOther = 303, + NotModified = 304, + UseProxy = 305, + Unused = 306, + TemporaryRedirect = 307, + PermanentRedirect = 308, + BadRequest = 400, + Unauthorized = 401, + PaymentRequired = 402, + Forbidden = 403, + NotFound = 404, + MethodNotAllowed = 405, + NotAcceptable = 406, + ProxyAuthenticationRequired = 407, + RequestTimeout = 408, + Conflict = 409, + Gone = 410, + LengthRequired = 411, + PreconditionFailed = 412, + PayloadTooLarge = 413, + UriTooLong = 414, + UnsupportedMediaType = 415, + RangeNotSatisfiable = 416, + ExpectationFailed = 417, + ImATeapot = 418, + MisdirectedRequest = 421, + UnprocessableEntity = 422, + Locked = 423, + FailedDependency = 424, + TooEarly = 425, + UpgradeRequired = 426, + PreconditionRequired = 428, + TooManyRequests = 429, + RequestHeaderFieldsTooLarge = 431, + UnavailableForLegalReasons = 451, + InternalServerError = 500, + NotImplemented = 501, + BadGateway = 502, + ServiceUnavailable = 503, + GatewayTimeout = 504, + HttpVersionNotSupported = 505, + VariantAlsoNegotiates = 506, + InsufficientStorage = 507, + LoopDetected = 508, + NotExtended = 510, + NetworkAuthenticationRequired = 511 +} + export declare interface HttpUploadProgressEvent extends HttpProgressEvent { type: HttpEventType.UploadProgress; } @@ -1722,7 +1930,7 @@ export declare interface HttpUserEvent { } export declare class HttpXhrBackend implements HttpBackend { - constructor(xhrFactory: XhrFactory); + constructor(xhrFactory: XhrFactory_2); handle(req: HttpRequest): Observable>; } @@ -1740,6 +1948,5 @@ export declare class JsonpInterceptor { intercept(req: HttpRequest, next: HttpHandler): Observable>; } -export declare abstract class XhrFactory { - abstract build(): XMLHttpRequest; -} +/** @deprecated */ +export declare type XhrFactory = XhrFactory_2; diff --git a/goldens/public-api/common/testing/testing.d.ts b/goldens/public-api/common/testing/testing.d.ts index cda45d1a1e..b4b4b8fca8 100644 --- a/goldens/public-api/common/testing/testing.d.ts +++ b/goldens/public-api/common/testing/testing.d.ts @@ -33,8 +33,8 @@ export declare class MockPlatformLocation implements PlatformLocation { forward(): void; getBaseHrefFromDOM(): string; getState(): unknown; - onHashChange(fn: LocationChangeListener): void; - onPopState(fn: LocationChangeListener): void; + onHashChange(fn: LocationChangeListener): VoidFunction; + onPopState(fn: LocationChangeListener): VoidFunction; pushState(state: any, title: string, newUrl: string): void; replaceState(state: any, title: string, newUrl: string): void; } diff --git a/goldens/public-api/compiler-cli/error_code.d.ts b/goldens/public-api/compiler-cli/error_code.d.ts index b9ed59e469..9116012fe4 100644 --- a/goldens/public-api/compiler-cli/error_code.d.ts +++ b/goldens/public-api/compiler-cli/error_code.d.ts @@ -14,8 +14,10 @@ export declare enum ErrorCode { UNDECORATED_PROVIDER = 2005, DIRECTIVE_INHERITS_UNDECORATED_CTOR = 2006, UNDECORATED_CLASS_USING_ANGULAR_FEATURES = 2007, + COMPONENT_RESOURCE_NOT_FOUND = 2008, SYMBOL_NOT_EXPORTED = 3001, SYMBOL_EXPORTED_UNDER_DIFFERENT_NAME = 3002, + IMPORT_CYCLE_DETECTED = 3003, CONFIG_FLAT_MODULE_NO_INDEX = 4001, CONFIG_STRICT_TEMPLATES_IMPLIES_FULL_TEMPLATE_TYPECHECK = 4002, HOST_BINDING_PARSE_ERROR = 5001, @@ -35,5 +37,7 @@ export declare enum ErrorCode { DUPLICATE_VARIABLE_DECLARATION = 8006, INLINE_TCB_REQUIRED = 8900, INLINE_TYPE_CTOR_REQUIRED = 8901, - INJECTABLE_DUPLICATE_PROV = 9001 + INJECTABLE_DUPLICATE_PROV = 9001, + SUGGEST_STRICT_TEMPLATES = 10001, + SUGGEST_SUBOPTIMAL_TYPE_INFERENCE = 10002 } diff --git a/goldens/public-api/core/core.d.ts b/goldens/public-api/core/core.d.ts index ad6dd6a01e..e5ed21e911 100644 --- a/goldens/public-api/core/core.d.ts +++ b/goldens/public-api/core/core.d.ts @@ -25,12 +25,12 @@ export declare const APP_BOOTSTRAP_LISTENER: InjectionToken<((compRef: Component export declare const APP_ID: InjectionToken; -export declare const APP_INITIALIZER: InjectionToken<(() => void)[]>; +export declare const APP_INITIALIZER: InjectionToken Observable | Promise | void)[]>; export declare class ApplicationInitStatus { readonly done = false; readonly donePromise: Promise; - constructor(appInits: (() => any)[]); + constructor(appInits: ReadonlyArray<() => Observable | Promise | void>); } export declare class ApplicationModule { @@ -174,10 +174,12 @@ export declare type ContentChildren = Query; export declare interface ContentChildrenDecorator { (selector: Type | InjectionToken | Function | string, opts?: { descendants?: boolean; + emitDistinctChangesOnly?: boolean; read?: any; }): any; new (selector: Type | InjectionToken | Function | string, opts?: { descendants?: boolean; + emitDistinctChangesOnly?: boolean; read?: any; }): Query; } @@ -299,7 +301,7 @@ export declare class ElementRef { } export declare abstract class EmbeddedViewRef extends ViewRef { - abstract get context(): C; + abstract context: C; abstract get rootNodes(): any[]; } @@ -312,7 +314,8 @@ export declare class ErrorHandler { export declare interface EventEmitter extends Subject { new (isAsync?: boolean): EventEmitter; emit(value?: T): void; - subscribe(generatorOrNext?: any, error?: any, complete?: any): Subscription; + subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): Subscription; + subscribe(observerOrNext?: any, error?: any, complete?: any): Subscription; } export declare const EventEmitter: { @@ -418,7 +421,7 @@ export declare interface InjectableDecorator { export declare type InjectableProvider = ValueSansProvider | ExistingSansProvider | StaticClassSansProvider | ConstructorSansProvider | FactorySansProvider | ClassSansProvider; export declare interface InjectableType extends Type { - ɵprov: never; + ɵprov: unknown; } export declare interface InjectDecorator { @@ -436,7 +439,7 @@ export declare enum InjectFlags { export declare class InjectionToken { protected _desc: string; - readonly ɵprov: never | undefined; + readonly ɵprov: unknown; constructor(_desc: string, options?: { providedIn?: Type | 'root' | 'platform' | 'any' | null; factory: () => T; @@ -449,7 +452,7 @@ export declare abstract class Injector { /** @deprecated */ abstract get(token: any, notFoundValue?: any): any; static NULL: Injector; static THROW_IF_NOT_FOUND: {}; - static ɵprov: never; + static ɵprov: unknown; /** @deprecated */ static create(providers: StaticProvider[], parent?: Injector): Injector; static create(options: { providers: StaticProvider[]; @@ -461,7 +464,8 @@ export declare abstract class Injector { export declare const INJECTOR: InjectionToken; export declare interface InjectorType extends Type { - ɵinj: never; + ɵfac?: unknown; + ɵinj: unknown; } export declare interface Input { @@ -507,7 +511,7 @@ export declare class IterableDiffers { /** @deprecated */ factories: IterableDifferFactory[]; constructor(factories: IterableDifferFactory[]); find(iterable: any): IterableDifferFactory; - static ɵprov: never; + static ɵprov: unknown; static create(factories: IterableDifferFactory[], parent?: IterableDiffers): IterableDiffers; static extend(factories: IterableDifferFactory[]): StaticProvider; } @@ -542,7 +546,7 @@ export declare class KeyValueDiffers { /** @deprecated */ factories: KeyValueDifferFactory[]; constructor(factories: KeyValueDifferFactory[]); find(kv: any): KeyValueDifferFactory; - static ɵprov: never; + static ɵprov: unknown; static create(factories: KeyValueDifferFactory[], parent?: KeyValueDiffers): KeyValueDiffers; static extend(factories: KeyValueDifferFactory[]): StaticProvider; } @@ -673,7 +677,7 @@ export declare function ɵɵdefineInjectable(opts: { token: unknown; providedIn?: Type | 'root' | 'platform' | 'any' | null; factory: () => T; -}): never; +}): unknown; /** @codeGenApi */ export declare function ɵɵinject(token: Type | AbstractType | InjectionToken): T; @@ -690,9 +694,6 @@ export declare interface ɵɵInjectableDef { /** @codeGenApi */ export declare function ɵɵinjectAttribute(attrNameToInject: string): string | null; -/** @codeGenApi */ -export declare function ɵɵinjectPipeChangeDetectorRef(flags?: InjectFlags): ChangeDetectorRef | null; - export declare const PACKAGE_ROOT_URL: InjectionToken; export declare interface Pipe { @@ -734,6 +735,7 @@ export declare type Provider = TypeProvider | ValueProvider | ClassProvider | Co export declare interface Query { descendants: boolean; + emitDistinctChangesOnly: boolean; first: boolean; isViewQuery: boolean; read: any; @@ -746,12 +748,12 @@ export declare abstract class Query { export declare class QueryList implements Iterable { [Symbol.iterator]: () => Iterator; - readonly changes: Observable; + get changes(): Observable; readonly dirty = true; readonly first: T; readonly last: T; readonly length: number; - constructor(); + constructor(_emitDistinctChangesOnly?: boolean); destroy(): void; filter(fn: (item: T, index: number, array: T[]) => boolean): T[]; find(fn: (item: T, index: number, array: T[]) => boolean): T | undefined; @@ -760,7 +762,7 @@ export declare class QueryList implements Iterable { map(fn: (item: T, index: number, array: T[]) => U): U[]; notifyOnChanges(): void; reduce(fn: (prevValue: U, curValue: T, curIndex: number, array: T[]) => U, init: U): U; - reset(resultsTree: Array): void; + reset(resultsTree: Array, identityAccessor?: (value: T) => unknown): void; setDirty(): void; some(fn: (value: T, index: number, array: T[]) => boolean): boolean; toArray(): T[]; @@ -838,11 +840,11 @@ export declare interface RendererType2 { } export declare class ResolvedReflectiveFactory { - dependencies: ɵangular_packages_core_core_d[]; + dependencies: ɵangular_packages_core_core_e[]; factory: Function; constructor( factory: Function, - dependencies: ɵangular_packages_core_core_d[]); + dependencies: ɵangular_packages_core_core_e[]); } export declare interface ResolvedReflectiveProvider { @@ -855,7 +857,7 @@ export declare function resolveForwardRef(type: T): T; export declare abstract class Sanitizer { abstract sanitize(context: SecurityContext, value: {} | string | null): string | null; - static ɵprov: never; + static ɵprov: unknown; } export declare interface SchemaMetadata { @@ -1010,9 +1012,11 @@ export declare type ViewChildren = Query; export declare interface ViewChildrenDecorator { (selector: Type | InjectionToken | Function | string, opts?: { read?: any; + emitDistinctChangesOnly?: boolean; }): any; new (selector: Type | InjectionToken | Function | string, opts?: { read?: any; + emitDistinctChangesOnly?: boolean; }): ViewChildren; } diff --git a/goldens/public-api/forms/forms.d.ts b/goldens/public-api/forms/forms.d.ts index 8c18a985be..da3f7bc85d 100644 --- a/goldens/public-api/forms/forms.d.ts +++ b/goldens/public-api/forms/forms.d.ts @@ -112,7 +112,7 @@ export declare interface AsyncValidatorFn { (control: AbstractControl): Promise | Observable; } -export declare class CheckboxControlValueAccessor implements ControlValueAccessor { +export declare class CheckboxControlValueAccessor extends ɵangular_packages_forms_forms_f implements ControlValueAccessor { onChange: (_: any) => void; onTouched: () => void; constructor(_renderer: Renderer2, _elementRef: ElementRef); @@ -172,20 +172,30 @@ export declare class FormArray extends AbstractControl { get length(): number; constructor(controls: AbstractControl[], validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null); at(index: number): AbstractControl; - clear(): void; + clear(options?: { + emitEvent?: boolean; + }): void; getRawValue(): any[]; - insert(index: number, control: AbstractControl): void; + insert(index: number, control: AbstractControl, options?: { + emitEvent?: boolean; + }): void; patchValue(value: any[], options?: { onlySelf?: boolean; emitEvent?: boolean; }): void; - push(control: AbstractControl): void; - removeAt(index: number): void; + push(control: AbstractControl, options?: { + emitEvent?: boolean; + }): void; + removeAt(index: number, options?: { + emitEvent?: boolean; + }): void; reset(value?: any, options?: { onlySelf?: boolean; emitEvent?: boolean; }): void; - setControl(index: number, control: AbstractControl): void; + setControl(index: number, control: AbstractControl, options?: { + emitEvent?: boolean; + }): void; setValue(value: any[], options?: { onlySelf?: boolean; emitEvent?: boolean; @@ -237,7 +247,7 @@ export declare class FormControl extends AbstractControl { }): void; } -export declare class FormControlDirective extends NgControl implements OnChanges { +export declare class FormControlDirective extends NgControl implements OnChanges, OnDestroy { get control(): FormControl; form: FormControl; set isDisabled(isDisabled: boolean); @@ -247,6 +257,7 @@ export declare class FormControlDirective extends NgControl implements OnChanges viewModel: any; constructor(validators: (Validator | ValidatorFn)[], asyncValidators: (AsyncValidator | AsyncValidatorFn)[], valueAccessors: ControlValueAccessor[], _ngModelWarningConfig: string | null); ngOnChanges(changes: SimpleChanges): void; + ngOnDestroy(): void; viewToModelUpdate(newValue: any): void; } @@ -271,7 +282,9 @@ export declare class FormGroup extends AbstractControl { constructor(controls: { [key: string]: AbstractControl; }, validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null); - addControl(name: string, control: AbstractControl): void; + addControl(name: string, control: AbstractControl, options?: { + emitEvent?: boolean; + }): void; contains(controlName: string): boolean; getRawValue(): any; patchValue(value: { @@ -281,12 +294,16 @@ export declare class FormGroup extends AbstractControl { emitEvent?: boolean; }): void; registerControl(name: string, control: AbstractControl): AbstractControl; - removeControl(name: string): void; + removeControl(name: string, options?: { + emitEvent?: boolean; + }): void; reset(value?: any, options?: { onlySelf?: boolean; emitEvent?: boolean; }): void; - setControl(name: string, control: AbstractControl): void; + setControl(name: string, control: AbstractControl, options?: { + emitEvent?: boolean; + }): void; setValue(value: { [key: string]: any; }, options?: { @@ -295,7 +312,7 @@ export declare class FormGroup extends AbstractControl { }): void; } -export declare class FormGroupDirective extends ControlContainer implements Form, OnChanges { +export declare class FormGroupDirective extends ControlContainer implements Form, OnChanges, OnDestroy { get control(): FormGroup; directives: FormControlName[]; form: FormGroup; @@ -311,6 +328,7 @@ export declare class FormGroupDirective extends ControlContainer implements Form getFormArray(dir: FormArrayName): FormArray; getFormGroup(dir: FormGroupName): FormGroup; ngOnChanges(changes: SimpleChanges): void; + ngOnDestroy(): void; onReset(): void; onSubmit($event: Event): boolean; removeControl(dir: FormControlName): void; @@ -335,6 +353,11 @@ export declare class MaxLengthValidator implements Validator, OnChanges { validate(control: AbstractControl): ValidationErrors | null; } +export declare class MaxValidator extends AbstractValidatorDirective implements OnChanges { + max: string | number; + ngOnChanges(changes: SimpleChanges): void; +} + export declare class MinLengthValidator implements Validator, OnChanges { minlength: string | number; ngOnChanges(changes: SimpleChanges): void; @@ -342,6 +365,11 @@ export declare class MinLengthValidator implements Validator, OnChanges { validate(control: AbstractControl): ValidationErrors | null; } +export declare class MinValidator extends AbstractValidatorDirective implements OnChanges { + min: string | number; + ngOnChanges(changes: SimpleChanges): void; +} + export declare const NG_ASYNC_VALIDATORS: InjectionToken<(Function | Validator)[]>; export declare const NG_VALIDATORS: InjectionToken<(Function | Validator)[]>; @@ -354,11 +382,11 @@ export declare abstract class NgControl extends AbstractControlDirective { abstract viewToModelUpdate(newValue: any): void; } -export declare class NgControlStatus extends ɵangular_packages_forms_forms_g { +export declare class NgControlStatus extends ɵangular_packages_forms_forms_h { constructor(cd: NgControl); } -export declare class NgControlStatusGroup extends ɵangular_packages_forms_forms_g { +export declare class NgControlStatusGroup extends ɵangular_packages_forms_forms_h { constructor(cd: ControlContainer); } @@ -426,7 +454,7 @@ export declare class NgSelectOption implements OnDestroy { ngOnDestroy(): void; } -export declare class NumberValueAccessor implements ControlValueAccessor { +export declare class NumberValueAccessor extends ɵangular_packages_forms_forms_f implements ControlValueAccessor { onChange: (_: any) => void; onTouched: () => void; constructor(_renderer: Renderer2, _elementRef: ElementRef); @@ -443,13 +471,13 @@ export declare class PatternValidator implements Validator, OnChanges { validate(control: AbstractControl): ValidationErrors | null; } -export declare class RadioControlValueAccessor implements ControlValueAccessor, OnDestroy, OnInit { +export declare class RadioControlValueAccessor extends ɵangular_packages_forms_forms_f implements ControlValueAccessor, OnDestroy, OnInit { formControlName: string; name: string; onChange: () => void; onTouched: () => void; value: any; - constructor(_renderer: Renderer2, _elementRef: ElementRef, _registry: ɵangular_packages_forms_forms_n, _injector: Injector); + constructor(_renderer: Renderer2, _elementRef: ElementRef, _registry: ɵangular_packages_forms_forms_p, _injector: Injector); fireUncheck(value: any): void; ngOnDestroy(): void; ngOnInit(): void; @@ -459,7 +487,7 @@ export declare class RadioControlValueAccessor implements ControlValueAccessor, writeValue(value: any): void; } -export declare class RangeValueAccessor implements ControlValueAccessor { +export declare class RangeValueAccessor extends ɵangular_packages_forms_forms_f implements ControlValueAccessor { onChange: (_: any) => void; onTouched: () => void; constructor(_renderer: Renderer2, _elementRef: ElementRef); @@ -481,7 +509,7 @@ export declare class RequiredValidator implements Validator { validate(control: AbstractControl): ValidationErrors | null; } -export declare class SelectControlValueAccessor implements ControlValueAccessor { +export declare class SelectControlValueAccessor extends ɵangular_packages_forms_forms_f implements ControlValueAccessor { set compareWith(fn: (o1: any, o2: any) => boolean); onChange: (_: any) => void; onTouched: () => void; @@ -493,7 +521,7 @@ export declare class SelectControlValueAccessor implements ControlValueAccessor writeValue(value: any): void; } -export declare class SelectMultipleControlValueAccessor implements ControlValueAccessor { +export declare class SelectMultipleControlValueAccessor extends ɵangular_packages_forms_forms_f implements ControlValueAccessor { set compareWith(fn: (o1: any, o2: any) => boolean); onChange: (_: any) => void; onTouched: () => void; diff --git a/goldens/public-api/platform-browser/animations/animations.d.ts b/goldens/public-api/platform-browser/animations/animations.d.ts index 80b165214e..e9e7450991 100644 --- a/goldens/public-api/platform-browser/animations/animations.d.ts +++ b/goldens/public-api/platform-browser/animations/animations.d.ts @@ -1,6 +1,11 @@ export declare const ANIMATION_MODULE_TYPE: InjectionToken<"NoopAnimations" | "BrowserAnimations">; export declare class BrowserAnimationsModule { + static withConfig(config: BrowserAnimationsModuleConfig): ModuleWithProviders; +} + +export declare interface BrowserAnimationsModuleConfig { + disableAnimations?: boolean; } export declare class NoopAnimationsModule { diff --git a/goldens/public-api/router/router.d.ts b/goldens/public-api/router/router.d.ts index 73cb56deb0..a8bd2ceadf 100644 --- a/goldens/public-api/router/router.d.ts +++ b/goldens/public-api/router/router.d.ts @@ -3,7 +3,7 @@ export declare class ActivatedRoute { component: Type | string | null; data: Observable; get firstChild(): ActivatedRoute | null; - fragment: Observable; + fragment: Observable; outlet: string; get paramMap(): Observable; params: Observable; @@ -23,7 +23,7 @@ export declare class ActivatedRouteSnapshot { component: Type | string | null; data: Data; get firstChild(): ActivatedRouteSnapshot | null; - fragment: string; + fragment: string | null; outlet: string; get paramMap(): ParamMap; params: Params; @@ -92,7 +92,7 @@ export declare class ChildActivationStart { export declare class ChildrenOutletContexts { getContext(childName: string): OutletContext | null; getOrCreateContext(childName: string): OutletContext; - onChildOutletCreated(childName: string, outlet: RouterOutlet): void; + onChildOutletCreated(childName: string, outlet: RouterOutletContract): void; onChildOutletDestroyed(childName: string): void; onOutletDeactivated(): Map; onOutletReAttached(contexts: Map): void; @@ -158,6 +158,13 @@ export declare class GuardsCheckStart extends RouterEvent { export declare type InitialNavigation = 'disabled' | 'enabled' | 'enabledBlocking' | 'enabledNonBlocking'; +export declare interface IsActiveMatchOptions { + fragment: 'exact' | 'ignored'; + matrixParams: 'exact' | 'subset' | 'ignored'; + paths: 'exact' | 'subset'; + queryParams: 'exact' | 'subset' | 'ignored'; +} + export declare type LoadChildren = LoadChildrenCallback | DeprecatedLoadChildren; export declare type LoadChildrenCallback = () => Type | NgModuleFactory | Observable> | Promise | Type | any>; @@ -234,7 +241,7 @@ export declare class NoPreloading implements PreloadingStrategy { export declare class OutletContext { attachRef: ComponentRef | null; children: ChildrenOutletContexts; - outlet: RouterOutlet | null; + outlet: RouterOutletContract | null; resolver: ComponentFactoryResolver | null; route: ActivatedRoute | null; } @@ -345,7 +352,8 @@ export declare class Router { dispose(): void; getCurrentNavigation(): Navigation | null; initialNavigation(): void; - isActive(url: string | UrlTree, exact: boolean): boolean; + /** @deprecated */ isActive(url: string | UrlTree, exact: boolean): boolean; + isActive(url: string | UrlTree, matchOptions: IsActiveMatchOptions): boolean; navigate(commands: any[], extras?: NavigationExtras): Promise; navigateByUrl(url: string | UrlTree, extras?: NavigationBehaviorOptions): Promise; ngOnDestroy(): void; @@ -380,6 +388,7 @@ export declare class RouterLink implements OnChanges { preserveFragment: boolean; queryParams?: Params | null; queryParamsHandling?: QueryParamsHandling | null; + relativeTo?: ActivatedRoute | null; replaceUrl: boolean; set routerLink(commands: any[] | string | null | undefined); skipLocationChange: boolean; @@ -399,7 +408,7 @@ export declare class RouterLinkActive implements OnChanges, OnDestroy, AfterCont set routerLinkActive(data: string[] | string); routerLinkActiveOptions: { exact: boolean; - }; + } | IsActiveMatchOptions; constructor(router: Router, element: ElementRef, renderer: Renderer2, cdr: ChangeDetectorRef, link?: RouterLink | undefined, linkWithHref?: RouterLinkWithHref | undefined); ngAfterContentInit(): void; ngOnChanges(changes: SimpleChanges): void; @@ -412,6 +421,7 @@ export declare class RouterLinkWithHref implements OnChanges, OnDestroy { preserveFragment: boolean; queryParams?: Params | null; queryParamsHandling?: QueryParamsHandling | null; + relativeTo?: ActivatedRoute | null; replaceUrl: boolean; set routerLink(commands: any[] | string | null | undefined); skipLocationChange: boolean; @@ -432,7 +442,7 @@ export declare class RouterModule { static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders; } -export declare class RouterOutlet implements OnDestroy, OnInit { +export declare class RouterOutlet implements OnDestroy, OnInit, RouterOutletContract { activateEvents: EventEmitter; get activatedRoute(): ActivatedRoute; get activatedRouteData(): Data; @@ -448,6 +458,17 @@ export declare class RouterOutlet implements OnDestroy, OnInit { ngOnInit(): void; } +export declare interface RouterOutletContract { + activatedRoute: ActivatedRoute | null; + activatedRouteData: Data; + component: Object | null; + isActivated: boolean; + activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver | null): void; + attach(ref: ComponentRef, activatedRoute: ActivatedRoute): void; + deactivate(): void; + detach(): ComponentRef; +} + export declare class RouterPreloader implements OnDestroy { constructor(router: Router, moduleLoader: NgModuleFactoryLoader, compiler: Compiler, injector: Injector, preloadingStrategy: PreloadingStrategy); ngOnDestroy(): void; diff --git a/goldens/public-api/upgrade/static/static.d.ts b/goldens/public-api/upgrade/static/static.d.ts index 47b7425ce9..d21bc57848 100644 --- a/goldens/public-api/upgrade/static/static.d.ts +++ b/goldens/public-api/upgrade/static/static.d.ts @@ -35,7 +35,8 @@ export declare class UpgradeModule { ngZone: NgZone; constructor( injector: Injector, - ngZone: NgZone); + ngZone: NgZone, + platformRef: PlatformRef); bootstrap(element: Element, modules?: string[], config?: any): void; } diff --git a/goldens/size-tracking/aio-payloads.json b/goldens/size-tracking/aio-payloads.json index c9869baede..b8d341e40e 100755 --- a/goldens/size-tracking/aio-payloads.json +++ b/goldens/size-tracking/aio-payloads.json @@ -3,8 +3,8 @@ "master": { "uncompressed": { "runtime-es2015": 3033, - "main-es2015": 447766, - "polyfills-es2015": 52343 + "main-es2015": 450953, + "polyfills-es2015": 52215 } } }, @@ -12,8 +12,8 @@ "master": { "uncompressed": { "runtime-es2015": 3033, - "main-es2015": 447975, - "polyfills-es2015": 52493 + "main-es2015": 451600, + "polyfills-es2015": 52215 } } }, @@ -21,7 +21,7 @@ "master": { "uncompressed": { "runtime-es2015": 3153, - "main-es2015": 431696, + "main-es2015": 437306, "polyfills-es2015": 52493 } } diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json index 347229d4b2..917d0775ba 100644 --- a/goldens/size-tracking/integration-payloads.json +++ b/goldens/size-tracking/integration-payloads.json @@ -3,7 +3,7 @@ "master": { "uncompressed": { "runtime-es2015": 1485, - "main-es2015": 141516, + "main-es2015": 138189, "polyfills-es2015": 36964 } } @@ -21,7 +21,7 @@ "master": { "uncompressed": { "runtime-es2015": 1485, - "main-es2015": 146680, + "main-es2015": 144579, "polyfills-es2015": 36964 } } @@ -30,7 +30,7 @@ "master": { "uncompressed": { "runtime-es2015": 1485, - "main-es2015": 136703, + "main-es2015": 136546, "polyfills-es2015": 37641 } } @@ -39,9 +39,9 @@ "master": { "uncompressed": { "runtime-es2015": 2285, - "main-es2015": 241879, - "polyfills-es2015": 36709, - "5-es2015": 745 + "main-es2015": 240883, + "polyfills-es2015": 36975, + "5-es2015": 753 } } }, @@ -49,12 +49,21 @@ "master": { "uncompressed": { "runtime-es2015": 2289, - "main-es2015": 218507, + "main-es2015": 216267, "polyfills-es2015": 36723, "5-es2015": 781 } } }, + "forms": { + "master": { + "uncompressed": { + "runtime-es2015": 1485, + "main-es2015": 162346, + "polyfills-es2015": 36975 + } + } + }, "hello_world__closure": { "master": { "uncompressed": { diff --git a/gulpfile.js b/gulpfile.js index 5853cf68b9..8c22aeb9f7 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -8,15 +8,6 @@ 'use strict'; -// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE -// This is to ensure that we catch env issues before we error while requiring other dependencies. -const engines = require('./package.json').engines; -require('./tools/check-environment')({ - requiredNodeVersion: engines.node, - requiredNpmVersion: engines.npm, - requiredYarnVersion: engines.yarn -}); - const gulp = require('gulp'); // See `tools/gulp-tasks/README.md` for information about task loading. @@ -30,7 +21,6 @@ function loadTask(fileName, taskName) { gulp.task('source-map-test', loadTask('source-map-test')); gulp.task('changelog', loadTask('changelog')); gulp.task('changelog:zonejs', loadTask('changelog-zonejs')); -gulp.task('check-env', () => {/* this is a noop because the env test ran already above */}); gulp.task('cldr:extract', loadTask('cldr', 'extract')); gulp.task('cldr:download', loadTask('cldr', 'download')); gulp.task('cldr:gen-closure-locale', loadTask('cldr', 'closure')); diff --git a/integration/BUILD.bazel b/integration/BUILD.bazel index 33d2e09d38..11e68f4040 100644 --- a/integration/BUILD.bazel +++ b/integration/BUILD.bazel @@ -4,7 +4,7 @@ load(":angular_integration_test.bzl", "angular_integration_test") # cases the tests are able to select a random free port. # # Where `ng e2e` is used we pass `ng e2e --port 0` which prompts the cli -# to select a random free port for the the e2e test. The protractor.conf is +# to select a random free port for the e2e test. The protractor.conf is # automatically updated to use this port. # # Karma automatically finds a free port so no effort is needed there. @@ -33,6 +33,7 @@ INTEGRATION_TESTS = { "no-ivy-aot", ], }, + "cli-elements-universal": {}, "cli-hello-world": {"commands": "payload_size_tracking"}, "cli-hello-world-ivy-compat": {"commands": "payload_size_tracking"}, "cli-hello-world-ivy-i18n": { @@ -52,6 +53,10 @@ INTEGRATION_TESTS = { "tags": ["no-ivy-aot"], }, "dynamic-compiler": {"tags": ["no-ivy-aot"]}, + "forms": { + "commands": "payload_size_tracking", + "tags": ["no-ivy-aot"], + }, "hello_world__closure": { # TODO: Re-enable the payload_size_tracking command: # We should define ngDevMode to false in Closure, but --define only works in the global scope. @@ -61,7 +66,7 @@ INTEGRATION_TESTS = { }, "hello_world__systemjs_umd": { # Special case for `hello_world__systemjs_umd` test as we want to pin - # `systems` at version 0.20.2 and not link to the the root @npm//systemjs + # `systems` at version 0.20.2 and not link to the root @npm//systemjs # which is stuck at 0.18.10 and can't be updated to 0.20.2 without # breaking the legacy saucelabs job. "pinned_npm_packages": ["systemjs"], @@ -80,18 +85,18 @@ INTEGRATION_TESTS = { "service-worker-schema": {}, "side-effects": {"tags": ["no-ivy-aot"]}, "terser": {}, - "typings_test_ts40": { - # Special case for `typings_test_ts40` test as we want to pin - # `typescript` at version 4.0.x for that test and not link to the - # root @npm//typescript package. - "pinned_npm_packages": ["typescript"], - }, "typings_test_ts41": { # Special case for `typings_test_ts41` test as we want to pin # `typescript` at version 4.1.x for that test and not link to the # root @npm//typescript package. "pinned_npm_packages": ["typescript"], }, + "typings_test_ts42": { + # Special case for `typings_test_ts42` test as we want to pin + # `typescript` at version 4.2.x for that test and not link to the + # root @npm//typescript package. + "pinned_npm_packages": ["typescript"], + }, } [ diff --git a/integration/bazel/.bazelrc b/integration/bazel/.bazelrc index 8f7faca4cf..a91cea38ef 100644 --- a/integration/bazel/.bazelrc +++ b/integration/bazel/.bazelrc @@ -22,7 +22,3 @@ build --define=enable_legacy_rollup_rule=1 # Don't create symlinks build --symlink_prefix=/ - -# Turn on managed directories feature in Bazel -# This allows us to avoid installing a second copy of node_modules -common --experimental_allow_incremental_repository_updates diff --git a/integration/bazel/.bazelversion b/integration/bazel/.bazelversion index 944880fa15..fcdb2e109f 100644 --- a/integration/bazel/.bazelversion +++ b/integration/bazel/.bazelversion @@ -1 +1 @@ -3.2.0 +4.0.0 diff --git a/integration/bazel/WORKSPACE b/integration/bazel/WORKSPACE index 92f347350a..66645e4701 100644 --- a/integration/bazel/WORKSPACE +++ b/integration/bazel/WORKSPACE @@ -5,18 +5,18 @@ 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 = "4952ef879704ab4ad6729a29007e7094aef213ea79e9f2e94cbe1c9a753e63ef", - urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/2.2.0/rules_nodejs-2.2.0.tar.gz"], + sha256 = "bfacf15161d96a6a39510e7b3d3b522cf61cb8b82a31e79400a84c5abcab5347", + urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/3.2.1/rules_nodejs-3.2.1.tar.gz"], ) # Fetch sass rules for compiling sass files http_archive( name = "io_bazel_rules_sass", - sha256 = "77e241148f26d5dbb98f96fe0029d8f221c6cb75edbb83e781e08ac7f5322c5f", - strip_prefix = "rules_sass-1.24.0", + sha256 = "596ab3616d370135e0ecc710e103422e0aa3719f1c970303a0886b70c81ee819", + strip_prefix = "rules_sass-1.32.2", urls = [ - "https://github.com/bazelbuild/rules_sass/archive/1.24.0.zip", - "https://mirror.bazel.build/github.com/bazelbuild/rules_sass/archive/1.24.0.zip", + "https://github.com/bazelbuild/rules_sass/archive/1.32.2.zip", + "https://mirror.bazel.build/github.com/bazelbuild/rules_sass/archive/1.32.2.zip", ], ) @@ -27,11 +27,6 @@ load("@build_bazel_rules_nodejs//:index.bzl", "node_repositories", "yarn_install node_repositories( # Use same node version as root angular WORKSPACE since # we share node_module packages and some require node >= 10.15.0 - node_repositories = { - "12.14.1-darwin_amd64": ("node-v12.14.1-darwin-x64.tar.gz", "node-v12.14.1-darwin-x64", "0be10a28737527a1e5e3784d3ad844d742fe8b0718acd701fd48f718fd3af78f"), - "12.14.1-linux_amd64": ("node-v12.14.1-linux-x64.tar.xz", "node-v12.14.1-linux-x64", "07cfcaa0aa9d0fcb6e99725408d9e0b07be03b844701588e3ab5dbc395b98e1b"), - "12.14.1-windows_amd64": ("node-v12.14.1-win-x64.zip", "node-v12.14.1-win-x64", "1f96ccce3ba045ecea3f458e189500adb90b8bc1a34de5d82fc10a5bf66ce7e3"), - }, node_version = "12.14.1", yarn_version = "1.12.1", ) @@ -39,6 +34,7 @@ node_repositories( # Install our npm dependencies into @npm yarn_install( name = "npm", + frozen_lockfile = False, package_json = "//:package.json", # Turn off symlink_node_modules here as it causes flakiness with missing # files in node_modules. @@ -53,11 +49,6 @@ load("@npm//@bazel/protractor:package.bzl", "npm_bazel_protractor_dependencies") npm_bazel_protractor_dependencies() -# Load karma dependencies -load("@npm//@bazel/karma:package.bzl", "npm_bazel_karma_dependencies") - -npm_bazel_karma_dependencies() - # Setup the rules_webtesting toolchain load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories") @@ -71,6 +62,6 @@ browser_repositories( ) # Setup the rules_sass toolchain -load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories") +load("@io_bazel_rules_sass//:defs.bzl", "sass_repositories") sass_repositories() diff --git a/integration/bazel/package.json b/integration/bazel/package.json index c7347b3814..b034d0eb6e 100644 --- a/integration/bazel/package.json +++ b/integration/bazel/package.json @@ -23,12 +23,14 @@ "@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": "2.2.0", - "@bazel/protractor": "2.2.0", - "@bazel/rollup": "2.2.0", - "@bazel/terser": "2.2.0", - "@bazel/typescript": "2.2.0", + "@bazel/protractor": "3.2.1", + "@bazel/rollup": "3.2.1", + "@bazel/concatjs": "3.2.1", + "@bazel/terser": "3.2.1", + "@bazel/typescript": "3.2.1", "@types/jasmine": "2.8.8", + "@types/node": "^12.11.1", + "jasmine": "^3.5.0", "http-server": "0.12.0", "karma": "4.4.1", "karma-chrome-launcher": "3.1.0", @@ -36,7 +38,7 @@ "karma-jasmine": "2.0.1", "karma-requirejs": "1.1.0", "karma-sourcemap-loader": "0.3.7", - "protractor": "5.4.2", + "protractor": "file:../../node_modules/protractor", "requirejs": "2.3.6", "rollup": "1.27.5", "rollup-plugin-commonjs": "10.1.0", @@ -45,6 +47,10 @@ "terser": "4.4.0", "typescript": "file:../../node_modules/typescript" }, + "//resolutions-comment": "Ensure a single version of webdriver-manager which comes from root node_modules that has already run webdriver-manager update", + "resolutions": { + "**/webdriver-manager": "file:../../node_modules/webdriver-manager" + }, "scripts": { "test": "bazelisk build ... --noshow_progress && bazelisk test ...", "postinstall": "ngcc" diff --git a/integration/bazel/src/BUILD.bazel b/integration/bazel/src/BUILD.bazel index a61abdb73f..3bc5bc985c 100644 --- a/integration/bazel/src/BUILD.bazel +++ b/integration/bazel/src/BUILD.bazel @@ -5,7 +5,7 @@ load("@npm//http-server:index.bzl", "http_server") load("@npm//@angular/bazel:index.bzl", "ng_module") load("@npm//@bazel/rollup:index.bzl", "rollup_bundle") load("@npm//@bazel/terser:index.bzl", "terser_minified") -load("@npm//@bazel/typescript:index.bzl", "ts_devserver") +load("@npm//@bazel/concatjs:index.bzl", "concatjs_devserver") # Allow targets under sub-packages to reference the tsconfig.json file exports_files(["tsconfig.json"]) @@ -35,7 +35,7 @@ filegroup( ], ) -ts_devserver( +concatjs_devserver( name = "devserver", entry_module = "bazel_integration_test/src/main", scripts = [ diff --git a/integration/bazel/src/hello-world/BUILD.bazel b/integration/bazel/src/hello-world/BUILD.bazel index 615aab8117..b8f506142f 100644 --- a/integration/bazel/src/hello-world/BUILD.bazel +++ b/integration/bazel/src/hello-world/BUILD.bazel @@ -1,8 +1,7 @@ package(default_visibility = ["//visibility:public"]) -load("@npm//@bazel/karma:index.bzl", "karma_web_test_suite") -load("@npm//@bazel/typescript:index.bzl", "ts_library") -load("@io_bazel_rules_sass//sass:sass.bzl", "sass_binary") +load("@npm//@bazel/concatjs:index.bzl", "karma_web_test_suite") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") load("@npm//@angular/bazel:index.bzl", "ng_package") load("//tools:ng_ts_library.bzl", "ng_ts_library") @@ -31,7 +30,7 @@ ng_package( deps = [":hello-world"], ) -ts_library( +ng_ts_library( name = "test_lib", testonly = 1, srcs = glob(["*.spec.ts"]), diff --git a/integration/bazel/yarn.lock b/integration/bazel/yarn.lock index 9cf0c6683f..665a104b72 100644 --- a/integration/bazel/yarn.lock +++ b/integration/bazel/yarn.lock @@ -3,12 +3,10 @@ "@angular/animations@file:../../dist/packages-dist/animations": - version "11.0.0-next.4" - dependencies: - tslib "^2.0.0" + version "11.1.0-next.4" "@angular/bazel@file:../../dist/packages-dist/bazel": - version "11.0.0-next.4" + version "11.1.0-next.4" dependencies: "@microsoft/api-extractor" "^7.7.13" shelljs "0.8.2" @@ -24,12 +22,12 @@ parse5 "^5.0.0" "@angular/common@file:../../dist/packages-dist/common": - version "11.0.0-next.4" + version "11.1.0-next.4" dependencies: tslib "^2.0.0" "@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli": - version "11.0.0-next.4" + version "11.1.0-next.4" dependencies: "@babel/core" "^7.8.6" "@babel/types" "^7.8.6" @@ -45,20 +43,20 @@ source-map "^0.6.1" sourcemap-codec "^1.4.8" tslib "^2.0.0" - yargs "15.3.0" + yargs "^16.1.1" "@angular/compiler@file:../../dist/packages-dist/compiler": - version "11.0.0-next.4" + version "11.1.0-next.4" dependencies: tslib "^2.0.0" "@angular/core@file:../../dist/packages-dist/core": - version "11.0.0-next.4" + version "11.1.0-next.4" dependencies: tslib "^2.0.0" "@angular/forms@file:../../dist/packages-dist/forms": - version "11.0.0-next.4" + version "11.1.0-next.4" dependencies: tslib "^2.0.0" @@ -70,17 +68,17 @@ tslib "^1.7.1" "@angular/platform-browser-dynamic@file:../../dist/packages-dist/platform-browser-dynamic": - version "11.0.0-next.4" + version "11.1.0-next.4" dependencies: tslib "^2.0.0" "@angular/platform-browser@file:../../dist/packages-dist/platform-browser": - version "11.0.0-next.4" + version "11.1.0-next.4" dependencies: tslib "^2.0.0" "@angular/router@file:../../dist/packages-dist/router": - version "11.0.0-next.4" + version "11.1.0-next.4" dependencies: tslib "^2.0.0" @@ -259,34 +257,36 @@ to-fast-properties "^2.0.0" "@bazel/bazelisk@file:../../node_modules/@bazel/bazelisk": - version "1.4.0" + version "1.7.3" -"@bazel/karma@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-2.2.0.tgz#9bf6f6f1aa5f12b25468b1cad5e4404138436600" - integrity sha512-qyVE7vZ/qaibmpmcRdjS0rlorLGR0zZtlUSImVVTcPTSXqt364fp8TWBWe7oOneJ1SOVyUmTOAzyE86ArxZ/AA== +"@bazel/concatjs@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@bazel/concatjs/-/concatjs-3.0.0.tgz#8a8b0c90ffcea4ed74a7c8db8fd37af861df394e" + integrity sha512-JQTKFxWY6KQwfwqQRJCJtg8sQBN0ydTvV/5umTIC9wUz+1RdVRzGVtypYuY+V8wbTWB7Pt3cr+6eq9tPjPSFWQ== dependencies: - tmp "0.1.0" + protobufjs "6.8.8" + source-map-support "0.5.9" + tsutils "2.27.2" -"@bazel/protractor@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-2.2.0.tgz#721b06507b84bf6d61d0e843e0af9939c6ad35a4" - integrity sha512-7dvAXxf/rGoi+S4+DWg8HNx3Co9s0kMg1xnBJ4T4nPF5wqSEoItkR6wsjkjQyocA2kIXan+HQcQvw4iIDMhGmg== +"@bazel/protractor@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-3.0.0.tgz#f1bc7daee3a6a3a2a7add091c45687f72863aee8" + integrity sha512-S1TS5llnhO9kf/81he2WD3dv+NoWbNnW5LQHZPPIcUO4Pv81XiF8YZ3AahY3wcpeR+r7wKnt2XO4qrOZt/gL9g== -"@bazel/rollup@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-2.2.0.tgz#15651d545114e08db056f10a1eeaa4e76fc4df56" - integrity sha512-N4SyrvFkdAVc24CqFNhDtrR6P3XJTdPGziCuF7QM/BGihnsGlxF6+Dt2n5BTLJnObiB1St8vtRwCtAY8faxYWQ== +"@bazel/rollup@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-3.0.0.tgz#6e424966d5ec41f6fcdbfbe25ec88d714f081b06" + integrity sha512-IEq+zbbzWC1hRsdCD/9UocznDJ5aNXlg+XcChM3+VJaloBCKoqAiGf337T6AkPZs3HuAlEHYMvqsRyEHxEQmtg== -"@bazel/terser@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-2.2.0.tgz#e0452f3d20e41d5e63048b3eea3280256172fd30" - integrity sha512-vukKS9ayJsW/eFFX6tG2Blem+NmEWbcYuCXxwgjL/uYiMCL/uowaTpcvVp9B+DsNSXhGyohOYwud0nBreSUFzg== +"@bazel/terser@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-3.0.0.tgz#20f3d278003e3263f8bf12334336845030d4b433" + integrity sha512-fODzVM2je+qOQTPzPf9wV/ifT31C3FAQJ3arFzl3+u+w992L+G6gdAE3ktJp/a5zfdTeLZfS9EW0xiD9fXi3uw== -"@bazel/typescript@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-2.2.0.tgz#f2d3dce8715d574fe3146f19fdb8479abcc4d608" - integrity sha512-Thf8pXntBzE3EvJtyiTBNsfIf1QnYmGPQmUSGLcKUuuFoplUVYShMRHaxBoPZmYsnD/x+BFLgUKIzlXiEQpGqQ== +"@bazel/typescript@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-3.0.0.tgz#7cb3bcf405c590228888be78b9e49d1dc298dfea" + integrity sha512-qYsfyi/+7QOFP9uVAv3gKaqkxo+fIamFrdQ71K85FlJSowxAkwj51pxOPnIWBcMzFVNv1p2ZyfM3ZctKqGye2g== dependencies: protobufjs "6.8.8" semver "5.6.0" @@ -742,11 +742,6 @@ callsite@1.0.0: resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA= -camelcase@^5.0.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - canonical-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-1.0.0.tgz#fcb470c23958def85081856be7a86e904f180d1d" @@ -792,14 +787,14 @@ chokidar@^3.0.0: optionalDependencies: fsevents "~2.1.1" -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" + wrap-ansi "^7.0.0" color-convert@^1.9.0: version "1.9.3" @@ -963,11 +958,6 @@ debug@~3.1.0: dependencies: ms "2.0.0" -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -1138,6 +1128,11 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -1208,14 +1203,6 @@ finalhandler@1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - flatted@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" @@ -1280,7 +1267,7 @@ gensync@^1.0.0-beta.1: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== -get-caller-file@^2.0.1: +get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -1783,13 +1770,6 @@ lie@~3.3.0: dependencies: immediate "~3.0.5" -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - lodash.get@^4.0.0: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -1987,25 +1967,6 @@ os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - pako@~1.0.2: version "1.0.10" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" @@ -2035,11 +1996,6 @@ parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -2115,10 +2071,8 @@ protobufjs@6.8.8: "@types/node" "^10.1.0" long "^4.0.0" -protractor@5.4.2: - version "5.4.2" - resolved "https://registry.yarnpkg.com/protractor/-/protractor-5.4.2.tgz#329efe37f48b2141ab9467799be2d4d12eb48c13" - integrity sha512-zlIj64Cr6IOWP7RwxVeD8O4UskLYPoyIcg0HboWJL9T79F1F0VWtKkGTr/9GN6BKL+/Q/GmM7C9kFVCfDbP5sA== +"protractor@file:../../node_modules/protractor": + version "5.4.3" dependencies: "@types/q" "^0.0.32" "@types/selenium-webdriver" "^3.0.0" @@ -2269,11 +2223,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - requirejs@2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.6.tgz#e5093d9601c2829251258c0b9445d4d19fa9e7c9" @@ -2308,7 +2257,7 @@ rfdc@^1.1.4: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" integrity sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug== -rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.3: +rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -2428,11 +2377,6 @@ semver@~7.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - set-immediate-shim@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" @@ -2679,13 +2623,6 @@ tmp@0.0.33, tmp@0.0.x: dependencies: os-tmpdir "~1.0.2" -tmp@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" - integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== - dependencies: - rimraf "^2.6.3" - to-array@0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" @@ -2762,7 +2699,7 @@ type-is@~1.6.17: mime-types "~2.1.24" "typescript@file:../../node_modules/typescript": - version "4.0.2" + version "4.1.2" typescript@~3.9.5: version "3.9.6" @@ -2866,10 +2803,8 @@ webdriver-js-extender@2.1.0: "@types/selenium-webdriver" "^3.0.0" selenium-webdriver "^3.0.1" -webdriver-manager@^12.0.6: - version "12.1.7" - resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.1.7.tgz#ed4eaee8f906b33c146e869b55e850553a1b1162" - integrity sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA== +webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" @@ -2883,11 +2818,6 @@ webdriver-manager@^12.0.6: semver "^5.3.0" xml2js "^0.4.17" -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - which@^1.2.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -2900,10 +2830,10 @@ wordwrap@~0.0.2: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" @@ -2942,40 +2872,33 @@ xmlhttprequest-ssl@~1.5.4: resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= -y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== +y18n@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" + integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= -yargs-parser@^18.1.0: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" +yargs-parser@^20.2.2: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== -yargs@15.3.0: - version "15.3.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.0.tgz#403af6edc75b3ae04bf66c94202228ba119f0976" - integrity sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA== +yargs@^16.1.1: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" yeast@0.1.2: version "0.1.2" @@ -2993,7 +2916,8 @@ z-schema@~3.18.3: optionalDependencies: commander "^2.7.1" -"zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.11.2" +"zone.js@file:../../dist/zone.js-dist/archive/zone.js.tgz": + version "0.11.3" + resolved "file:../../dist/zone.js-dist/archive/zone.js.tgz#980e0cb8787678150873a9904781f9e028e58d8a" dependencies: tslib "^2.0.0" diff --git a/integration/cli-hello-world-ivy-compat/src/app/app.component.html b/integration/cli-hello-world-ivy-compat/src/app/app.component.html index 6bc721e5e7..d2e2b35602 100644 --- a/integration/cli-hello-world-ivy-compat/src/app/app.component.html +++ b/integration/cli-hello-world-ivy-compat/src/app/app.component.html @@ -445,28 +445,6 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/integration/cli-hello-world-ivy-compat/src/environments/environment.ts b/integration/cli-hello-world-ivy-compat/src/environments/environment.ts index 7b4f817adb..30d7bccb19 100644 --- a/integration/cli-hello-world-ivy-compat/src/environments/environment.ts +++ b/integration/cli-hello-world-ivy-compat/src/environments/environment.ts @@ -13,4 +13,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/integration/cli-hello-world-ivy-compat/src/polyfills.ts b/integration/cli-hello-world-ivy-compat/src/polyfills.ts index f39dafba9c..b2e18b7be3 100644 --- a/integration/cli-hello-world-ivy-compat/src/polyfills.ts +++ b/integration/cli-hello-world-ivy-compat/src/polyfills.ts @@ -55,7 +55,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** diff --git a/integration/cli-hello-world-ivy-compat/src/test.ts b/integration/cli-hello-world-ivy-compat/src/test.ts index d56a4e9e92..a6f15af366 100644 --- a/integration/cli-hello-world-ivy-compat/src/test.ts +++ b/integration/cli-hello-world-ivy-compat/src/test.ts @@ -1,8 +1,11 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; -import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; declare const require: any; diff --git a/integration/cli-hello-world-ivy-compat/yarn.lock b/integration/cli-hello-world-ivy-compat/yarn.lock index fbcbc6df63..ca977e1bd4 100644 --- a/integration/cli-hello-world-ivy-compat/yarn.lock +++ b/integration/cli-hello-world-ivy-compat/yarn.lock @@ -9136,7 +9136,7 @@ webdriver-js-extender@2.1.0: selenium-webdriver "^3.0.1" webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": - version "12.1.7" + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" diff --git a/integration/cli-hello-world-ivy-i18n/src/environments/environment.ts b/integration/cli-hello-world-ivy-i18n/src/environments/environment.ts index 7b4f817adb..30d7bccb19 100644 --- a/integration/cli-hello-world-ivy-i18n/src/environments/environment.ts +++ b/integration/cli-hello-world-ivy-i18n/src/environments/environment.ts @@ -13,4 +13,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/integration/cli-hello-world-ivy-i18n/src/polyfills.ts b/integration/cli-hello-world-ivy-i18n/src/polyfills.ts index ac4cd2caed..5e92eaf278 100644 --- a/integration/cli-hello-world-ivy-i18n/src/polyfills.ts +++ b/integration/cli-hello-world-ivy-i18n/src/polyfills.ts @@ -55,7 +55,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates. diff --git a/integration/cli-hello-world-ivy-i18n/src/test.ts b/integration/cli-hello-world-ivy-i18n/src/test.ts index 16317897b1..a6f15af366 100644 --- a/integration/cli-hello-world-ivy-i18n/src/test.ts +++ b/integration/cli-hello-world-ivy-i18n/src/test.ts @@ -1,6 +1,6 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/integration/cli-hello-world-ivy-i18n/yarn.lock b/integration/cli-hello-world-ivy-i18n/yarn.lock index 7f8c48e69c..c8c2cfa06d 100644 --- a/integration/cli-hello-world-ivy-i18n/yarn.lock +++ b/integration/cli-hello-world-ivy-i18n/yarn.lock @@ -9063,7 +9063,7 @@ webdriver-js-extender@2.1.0: selenium-webdriver "^3.0.1" webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": - version "12.1.7" + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" diff --git a/integration/cli-hello-world-ivy-minimal/src/polyfills.ts b/integration/cli-hello-world-ivy-minimal/src/polyfills.ts index f39dafba9c..b2e18b7be3 100644 --- a/integration/cli-hello-world-ivy-minimal/src/polyfills.ts +++ b/integration/cli-hello-world-ivy-minimal/src/polyfills.ts @@ -55,7 +55,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** diff --git a/integration/cli-hello-world-ivy-minimal/src/test.ts b/integration/cli-hello-world-ivy-minimal/src/test.ts index 16317897b1..a6f15af366 100644 --- a/integration/cli-hello-world-ivy-minimal/src/test.ts +++ b/integration/cli-hello-world-ivy-minimal/src/test.ts @@ -1,6 +1,6 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/integration/cli-hello-world-ivy-minimal/yarn.lock b/integration/cli-hello-world-ivy-minimal/yarn.lock index fbcbc6df63..ca977e1bd4 100644 --- a/integration/cli-hello-world-ivy-minimal/yarn.lock +++ b/integration/cli-hello-world-ivy-minimal/yarn.lock @@ -9136,7 +9136,7 @@ webdriver-js-extender@2.1.0: selenium-webdriver "^3.0.1" webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": - version "12.1.7" + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" diff --git a/integration/cli-hello-world-lazy-rollup/src/app/app.component.html b/integration/cli-hello-world-lazy-rollup/src/app/app.component.html index 40152fefaf..c5c71f8c3e 100644 --- a/integration/cli-hello-world-lazy-rollup/src/app/app.component.html +++ b/integration/cli-hello-world-lazy-rollup/src/app/app.component.html @@ -443,28 +443,6 @@ - - - - - - - - - - - - - - - - - - - - - - @@ -528,4 +506,4 @@ - \ No newline at end of file + diff --git a/integration/cli-hello-world-lazy-rollup/src/environments/environment.ts b/integration/cli-hello-world-lazy-rollup/src/environments/environment.ts index 7b4f817adb..30d7bccb19 100644 --- a/integration/cli-hello-world-lazy-rollup/src/environments/environment.ts +++ b/integration/cli-hello-world-lazy-rollup/src/environments/environment.ts @@ -13,4 +13,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/integration/cli-hello-world-lazy-rollup/src/polyfills.ts b/integration/cli-hello-world-lazy-rollup/src/polyfills.ts index f39dafba9c..b2e18b7be3 100644 --- a/integration/cli-hello-world-lazy-rollup/src/polyfills.ts +++ b/integration/cli-hello-world-lazy-rollup/src/polyfills.ts @@ -55,7 +55,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** diff --git a/integration/cli-hello-world-lazy-rollup/src/test.ts b/integration/cli-hello-world-lazy-rollup/src/test.ts index 16317897b1..a6f15af366 100644 --- a/integration/cli-hello-world-lazy-rollup/src/test.ts +++ b/integration/cli-hello-world-lazy-rollup/src/test.ts @@ -1,6 +1,6 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/integration/cli-hello-world-lazy-rollup/yarn.lock b/integration/cli-hello-world-lazy-rollup/yarn.lock index 7c6b3b721d..addbeca040 100644 --- a/integration/cli-hello-world-lazy-rollup/yarn.lock +++ b/integration/cli-hello-world-lazy-rollup/yarn.lock @@ -8722,7 +8722,7 @@ webdriver-js-extender@2.1.0: selenium-webdriver "^3.0.1" webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": - version "12.1.7" + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" diff --git a/integration/cli-hello-world-lazy/src/app/app.component.html b/integration/cli-hello-world-lazy/src/app/app.component.html index 40152fefaf..c5c71f8c3e 100644 --- a/integration/cli-hello-world-lazy/src/app/app.component.html +++ b/integration/cli-hello-world-lazy/src/app/app.component.html @@ -443,28 +443,6 @@ - - - - - - - - - - - - - - - - - - - - - - @@ -528,4 +506,4 @@ - \ No newline at end of file + diff --git a/integration/cli-hello-world-lazy/src/environments/environment.ts b/integration/cli-hello-world-lazy/src/environments/environment.ts index 7b4f817adb..30d7bccb19 100644 --- a/integration/cli-hello-world-lazy/src/environments/environment.ts +++ b/integration/cli-hello-world-lazy/src/environments/environment.ts @@ -13,4 +13,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/integration/cli-hello-world-lazy/src/polyfills.ts b/integration/cli-hello-world-lazy/src/polyfills.ts index f39dafba9c..b2e18b7be3 100644 --- a/integration/cli-hello-world-lazy/src/polyfills.ts +++ b/integration/cli-hello-world-lazy/src/polyfills.ts @@ -55,7 +55,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** diff --git a/integration/cli-hello-world-lazy/src/test.ts b/integration/cli-hello-world-lazy/src/test.ts index 16317897b1..a6f15af366 100644 --- a/integration/cli-hello-world-lazy/src/test.ts +++ b/integration/cli-hello-world-lazy/src/test.ts @@ -1,6 +1,6 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/integration/cli-hello-world-lazy/yarn.lock b/integration/cli-hello-world-lazy/yarn.lock index 1f63ab592d..c9dea56212 100644 --- a/integration/cli-hello-world-lazy/yarn.lock +++ b/integration/cli-hello-world-lazy/yarn.lock @@ -8722,7 +8722,7 @@ webdriver-js-extender@2.1.0: selenium-webdriver "^3.0.1" webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": - version "12.1.7" + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" diff --git a/integration/cli-hello-world/src/app/app.component.html b/integration/cli-hello-world/src/app/app.component.html index 343c5cc22d..bacd7ef1d7 100644 --- a/integration/cli-hello-world/src/app/app.component.html +++ b/integration/cli-hello-world/src/app/app.component.html @@ -443,28 +443,6 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/integration/cli-hello-world/src/environments/environment.ts b/integration/cli-hello-world/src/environments/environment.ts index 7b4f817adb..30d7bccb19 100644 --- a/integration/cli-hello-world/src/environments/environment.ts +++ b/integration/cli-hello-world/src/environments/environment.ts @@ -13,4 +13,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/integration/cli-hello-world/src/polyfills.ts b/integration/cli-hello-world/src/polyfills.ts index f39dafba9c..b2e18b7be3 100644 --- a/integration/cli-hello-world/src/polyfills.ts +++ b/integration/cli-hello-world/src/polyfills.ts @@ -55,7 +55,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** diff --git a/integration/cli-hello-world/src/test.ts b/integration/cli-hello-world/src/test.ts index 16317897b1..a6f15af366 100644 --- a/integration/cli-hello-world/src/test.ts +++ b/integration/cli-hello-world/src/test.ts @@ -1,6 +1,6 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/integration/cli-hello-world/yarn.lock b/integration/cli-hello-world/yarn.lock index c43b89fea0..84a9408b4f 100644 --- a/integration/cli-hello-world/yarn.lock +++ b/integration/cli-hello-world/yarn.lock @@ -9658,7 +9658,7 @@ webdriver-js-extender@2.1.0: selenium-webdriver "^3.0.1" webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": - version "12.1.7" + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" diff --git a/integration/dynamic-compiler/yarn.lock b/integration/dynamic-compiler/yarn.lock index 41a34f0086..3f273cb1f1 100644 --- a/integration/dynamic-compiler/yarn.lock +++ b/integration/dynamic-compiler/yarn.lock @@ -3566,7 +3566,7 @@ webdriver-js-extender@2.1.0: selenium-webdriver "^3.0.1" webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": - version "12.1.7" + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" diff --git a/integration/hello_world__closure/yarn.lock b/integration/hello_world__closure/yarn.lock index a29fcd8587..0cd2f78f8f 100644 --- a/integration/hello_world__closure/yarn.lock +++ b/integration/hello_world__closure/yarn.lock @@ -3428,7 +3428,7 @@ webdriver-js-extender@2.1.0: selenium-webdriver "^3.0.1" webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": - version "12.1.7" + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" diff --git a/integration/hello_world__systemjs_umd/yarn.lock b/integration/hello_world__systemjs_umd/yarn.lock index ec4d8cbaa4..9b351456d6 100644 --- a/integration/hello_world__systemjs_umd/yarn.lock +++ b/integration/hello_world__systemjs_umd/yarn.lock @@ -2404,7 +2404,7 @@ webdriver-js-extender@2.1.0: selenium-webdriver "^3.0.1" webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": - version "12.1.7" + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" diff --git a/integration/i18n/package.json b/integration/i18n/package.json index ad0c9955d0..533fb325f1 100644 --- a/integration/i18n/package.json +++ b/integration/i18n/package.json @@ -29,7 +29,7 @@ }, "scripts": { "closure": "google-closure-compiler --flagfile closure.conf", - "test": "ngc && yarn run closure && concurrently \"yarn run serve\" \"yarn run protractor\" --kill-others --success first && npm run test-locale-folder", + "test": "ngc && yarn run closure && concurrently \"yarn run serve\" \"yarn run protractor\" --kill-others --success first && yarn test-locale-folder", "test-locale-folder": "node test-locale-folder.js", "serve": "lite-server -c e2e/browser.config.json", "preprotractor": "tsc -p e2e", diff --git a/integration/i18n/yarn.lock b/integration/i18n/yarn.lock index 20ec898cfa..653d98b6a8 100644 --- a/integration/i18n/yarn.lock +++ b/integration/i18n/yarn.lock @@ -3262,7 +3262,7 @@ webdriver-js-extender@2.1.0: selenium-webdriver "^3.0.1" webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": - version "12.1.7" + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" diff --git a/integration/injectable-def/package.json b/integration/injectable-def/package.json index 054b482fab..66fdde6ffe 100644 --- a/integration/injectable-def/package.json +++ b/integration/injectable-def/package.json @@ -22,6 +22,10 @@ "lite-server": "2.2.2", "protractor": "file:../../node_modules/protractor" }, + "//resolutions-comment": "Ensure a single version of webdriver-manager which comes from root node_modules that has already run webdriver-manager update", + "resolutions": { + "**/webdriver-manager": "file:../../node_modules/webdriver-manager" + }, "scripts": { "test": "./test.sh" } diff --git a/integration/injectable-def/src/main.ts b/integration/injectable-def/src/main.ts index 85cb135d51..b40898c59d 100644 --- a/integration/injectable-def/src/main.ts +++ b/integration/injectable-def/src/main.ts @@ -1,4 +1,4 @@ -import 'zone.js/dist/zone-node'; +import 'zone.js/node'; import {enableProdMode} from '@angular/core'; import {renderModuleFactory} from '@angular/platform-server'; diff --git a/integration/injectable-def/test.sh b/integration/injectable-def/test.sh index e0bceaeb07..9e6bbda0e0 100755 --- a/integration/injectable-def/test.sh +++ b/integration/injectable-def/test.sh @@ -1,9 +1,6 @@ #!/bin/bash set -e -x -NPM_BIN=$(npm bin) -PATH="$PATH:${NPM_BIN}" - rm -rf node_modules/lib1_built node_modules/lib2_built dist/ ngc -p tsconfig-lib1.json diff --git a/integration/injectable-def/yarn.lock b/integration/injectable-def/yarn.lock index 54910874fc..9385dab027 100644 --- a/integration/injectable-def/yarn.lock +++ b/integration/injectable-def/yarn.lock @@ -68,9 +68,10 @@ accepts@~1.3.3, accepts@~1.3.4: mime-types "~2.1.18" negotiator "0.6.1" -adm-zip@^0.4.7: - version "0.4.7" - resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.7.tgz#8606c2cbf1c426ce8c8ec00174447fd49b6eafc1" +adm-zip@^0.4.9: + version "0.4.13" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.13.tgz#597e2f8cc3672151e1307d3e95cddbc75672314a" + integrity sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw== after@0.8.2: version "0.8.2" @@ -2697,11 +2698,10 @@ webdriver-js-extender@2.1.0: "@types/selenium-webdriver" "^3.0.0" selenium-webdriver "^3.0.1" -webdriver-manager@^12.0.6: - version "12.0.6" - resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.0.6.tgz#3df1a481977010b4cbf8c9d85c7a577828c0e70b" +webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": + version "12.1.8" dependencies: - adm-zip "^0.4.7" + adm-zip "^0.4.9" chalk "^1.1.1" del "^2.2.0" glob "^7.0.3" diff --git a/integration/ivy-i18n/package.json b/integration/ivy-i18n/package.json index e07a6202d5..52f14f25a5 100644 --- a/integration/ivy-i18n/package.json +++ b/integration/ivy-i18n/package.json @@ -31,28 +31,27 @@ }, "private": true, "dependencies": { - "@angular/animations": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/animations/npm_package_archive.tar.gz", - "@angular/common": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/common/npm_package_archive.tar.gz", - "@angular/compiler": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/compiler/npm_package_archive.tar.gz", - "@angular/core": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/core/npm_package_archive.tar.gz", - "@angular/forms": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/forms/npm_package_archive.tar.gz", - "@angular/localize": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/localize/npm_package_archive.tar.gz", - "@angular/platform-browser": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/platform-browser/npm_package_archive.tar.gz", - "@angular/platform-browser-dynamic": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/platform-browser-dynamic/npm_package_archive.tar.gz", - "@angular/router": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/router/npm_package_archive.tar.gz", - "core-js": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/core-js_archive.tar.gz", - "rxjs": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/rxjs_archive.tar.gz", - "tslib": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/tslib_archive.tar.gz", - "zone.js": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/zone.js/npm_package_archive.tar.gz" + "@angular/animations": "file:../../dist/packages-dist/animations", + "@angular/common": "file:../../dist/packages-dist/common", + "@angular/compiler": "file:../../dist/packages-dist/compiler", + "@angular/core": "file:../../dist/packages-dist/core", + "@angular/forms": "file:../../dist/packages-dist/forms", + "@angular/localize": "file:../../dist/packages-dist/localize", + "@angular/platform-browser": "file:../../dist/packages-dist/platform-browser", + "@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic", + "@angular/router": "file:../../dist/packages-dist/router", + "rxjs": "file:../../node_modules/rxjs", + "tslib": "file:../../node_modules/tslib", + "zone.js": "file:../../dist/zone.js-dist/archive/zone.js.tgz" }, "devDependencies": { - "@angular-devkit/build-angular": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/angular-devkit_build-angular_archive.tar.gz", - "@angular/cli": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/angular_cli_archive.tar.gz", - "@angular/compiler-cli": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/compiler-cli/npm_package_archive.tar.gz", - "@angular/language-service": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/language-service/npm_package_archive.tar.gz", - "@types/jasmine": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/types_jasmine_archive.tar.gz", - "@types/jasminewd2": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/types_jasminewd2_archive.tar.gz", - "@types/node": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/types_node_archive.tar.gz", + "@angular-devkit/build-angular": "file:../../node_modules/@angular-devkit/build-angular", + "@angular/cli": "file:../../node_modules/@angular/cli", + "@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli", + "@angular/language-service": "file:../../dist/packages-dist/language-service", + "@types/jasmine": "file:../../node_modules/@types/jasmine", + "@types/jasminewd2": "file:../../node_modules/@types/jasminewd2", + "@types/node": "file:../../node_modules/@types/node", "codelyzer": "5.2.0", "jasmine-core": "3.5.0", "jasmine-spec-reporter": "4.2.1", @@ -62,15 +61,15 @@ "karma-jasmine": "2.0.1", "karma-jasmine-html-reporter": "1.4.2", "npm-run-all": "4.1.5", - "protractor": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/protractor_archive.tar.gz", - "puppeteer": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/puppeteer_archive.tar.gz", + "protractor": "file:../../node_modules/protractor", + "puppeteer": "file:../../node_modules/puppeteer", "serve": "11.2.0", "ts-node": "8.3.0", "tslint": "5.18.0", - "typescript": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/typescript_archive.tar.gz" + "typescript": "file:../../node_modules/typescript" }, "//resolutions-comment": "Ensure a single version of webdriver-manager which comes from root node_modules that has already run webdriver-manager update", "resolutions": { - "**/webdriver-manager": "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/webdriver-manager_archive.tar.gz" + "**/webdriver-manager": "file:../../node_modules/webdriver-manager" } -} \ No newline at end of file +} diff --git a/integration/ivy-i18n/src/environments/environment.ts b/integration/ivy-i18n/src/environments/environment.ts index 7b4f817adb..30d7bccb19 100644 --- a/integration/ivy-i18n/src/environments/environment.ts +++ b/integration/ivy-i18n/src/environments/environment.ts @@ -13,4 +13,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/integration/ivy-i18n/src/polyfills-runtime.ts b/integration/ivy-i18n/src/polyfills-runtime.ts index 63ba0f8456..4ab7f057f3 100644 --- a/integration/ivy-i18n/src/polyfills-runtime.ts +++ b/integration/ivy-i18n/src/polyfills-runtime.ts @@ -57,7 +57,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates. diff --git a/integration/ivy-i18n/src/polyfills.ts b/integration/ivy-i18n/src/polyfills.ts index ac4cd2caed..5e92eaf278 100644 --- a/integration/ivy-i18n/src/polyfills.ts +++ b/integration/ivy-i18n/src/polyfills.ts @@ -55,7 +55,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates. diff --git a/integration/ivy-i18n/src/test.ts b/integration/ivy-i18n/src/test.ts index 16317897b1..a6f15af366 100644 --- a/integration/ivy-i18n/src/test.ts +++ b/integration/ivy-i18n/src/test.ts @@ -1,6 +1,6 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/integration/ivy-i18n/yarn.lock b/integration/ivy-i18n/yarn.lock index ba2f3923ae..ce5bdde629 100644 --- a/integration/ivy-i18n/yarn.lock +++ b/integration/ivy-i18n/yarn.lock @@ -2,183 +2,166 @@ # yarn lockfile v1 -"@angular-devkit/architect@0.1000.0-rc.2": - version "0.1000.0-rc.2" - resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1000.0-rc.2.tgz#a6aaef2ceed03c28817b23d0e393bf67b383a393" - integrity sha512-4Nhrr56cVEXAykIwAVcpqKNNeMXIpfoxeWF/PLWr5VTV8XR2GO1h7wGz0f1/RRrxkOy5/6EGD7GoPpNVoPQ1/A== +"@angular-devkit/architect@0.1100.0-rc.1": + version "0.1100.0-rc.1" + resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1100.0-rc.1.tgz#8cc88af98a0158a75ed53b0d12b302457f282f77" + integrity sha512-8WeuUSbwfbC8A5xpXUYuMjp11pJ9YIJXBFqQX9jHyxrqkYSpck9eQkJuEEdVV9+YzRORAjnCh8vtQlOvo0pzYg== dependencies: - "@angular-devkit/core" "10.0.0-rc.2" - rxjs "6.5.5" + "@angular-devkit/core" "11.0.0-rc.1" + rxjs "6.6.3" -"@angular-devkit/architect@0.901.0": - version "0.901.0" - resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.901.0.tgz#c660cb76e3bd35fc294d8e8578782b83157924aa" - integrity sha512-SlqEBkPrT40zMCy5344AsUqC76pEPCaGPaAkCIvadaz2dC9vNMzQrvubCPJHViD/TumkSX1kYmLS3iYASVM9GQ== +"@angular-devkit/build-angular@file:../../node_modules/@angular-devkit/build-angular": + version "0.1100.0-rc.1" dependencies: - "@angular-devkit/core" "9.1.0" - rxjs "6.5.4" - -"@angular-devkit/build-angular@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/angular-devkit_build-angular_archive.tar.gz": - version "0.1000.0-rc.2" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/angular-devkit_build-angular_archive.tar.gz#ebf425b04d732b6bd21f2017338d80b2f9046dff" - dependencies: - "@angular-devkit/architect" "0.1000.0-rc.2" - "@angular-devkit/build-optimizer" "0.1000.0-rc.2" - "@angular-devkit/build-webpack" "0.1000.0-rc.2" - "@angular-devkit/core" "10.0.0-rc.2" - "@babel/core" "7.9.6" - "@babel/generator" "7.9.6" - "@babel/plugin-transform-runtime" "7.9.6" - "@babel/preset-env" "7.9.6" - "@babel/runtime" "7.9.6" - "@babel/template" "7.8.6" - "@jsdevtools/coverage-istanbul-loader" "3.0.3" - "@ngtools/webpack" "10.0.0-rc.2" - ajv "6.12.2" - autoprefixer "9.8.0" + "@angular-devkit/architect" "0.1100.0-rc.1" + "@angular-devkit/build-optimizer" "0.1100.0-rc.1" + "@angular-devkit/build-webpack" "0.1100.0-rc.1" + "@angular-devkit/core" "11.0.0-rc.1" + "@babel/core" "7.12.3" + "@babel/generator" "7.12.1" + "@babel/plugin-transform-runtime" "7.12.1" + "@babel/preset-env" "7.12.1" + "@babel/runtime" "7.12.1" + "@babel/template" "7.10.4" + "@jsdevtools/coverage-istanbul-loader" "3.0.5" + "@ngtools/webpack" "11.0.0-rc.1" + ansi-colors "4.1.1" + autoprefixer "9.8.6" babel-loader "8.1.0" browserslist "^4.9.1" - cacache "15.0.3" + cacache "15.0.5" caniuse-lite "^1.0.30001032" circular-dependency-plugin "5.2.0" - copy-webpack-plugin "5.1.1" - core-js "3.6.4" - css-loader "3.5.3" + copy-webpack-plugin "6.2.1" + core-js "3.6.5" + css-loader "5.0.0" cssnano "4.1.10" - file-loader "6.0.0" + file-loader "6.1.1" find-cache-dir "3.3.1" glob "7.1.6" - jest-worker "26.0.0" + inquirer "7.3.3" + jest-worker "26.5.0" karma-source-map-support "1.4.0" - less-loader "6.1.0" - license-webpack-plugin "2.2.0" + less "3.12.2" + less-loader "7.0.2" + license-webpack-plugin "2.3.1" loader-utils "2.0.0" - mini-css-extract-plugin "0.9.0" + mini-css-extract-plugin "1.2.1" minimatch "3.0.4" - open "7.0.4" - parse5 "4.0.0" + open "7.3.0" + ora "5.1.0" + parse5-html-rewriting-stream "6.0.1" pnp-webpack-plugin "1.6.4" - postcss "7.0.31" + postcss "7.0.32" postcss-import "12.0.1" - postcss-loader "3.0.0" - raw-loader "4.0.1" - regenerator-runtime "0.13.5" - resolve-url-loader "3.1.1" + postcss-loader "4.0.4" + raw-loader "4.0.2" + regenerator-runtime "0.13.7" + resolve-url-loader "3.1.2" rimraf "3.0.2" - rollup "2.10.9" - rxjs "6.5.5" - sass "1.26.5" - sass-loader "8.0.2" + rollup "2.32.1" + rxjs "6.6.3" + sass "1.27.0" + sass-loader "10.0.4" semver "7.3.2" source-map "0.7.3" - source-map-loader "1.0.0" + source-map-loader "1.1.2" source-map-support "0.5.19" speed-measure-webpack-plugin "1.3.3" - style-loader "1.2.1" - stylus "0.54.7" - stylus-loader "3.0.2" - terser "4.7.0" - terser-webpack-plugin "3.0.1" + style-loader "2.0.0" + stylus "0.54.8" + stylus-loader "4.1.1" + terser "5.3.7" + terser-webpack-plugin "4.2.3" + text-table "0.2.0" tree-kill "1.2.2" - webpack "4.43.0" + webpack "4.44.2" webpack-dev-middleware "3.7.2" webpack-dev-server "3.11.0" - webpack-merge "4.2.2" - webpack-sources "1.4.3" - webpack-subresource-integrity "1.4.1" - worker-plugin "4.0.3" + webpack-merge "5.2.0" + webpack-sources "2.0.1" + webpack-subresource-integrity "1.5.1" + worker-plugin "5.0.0" -"@angular-devkit/build-optimizer@0.1000.0-rc.2": - version "0.1000.0-rc.2" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1000.0-rc.2.tgz#963043cbcc50869a3b8f6c9152388dcb2240c42a" - integrity sha512-z9lhoS9/mwsQ5zltoiWkzz3NDhqtAu1jr8WObha+nV2Lh087Un1PbgmZDGfZUKoOacve8vm39472D9+ypT5U+w== +"@angular-devkit/build-optimizer@0.1100.0-rc.1": + version "0.1100.0-rc.1" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1100.0-rc.1.tgz#7f211949497f2d24d9b7caa50b8e151deef4dd30" + integrity sha512-ST+N1gMiPUPtuvhKRh6HaVPP0fBVW4vFRo9GY1MFLP1n3RXR8XjEWTIIRuqo8XueG31VfI0EUfFVc6V4VEuRuQ== dependencies: loader-utils "2.0.0" source-map "0.7.3" - tslib "2.0.0" - webpack-sources "1.4.3" + tslib "2.0.3" + typescript "4.0.5" + webpack-sources "2.0.1" -"@angular-devkit/build-webpack@0.1000.0-rc.2": - version "0.1000.0-rc.2" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1000.0-rc.2.tgz#2d4fb96614d95aca6d825aeae71194335a79e3d0" - integrity sha512-2UCyDiGC9ymo0vNkDyc9c6u//+Z5VlU9hO7CI1fodH0/snXpjnPuP/BI74NMQFyYuK0MWQZe3AiUgybisqhnEA== +"@angular-devkit/build-webpack@0.1100.0-rc.1": + version "0.1100.0-rc.1" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1100.0-rc.1.tgz#0fd12216ff1a74ef53bfe27c603bddaa55ce4844" + integrity sha512-H9+HCT66fwYI+ZWyIVTfM8LLXQOqU3rtY9Bd5EvbgM3ZgFs76qzwqy0eAsgxWgpATULD2fpv1StY0gb07pgihA== dependencies: - "@angular-devkit/architect" "0.1000.0-rc.2" - "@angular-devkit/core" "10.0.0-rc.2" - rxjs "6.5.5" + "@angular-devkit/architect" "0.1100.0-rc.1" + "@angular-devkit/core" "11.0.0-rc.1" + rxjs "6.6.3" -"@angular-devkit/core@10.0.0-rc.2": - version "10.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-10.0.0-rc.2.tgz#6bc0bea5dec4b86960ff778e2d2b0ab5384648c3" - integrity sha512-Yggx8uKCLJ31u1NpSb6USZsHcbejUHgJlBAmC8WiJeSDROO/kiDWsfPqa5q94NmKXcv3gdVwN44c8h5HA4hcEQ== +"@angular-devkit/core@11.0.0-rc.1": + version "11.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-11.0.0-rc.1.tgz#3e60d3016c6dfad4dfc5b5073ae0e274e1cb78b2" + integrity sha512-RZGWAuehQ83n76i+iDPC4nmgGqwG1ek1a0Wk6je8hw1oCKeSvGqfW0olFjFc2BO8pdkEGNLZdvhuoBLhElGoiw== dependencies: - ajv "6.12.2" + ajv "6.12.6" fast-json-stable-stringify "2.1.0" magic-string "0.25.7" - rxjs "6.5.5" + rxjs "6.6.3" source-map "0.7.3" -"@angular-devkit/core@9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-9.1.0.tgz#5cc89bb5d44c6fd12a3527bae0253adb60d64b07" - integrity sha512-vHTsrB4JaVUQ95FRnKrgo79Y3F6FokImrZdrmwkQmwAThpjXeXmpUEKZS+ZSTFRgesjiIysVGOFijARP4BQ7Bg== +"@angular-devkit/schematics@11.0.0-rc.1": + version "11.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-11.0.0-rc.1.tgz#6dafcd4f015ce2b71d2bebca36c7fa4cd7d16095" + integrity sha512-v/HgErCoGS84k6iEjRhmiIafHOrUEB7Ib+Bn4ruDEwY2amnxOOqyS7JiimAlOlcbqRZGmf6vBQ1Jmi2fjZYfRw== dependencies: - ajv "6.12.0" - fast-json-stable-stringify "2.1.0" - magic-string "0.25.7" - rxjs "6.5.4" - source-map "0.7.3" + "@angular-devkit/core" "11.0.0-rc.1" + ora "5.1.0" + rxjs "6.6.3" -"@angular-devkit/schematics@9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-9.1.0.tgz#f453a9ff78c34a5468cc21830ac4a46089ee1a31" - integrity sha512-cb9PSvskMwWlL54fPfCcpJoyNDWAX6Wo7CzL5qpIB2cJCPLAuyfRUYYrkO77YUST+n2HvypHz0cZ5SNGMfaaBQ== - dependencies: - "@angular-devkit/core" "9.1.0" - ora "4.0.3" - rxjs "6.5.4" - -"@angular/animations@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/animations/npm_package_archive.tar.gz": - version "0.0.0" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/animations/npm_package_archive.tar.gz#080c969d73724fbd72c36122f1a82883d74611d9" +"@angular/animations@file:../../dist/packages-dist/animations": + version "11.1.0-next.1" dependencies: tslib "^2.0.0" -"@angular/cli@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/angular_cli_archive.tar.gz": - version "9.1.0" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/angular_cli_archive.tar.gz#011b7e1c77e111e97fb9efccbc220390213deea0" +"@angular/cli@file:../../node_modules/@angular/cli": + version "11.0.0-rc.1" dependencies: - "@angular-devkit/architect" "0.901.0" - "@angular-devkit/core" "9.1.0" - "@angular-devkit/schematics" "9.1.0" - "@schematics/angular" "9.1.0" - "@schematics/update" "0.901.0" + "@angular-devkit/architect" "0.1100.0-rc.1" + "@angular-devkit/core" "11.0.0-rc.1" + "@angular-devkit/schematics" "11.0.0-rc.1" + "@schematics/angular" "11.0.0-rc.1" + "@schematics/update" "0.1100.0-rc.1" "@yarnpkg/lockfile" "1.1.0" ansi-colors "4.1.1" - debug "4.1.1" + debug "4.2.0" ini "1.3.5" - inquirer "7.1.0" - npm-package-arg "8.0.1" - npm-pick-manifest "6.0.0" - open "7.0.3" - pacote "11.1.4" - read-package-tree "5.3.1" + inquirer "7.3.3" + npm-package-arg "8.1.0" + npm-pick-manifest "6.1.0" + open "7.3.0" + pacote "9.5.12" + resolve "1.18.1" rimraf "3.0.2" - semver "7.1.3" - symbol-observable "1.2.0" - universal-analytics "0.4.20" - uuid "7.0.2" + semver "7.3.2" + symbol-observable "2.0.3" + universal-analytics "0.4.23" + uuid "8.3.1" -"@angular/common@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/common/npm_package_archive.tar.gz": - version "0.0.0" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/common/npm_package_archive.tar.gz#9242646930ec2b7540ef4a691375386324b41265" +"@angular/common@file:../../dist/packages-dist/common": + version "11.1.0-next.1" dependencies: tslib "^2.0.0" -"@angular/compiler-cli@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/compiler-cli/npm_package_archive.tar.gz": - version "0.0.0" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/compiler-cli/npm_package_archive.tar.gz#cab4a270d5dc00d0193cea285a427b4f34774949" +"@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli": + version "11.1.0-next.1" dependencies: + "@babel/core" "^7.8.6" + "@babel/types" "^7.8.6" canonical-path "1.0.0" chokidar "^3.0.0" convert-source-map "^1.5.1" @@ -191,53 +174,45 @@ source-map "^0.6.1" sourcemap-codec "^1.4.8" tslib "^2.0.0" - yargs "15.3.0" + yargs "^16.1.1" -"@angular/compiler@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/compiler/npm_package_archive.tar.gz": - version "0.0.0" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/compiler/npm_package_archive.tar.gz#f7bd74c6e8469dd368a350a23fb06d977e611cf0" +"@angular/compiler@file:../../dist/packages-dist/compiler": + version "11.1.0-next.1" dependencies: tslib "^2.0.0" -"@angular/core@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/core/npm_package_archive.tar.gz": - version "0.0.0" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/core/npm_package_archive.tar.gz#8a876eccb8039b838f3baaf47e57ebb6d6401b3d" +"@angular/core@file:../../dist/packages-dist/core": + version "11.1.0-next.1" dependencies: tslib "^2.0.0" -"@angular/forms@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/forms/npm_package_archive.tar.gz": - version "0.0.0" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/forms/npm_package_archive.tar.gz#7285060079740ec6affc6635a5b51a7c4121b519" +"@angular/forms@file:../../dist/packages-dist/forms": + version "11.1.0-next.1" dependencies: tslib "^2.0.0" -"@angular/language-service@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/language-service/npm_package_archive.tar.gz": - version "0.0.0" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/language-service/npm_package_archive.tar.gz#fb441316133a05929632b0bacb20ae6a74c95069" +"@angular/language-service@file:../../dist/packages-dist/language-service": + version "11.1.0-next.1" -"@angular/localize@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/localize/npm_package_archive.tar.gz": - version "0.0.0" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/localize/npm_package_archive.tar.gz#7c32dc03f3dadbae328e08a9e43f9aa9bf89b654" +"@angular/localize@file:../../dist/packages-dist/localize": + version "11.1.0-next.1" dependencies: "@babel/core" "7.8.3" glob "7.1.2" - yargs "15.3.0" + yargs "^16.1.1" -"@angular/platform-browser-dynamic@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/platform-browser-dynamic/npm_package_archive.tar.gz": - version "0.0.0" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/platform-browser-dynamic/npm_package_archive.tar.gz#84f4c6e92704446ab30ea3891de31d786ebad453" +"@angular/platform-browser-dynamic@file:../../dist/packages-dist/platform-browser-dynamic": + version "11.1.0-next.1" dependencies: tslib "^2.0.0" -"@angular/platform-browser@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/platform-browser/npm_package_archive.tar.gz": - version "0.0.0" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/platform-browser/npm_package_archive.tar.gz#79bb5d589502126165c7ca7a05c5e2d1519f37b3" +"@angular/platform-browser@file:../../dist/packages-dist/platform-browser": + version "11.1.0-next.1" dependencies: tslib "^2.0.0" -"@angular/router@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/router/npm_package_archive.tar.gz": - version "0.0.0" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/router/npm_package_archive.tar.gz#c81f91451732b12539c436832eff6897b5c0f56e" +"@angular/router@file:../../dist/packages-dist/router": + version "11.1.0-next.1" dependencies: tslib "^2.0.0" @@ -248,12 +223,12 @@ dependencies: "@babel/highlight" "^7.0.0" -"@babel/code-frame@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff" - integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw== +"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" + integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== dependencies: - "@babel/highlight" "^7.10.1" + "@babel/highlight" "^7.12.13" "@babel/code-frame@^7.8.3": version "7.8.3" @@ -262,14 +237,32 @@ dependencies: "@babel/highlight" "^7.8.3" -"@babel/compat-data@^7.10.1", "@babel/compat-data@^7.9.6": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.10.1.tgz#b1085ffe72cd17bf2c0ee790fc09f9626011b2db" - integrity sha512-CHvCj7So7iCkGKPRFUfryXIkU2gSBw7VSZFYLsqVhrS47269VK2Hfi9S/YcublPMW8k1u2bQBlbDruoQEm4fgw== +"@babel/compat-data@^7.12.1", "@babel/compat-data@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.12.13.tgz#27e19e0ed3726ccf54067ced4109501765e7e2e8" + integrity sha512-U/hshG5R+SIoW7HVWIdmy1cB7s3ki+r3FpyEZiCgpi4tFgPnX/vynY80ZGSASOIrUM6O7VxOgCZgdt7h97bUGg== + +"@babel/core@7.12.3": + version "7.12.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz#1b436884e1e3bff6fb1328dc02b208759de92ad8" + integrity sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g== dependencies: - browserslist "^4.12.0" - invariant "^2.2.4" - semver "^5.5.0" + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.1" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.1" + "@babel/parser" "^7.12.3" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.1" + "@babel/types" "^7.12.1" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" "@babel/core@7.8.3", "@babel/core@^7.7.5": version "7.8.3" @@ -292,46 +285,43 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.6.tgz#d9aa1f580abf3b2286ef40b6904d390904c63376" - integrity sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg== +"@babel/core@^7.8.6": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.13.tgz#b73a87a3a3e7d142a66248bf6ad88b9ceb093425" + integrity sha512-BQKE9kXkPlXHPeqissfxo0lySWJcYdEP0hdtJOH/iJfDdhOCcgtNCjftCJg3qqauB4h+lz2N6ixM++b9DN1Tcw== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.6" - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helpers" "^7.9.6" - "@babel/parser" "^7.9.6" - "@babel/template" "^7.8.6" - "@babel/traverse" "^7.9.6" - "@babel/types" "^7.9.6" + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.12.13" + "@babel/helper-module-transforms" "^7.12.13" + "@babel/helpers" "^7.12.13" + "@babel/parser" "^7.12.13" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.12.13" + "@babel/types" "^7.12.13" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.1" json5 "^2.1.2" - lodash "^4.17.13" - resolve "^1.3.2" + lodash "^4.17.19" semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.6.tgz#5408c82ac5de98cda0d77d8124e99fa1f2170a43" - integrity sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ== +"@babel/generator@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.1.tgz#0d70be32bdaa03d7c51c8597dda76e0df1f15468" + integrity sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg== dependencies: - "@babel/types" "^7.9.6" + "@babel/types" "^7.12.1" jsesc "^2.5.1" - lodash "^4.17.13" source-map "^0.5.0" -"@babel/generator@^7.10.1", "@babel/generator@^7.9.6": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.2.tgz#0fa5b5b2389db8bfdfcc3492b551ee20f5dd69a9" - integrity sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA== +"@babel/generator@^7.12.1", "@babel/generator@^7.12.13": + version "7.12.15" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.15.tgz#4617b5d0b25cc572474cc1aafee1edeaf9b5368f" + integrity sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ== dependencies: - "@babel/types" "^7.10.2" + "@babel/types" "^7.12.13" jsesc "^2.5.1" - lodash "^4.17.13" source-map "^0.5.0" "@babel/generator@^7.4.0", "@babel/generator@^7.6.2": @@ -361,25 +351,42 @@ dependencies: "@babel/types" "^7.10.1" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.1.tgz#0ec7d9be8174934532661f87783eb18d72290059" - integrity sha512-cQpVq48EkYxUU0xozpGCLla3wlkdRRqLWu1ksFMXA9CM5KQmyyRpSEsYXbao7JUkOw/tAaYKCaYyZq6HOFYtyw== +"@babel/helper-annotate-as-pure@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab" + integrity sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw== dependencies: - "@babel/helper-explode-assignable-expression" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/types" "^7.12.13" -"@babel/helper-compilation-targets@^7.9.6": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.2.tgz#a17d9723b6e2c750299d2a14d4637c76936d8285" - integrity sha512-hYgOhF4To2UTB4LTaZepN/4Pl9LD4gfbJx8A34mqoluT8TLbof1mhUlYuNWTEebONa8+UlCC4X0TEXu7AOUyGA== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz#6bc20361c88b0a74d05137a65cac8d3cbf6f61fc" + integrity sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA== dependencies: - "@babel/compat-data" "^7.10.1" - browserslist "^4.12.0" - invariant "^2.2.4" - levenary "^1.1.1" + "@babel/helper-explode-assignable-expression" "^7.12.13" + "@babel/types" "^7.12.13" + +"@babel/helper-compilation-targets@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.13.tgz#d689cdef88810aa74e15a7a94186f26a3d773c98" + integrity sha512-dXof20y/6wB5HnLOGyLh/gobsMvDNoekcC+8MCV2iaTd5JemhFkPD73QB+tK3iFC9P0xJC73B6MvKkyUfS9cCw== + dependencies: + "@babel/compat-data" "^7.12.13" + "@babel/helper-validator-option" "^7.12.11" + browserslist "^4.14.5" semver "^5.5.0" +"@babel/helper-create-class-features-plugin@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.13.tgz#0f1707c2eec1a4604f2a22a6fb209854ef2a399a" + integrity sha512-Vs/e9wv7rakKYeywsmEBSRC9KtmE7Px+YBlESekLeJOF0zbGUicGfXSNi3o+tfXSNS48U/7K9mIOOCR79Cl3+Q== + dependencies: + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-member-expression-to-functions" "^7.12.13" + "@babel/helper-optimise-call-expression" "^7.12.13" + "@babel/helper-replace-supers" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/helper-create-regexp-features-plugin@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.1.tgz#1b8feeab1594cbcfbf3ab5a3bbcabac0468efdbd" @@ -389,30 +396,20 @@ "@babel/helper-regex" "^7.10.1" regexpu-core "^4.7.0" -"@babel/helper-create-regexp-features-plugin@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.3.tgz#c774268c95ec07ee92476a3862b75cc2839beb79" - integrity sha512-Gcsm1OHCUr9o9TcJln57xhWHtdXbA2pgQ58S0Lxlks0WMGNXuki4+GLfX0p+L2ZkINUGZvfkz8rzoqJQSthI+Q== +"@babel/helper-create-regexp-features-plugin@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.13.tgz#0996d370a92896c612ae41a4215544bd152579c0" + integrity sha512-XC+kiA0J3at6E85dL5UnCYfVOcIZ834QcAY0TIpgUVnz0zDzg+0TtvZTnJ4g9L1dPRGe30Qi03XCIS4tYCLtqw== dependencies: - "@babel/helper-regex" "^7.8.3" - regexpu-core "^4.6.0" + "@babel/helper-annotate-as-pure" "^7.12.13" + regexpu-core "^4.7.1" -"@babel/helper-define-map@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.1.tgz#5e69ee8308648470dd7900d159c044c10285221d" - integrity sha512-+5odWpX+OnvkD0Zmq7panrMuAGQBu6aPUgvMzuMGo4R+jUOvealEj2hiqI6WhxgKrTpFoFj0+VdsuA8KDxHBDg== +"@babel/helper-explode-assignable-expression@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.13.tgz#0e46990da9e271502f77507efa4c9918d3d8634a" + integrity sha512-5loeRNvMo9mx1dA/d6yNi+YiKziJZFylZnCo1nmFF4qPU4yJ14abhWESuSMQSlQxWdxdOFzxXjk/PpfudTtYyw== dependencies: - "@babel/helper-function-name" "^7.10.1" - "@babel/types" "^7.10.1" - lodash "^4.17.13" - -"@babel/helper-explode-assignable-expression@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.1.tgz#e9d76305ee1162ca467357ae25df94f179af2b7e" - integrity sha512-vcUJ3cDjLjvkKzt6rHrl767FeE7pMEYfPanq5L16GRtrXIoznc0HykNW2aEYkcnP76P0isoqJ34dDMFZwzEpJg== - dependencies: - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/types" "^7.12.13" "@babel/helper-function-name@^7.1.0": version "7.1.0" @@ -423,14 +420,14 @@ "@babel/template" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-function-name@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz#92bd63829bfc9215aca9d9defa85f56b539454f4" - integrity sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ== +"@babel/helper-function-name@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz#93ad656db3c3c2232559fd7b2c3dbdcbe0eb377a" + integrity sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA== dependencies: - "@babel/helper-get-function-arity" "^7.10.1" - "@babel/template" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/helper-get-function-arity" "^7.12.13" + "@babel/template" "^7.12.13" + "@babel/types" "^7.12.13" "@babel/helper-function-name@^7.8.3": version "7.8.3" @@ -448,12 +445,12 @@ dependencies: "@babel/types" "^7.0.0" -"@babel/helper-get-function-arity@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz#7303390a81ba7cb59613895a192b93850e373f7d" - integrity sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw== +"@babel/helper-get-function-arity@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" + integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg== dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.12.13" "@babel/helper-get-function-arity@^7.8.3": version "7.8.3" @@ -462,53 +459,48 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-hoist-variables@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.1.tgz#7e77c82e5dcae1ebf123174c385aaadbf787d077" - integrity sha512-vLm5srkU8rI6X3+aQ1rQJyfjvCBLXP8cAGeuw04zeAM2ItKb1e7pmVmLyHb4sDaAYnLL13RHOZPLEtcGZ5xvjg== +"@babel/helper-hoist-variables@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.12.13.tgz#13aba58b7480b502362316ea02f52cca0e9796cd" + integrity sha512-KSC5XSj5HreRhYQtZ3cnSnQwDzgnbdUDEFsxkN0m6Q3WrCRt72xrnZ8+h+pX7YxM7hr87zIO3a/v5p/H3TrnVw== dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.12.13" -"@babel/helper-member-expression-to-functions@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz#432967fd7e12a4afef66c4687d4ca22bc0456f15" - integrity sha512-u7XLXeM2n50gb6PWJ9hoO5oO7JFPaZtrh35t8RqKLT1jFKj9IWeD1zrcrYp1q1qiZTdEarfDWfTIP8nGsu0h5g== +"@babel/helper-member-expression-to-functions@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.13.tgz#c5715695b4f8bab32660dbdcdc2341dec7e3df40" + integrity sha512-B+7nN0gIL8FZ8SvMcF+EPyB21KnCcZHQZFczCxbiNGV/O0rsrSBlWGLzmtBJ3GMjSVMIm4lpFhR+VdVBuIsUcQ== dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.12.13" -"@babel/helper-module-imports@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz#dd331bd45bccc566ce77004e9d05fe17add13876" - integrity sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg== +"@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz#ec67e4404f41750463e455cc3203f6a32e93fcb0" + integrity sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g== dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.12.13" -"@babel/helper-module-imports@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" - integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.12.13.tgz#01afb052dcad2044289b7b20beb3fa8bd0265bea" + integrity sha512-acKF7EjqOR67ASIlDTupwkKM1eUisNAjaSduo5Cz+793ikfnpe7p4Q7B7EWU2PCoSTPWsQkR7hRUWEIZPiVLGA== dependencies: - "@babel/types" "^7.8.3" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-replace-supers" "^7.12.13" + "@babel/helper-simple-access" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/helper-validator-identifier" "^7.12.11" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.12.13" + "@babel/types" "^7.12.13" + lodash "^4.17.19" -"@babel/helper-module-transforms@^7.10.1", "@babel/helper-module-transforms@^7.9.0": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz#24e2f08ee6832c60b157bb0936c86bef7210c622" - integrity sha512-RLHRCAzyJe7Q7sF4oy2cB+kRnU4wDZY/H2xJFGof+M+SJEGhZsb+GFj5j1AD8NiSaVBJ+Pf0/WObiXu/zxWpFg== +"@babel/helper-optimise-call-expression@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" + integrity sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA== dependencies: - "@babel/helper-module-imports" "^7.10.1" - "@babel/helper-replace-supers" "^7.10.1" - "@babel/helper-simple-access" "^7.10.1" - "@babel/helper-split-export-declaration" "^7.10.1" - "@babel/template" "^7.10.1" - "@babel/types" "^7.10.1" - lodash "^4.17.13" - -"@babel/helper-optimise-call-expression@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz#b4a1f2561870ce1247ceddb02a3860fa96d72543" - integrity sha512-a0DjNS1prnBsoKx83dP2falChcs7p3i8VMzdrSbfLhuQra/2ENC4sbri34dz/rWmDADsmF1q5GbfaXydh0Jbjg== - dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.12.13" "@babel/helper-plugin-utils@^7.0.0": version "7.0.0" @@ -520,6 +512,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz#ec5a5cf0eec925b66c60580328b122c01230a127" integrity sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA== +"@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz#174254d0f2424d8aefb4dd48057511247b0a9eeb" + integrity sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA== + "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" @@ -532,48 +529,45 @@ dependencies: lodash "^4.17.13" -"@babel/helper-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.8.3.tgz#139772607d51b93f23effe72105b319d2a4c6965" - integrity sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ== +"@babel/helper-remap-async-to-generator@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.13.tgz#170365f4140e2d20e5c88f8ba23c24468c296878" + integrity sha512-Qa6PU9vNcj1NZacZZI1Mvwt+gXDH6CTfgAkSjeRMLE8HxtDK76+YDId6NQR+z7Rgd5arhD2cIbS74r0SxD6PDA== dependencies: - lodash "^4.17.13" + "@babel/helper-annotate-as-pure" "^7.12.13" + "@babel/helper-wrap-function" "^7.12.13" + "@babel/types" "^7.12.13" -"@babel/helper-remap-async-to-generator@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.1.tgz#bad6aaa4ff39ce8d4b82ccaae0bfe0f7dbb5f432" - integrity sha512-RfX1P8HqsfgmJ6CwaXGKMAqbYdlleqglvVtht0HGPMSsy2V6MqLlOJVF/0Qyb/m2ZCi2z3q3+s6Pv7R/dQuZ6A== +"@babel/helper-replace-supers@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz#00ec4fb6862546bd3d0aff9aac56074277173121" + integrity sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/helper-wrap-function" "^7.10.1" - "@babel/template" "^7.10.1" - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/helper-member-expression-to-functions" "^7.12.13" + "@babel/helper-optimise-call-expression" "^7.12.13" + "@babel/traverse" "^7.12.13" + "@babel/types" "^7.12.13" -"@babel/helper-replace-supers@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz#ec6859d20c5d8087f6a2dc4e014db7228975f13d" - integrity sha512-SOwJzEfpuQwInzzQJGjGaiG578UYmyi2Xw668klPWV5n07B73S0a9btjLk/52Mlcxa+5AdIYqws1KyXRfMoB7A== +"@babel/helper-simple-access@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz#8478bcc5cacf6aa1672b251c1d2dde5ccd61a6c4" + integrity sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA== dependencies: - "@babel/helper-member-expression-to-functions" "^7.10.1" - "@babel/helper-optimise-call-expression" "^7.10.1" - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/types" "^7.12.13" -"@babel/helper-simple-access@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz#08fb7e22ace9eb8326f7e3920a1c2052f13d851e" - integrity sha512-VSWpWzRzn9VtgMJBIWTZ+GP107kZdQ4YplJlCmIrjoLVSi/0upixezHCDG8kpPVTBJpKfxTH01wDhh+jS2zKbw== +"@babel/helper-skip-transparent-expression-wrappers@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf" + integrity sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA== dependencies: - "@babel/template" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/types" "^7.12.1" -"@babel/helper-split-export-declaration@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz#c6f4be1cbc15e3a868e4c64a17d5d31d754da35f" - integrity sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g== +"@babel/helper-split-export-declaration@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05" + integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== dependencies: - "@babel/types" "^7.10.1" + "@babel/types" "^7.12.13" "@babel/helper-split-export-declaration@^7.4.4": version "7.4.4" @@ -594,15 +588,34 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5" integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw== -"@babel/helper-wrap-function@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.1.tgz#956d1310d6696257a7afd47e4c42dfda5dfcedc9" - integrity sha512-C0MzRGteVDn+H32/ZgbAv5r56f2o1fZSA/rj/TYo8JEJNHg+9BdSmKBUND0shxWRztWhjlT2cvHYuynpPsVJwQ== +"@babel/helper-validator-identifier@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== + +"@babel/helper-validator-option@^7.12.1", "@babel/helper-validator-option@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz#d66cb8b7a3e7fe4c6962b32020a131ecf0847f4f" + integrity sha512-TBFCyj939mFSdeX7U7DDj32WtzYY7fDcalgq8v3fBZMNOJQNn7nOYzMaUCiPxPYfCup69mtIpqlKgMZLvQ8Xhw== + +"@babel/helper-wrap-function@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.12.13.tgz#e3ea8cb3ee0a16911f9c1b50d9e99fe8fe30f9ff" + integrity sha512-t0aZFEmBJ1LojdtJnhOaQEVejnzYhyjWHSsNSNo8vOYRbAJNh6r6GQF7pd36SqG7OKGbn+AewVQ/0IfYfIuGdw== dependencies: - "@babel/helper-function-name" "^7.10.1" - "@babel/template" "^7.10.1" - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/helper-function-name" "^7.12.13" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.12.13" + "@babel/types" "^7.12.13" + +"@babel/helpers@^7.12.1", "@babel/helpers@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.12.13.tgz#3c75e993632e4dadc0274eae219c73eb7645ba47" + integrity sha512-oohVzLRZ3GQEk4Cjhfs9YkJA4TdIDTObdBEZGrd6F/T0GPSnuV6l22eMcxlvcvzVIPH3VTtxbseudM1zIE+rPQ== + dependencies: + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.12.13" + "@babel/types" "^7.12.13" "@babel/helpers@^7.8.3": version "7.8.3" @@ -613,15 +626,6 @@ "@babel/traverse" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/helpers@^7.9.6": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.1.tgz#a6827b7cb975c9d9cef5fd61d919f60d8844a973" - integrity sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw== - dependencies: - "@babel/template" "^7.10.1" - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.1" - "@babel/highlight@^7.0.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" @@ -631,12 +635,12 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/highlight@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.1.tgz#841d098ba613ba1a427a2b383d79e35552c38ae0" - integrity sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg== +"@babel/highlight@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.12.13.tgz#8ab538393e00370b26271b01fa08f7f27f2e795c" + integrity sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww== dependencies: - "@babel/helper-validator-identifier" "^7.10.1" + "@babel/helper-validator-identifier" "^7.12.11" chalk "^2.0.0" js-tokens "^4.0.0" @@ -649,10 +653,10 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.10.1", "@babel/parser@^7.8.6", "@babel/parser@^7.9.6": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" - integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ== +"@babel/parser@^7.10.4", "@babel/parser@^7.12.13", "@babel/parser@^7.12.3": + version "7.12.15" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.15.tgz#2b20de7f0b4b332d9b119dd9c33409c538b8aacf" + integrity sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA== "@babel/parser@^7.4.3", "@babel/parser@^7.6.0", "@babel/parser@^7.6.2": version "7.6.2" @@ -664,73 +668,114 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.3.tgz#790874091d2001c9be6ec426c2eed47bc7679081" integrity sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ== -"@babel/plugin-proposal-async-generator-functions@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.1.tgz#6911af5ba2e615c4ff3c497fe2f47b35bf6d7e55" - integrity sha512-vzZE12ZTdB336POZjmpblWfNNRpMSua45EYnRigE2XsZxcXcIyly2ixnTJasJE4Zq3U7t2d8rRF7XRUuzHxbOw== +"@babel/plugin-proposal-async-generator-functions@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.13.tgz#d1c6d841802ffb88c64a2413e311f7345b9e66b5" + integrity sha512-1KH46Hx4WqP77f978+5Ye/VUbuwQld2hph70yaw2hXS2v7ER2f3nlpNMu909HO2rbvP0NKLlMVDPh9KXklVMhA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-remap-async-to-generator" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-remap-async-to-generator" "^7.12.13" "@babel/plugin-syntax-async-generators" "^7.8.0" -"@babel/plugin-proposal-dynamic-import@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.1.tgz#e36979dc1dc3b73f6d6816fc4951da2363488ef0" - integrity sha512-Cpc2yUVHTEGPlmiQzXj026kqwjEQAD9I4ZC16uzdbgWgitg/UHKHLffKNCQZ5+y8jpIZPJcKcwsr2HwPh+w3XA== +"@babel/plugin-proposal-class-properties@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.13.tgz#3d2ce350367058033c93c098e348161d6dc0d8c8" + integrity sha512-8SCJ0Ddrpwv4T7Gwb33EmW1V9PY5lggTO+A8WjyIwxrSHDUyBw4MtF96ifn1n8H806YlxbVCoKXbbmzD6RD+cA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-create-class-features-plugin" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-proposal-dynamic-import@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz#43eb5c2a3487ecd98c5c8ea8b5fdb69a2749b2dc" + integrity sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-dynamic-import" "^7.8.0" -"@babel/plugin-proposal-json-strings@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.1.tgz#b1e691ee24c651b5a5e32213222b2379734aff09" - integrity sha512-m8r5BmV+ZLpWPtMY2mOKN7wre6HIO4gfIiV+eOmsnZABNenrt/kzYBwrh+KOfgumSWpnlGs5F70J8afYMSJMBg== +"@babel/plugin-proposal-export-namespace-from@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz#393be47a4acd03fa2af6e3cde9b06e33de1b446d" + integrity sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.13.tgz#ced7888a2db92a3d520a2e35eb421fdb7fcc9b5d" + integrity sha512-v9eEi4GiORDg8x+Dmi5r8ibOe0VXoKDeNPYcTTxdGN4eOWikrJfDJCJrr1l5gKGvsNyGJbrfMftC2dTL6oz7pg== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-json-strings" "^7.8.0" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.1.tgz#02dca21673842ff2fe763ac253777f235e9bbf78" - integrity sha512-56cI/uHYgL2C8HVuHOuvVowihhX0sxb3nnfVRzUeVHTWmRHTZrKuAh/OBIMggGU/S1g/1D2CRCXqP+3u7vX7iA== +"@babel/plugin-proposal-logical-assignment-operators@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.13.tgz#575b5d9a08d8299eeb4db6430da6e16e5cf14350" + integrity sha512-fqmiD3Lz7jVdK6kabeSr1PZlWSUVqSitmHEe3Z00dtGTKieWnX9beafvavc32kjORa5Bai4QNHgFDwWJP+WtSQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.13.tgz#24867307285cee4e1031170efd8a7ac807deefde" + integrity sha512-Qoxpy+OxhDBI5kRqliJFAl4uWXk3Bn24WeFstPH0iLymFehSAUR8MHpqU7njyXv/qbo7oN6yTy5bfCmXdKpo1Q== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" -"@babel/plugin-proposal-numeric-separator@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.1.tgz#a9a38bc34f78bdfd981e791c27c6fdcec478c123" - integrity sha512-jjfym4N9HtCiNfyyLAVD8WqPYeHUrw4ihxuAynWj6zzp2gf9Ey2f7ImhFm6ikB3CLf5Z/zmcJDri6B4+9j9RsA== +"@babel/plugin-proposal-numeric-separator@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz#bd9da3188e787b5120b4f9d465a8261ce67ed1db" + integrity sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-numeric-separator" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.9.6": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.1.tgz#cba44908ac9f142650b4a65b8aa06bf3478d5fb6" - integrity sha512-Z+Qri55KiQkHh7Fc4BW6o+QBuTagbOp9txE+4U1i79u9oWlf2npkiDx+Rf3iK3lbcHBuNy9UOkwuR5wOMH3LIQ== +"@babel/plugin-proposal-object-rest-spread@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.13.tgz#f93f3116381ff94bc676fdcb29d71045cd1ec011" + integrity sha512-WvA1okB/0OS/N3Ldb3sziSrXg6sRphsBgqiccfcQq7woEn5wQLNX82Oc4PlaFcdwcWHuQXAtb8ftbS8Fbsg/sg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-transform-parameters" "^7.10.1" + "@babel/plugin-transform-parameters" "^7.12.13" -"@babel/plugin-proposal-optional-catch-binding@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.1.tgz#c9f86d99305f9fa531b568ff5ab8c964b8b223d2" - integrity sha512-VqExgeE62YBqI3ogkGoOJp1R6u12DFZjqwJhqtKc2o5m1YTUuUWnos7bZQFBhwkxIFpWYJ7uB75U7VAPPiKETA== +"@babel/plugin-proposal-optional-catch-binding@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.13.tgz#4640520afe57728af14b4d1574ba844f263bcae5" + integrity sha512-9+MIm6msl9sHWg58NvqpNpLtuFbmpFYk37x8kgnGzAHvX35E1FyAwSUt5hIkSoWJFSAH+iwU8bJ4fcD1zKXOzg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" -"@babel/plugin-proposal-optional-chaining@^7.9.0": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.1.tgz#15f5d6d22708629451a91be28f8facc55b0e818c" - integrity sha512-dqQj475q8+/avvok72CF3AOSV/SGEcH29zT5hhohqqvvZ2+boQoOr7iGldBG5YXTO2qgCgc2B3WvVLUdbeMlGA== +"@babel/plugin-proposal-optional-chaining@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.13.tgz#63a7d805bc8ce626f3234ee5421a2a7fb23f66d9" + integrity sha512-0ZwjGfTcnZqyV3y9DSD1Yk3ebp+sIUpT2YDqP8hovzaNZnQq2Kd7PEqa6iOIUDBXBt7Jl3P7YAcEIL5Pz8u09Q== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" "@babel/plugin-syntax-optional-chaining" "^7.8.0" -"@babel/plugin-proposal-unicode-property-regex@^7.4.4", "@babel/plugin-proposal-unicode-property-regex@^7.8.3": +"@babel/plugin-proposal-private-methods@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.13.tgz#ea78a12554d784ecf7fc55950b752d469d9c4a71" + integrity sha512-sV0V57uUwpauixvR7s2o75LmwJI6JECwm5oPUY5beZB1nBl2i37hc7CJGqB5G+58fur5Y6ugvl3LRONk5x34rg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-proposal-unicode-property-regex@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz#bebde51339be829c17aaaaced18641deb62b39ba" + integrity sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.1.tgz#dc04feb25e2dd70c12b05d680190e138fa2c0c6f" integrity sha512-JjfngYRvwmPwmnbRZyNiPFI8zxCZb8euzbCG/LxyKdeTb59tVciKo9GK9bi6JYKInk1H11Dq9j/zRqIH4KigfQ== @@ -745,6 +790,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-class-properties@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/plugin-syntax-dynamic-import@^7.8.0": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" @@ -752,6 +804,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-json-strings@^7.8.0": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" @@ -759,6 +818,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" @@ -766,12 +832,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.10.1", "@babel/plugin-syntax-numeric-separator@^7.8.0": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.1.tgz#25761ee7410bc8cf97327ba741ee94e4a61b7d99" - integrity sha512-uTd0OsHrpe3tH5gRPTxG8Voh99/WCU78vIm5NMRYPAqC8lR4vajt6KkCAknCHrx24vkPdd/05yfdGSB4EIY2mg== +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread@^7.8.0": version "7.8.3" @@ -794,73 +860,79 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.1.tgz#8b8733f8c57397b3eaa47ddba8841586dcaef362" - integrity sha512-hgA5RYkmZm8FTFT3yu2N9Bx7yVVOKYT6yEdXXo6j2JTm0wNxgqaGeQVaSHRjhfnQbX91DtjFB6McRFSlcJH3xQ== +"@babel/plugin-syntax-top-level-await@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz#c5f0fa6e249f5b739727f923540cf7a806130178" + integrity sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-arrow-functions@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.1.tgz#cb5ee3a36f0863c06ead0b409b4cc43a889b295b" - integrity sha512-6AZHgFJKP3DJX0eCNJj01RpytUa3SOGawIxweHkNX2L6PYikOZmoh5B0d7hIHaIgveMjX990IAa/xK7jRTN8OA== +"@babel/plugin-transform-arrow-functions@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.13.tgz#eda5670b282952100c229f8a3bd49e0f6a72e9fe" + integrity sha512-tBtuN6qtCTd+iHzVZVOMNp+L04iIJBpqkdY42tWbmjIT5wvR2kx7gxMBsyhQtFzHwBbyGi9h8J8r9HgnOpQHxg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-async-to-generator@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.1.tgz#e5153eb1a3e028f79194ed8a7a4bf55f862b2062" - integrity sha512-XCgYjJ8TY2slj6SReBUyamJn3k2JLUIiiR5b6t1mNCMSvv7yx+jJpaewakikp0uWFQSF7ChPPoe3dHmXLpISkg== +"@babel/plugin-transform-async-to-generator@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.13.tgz#fed8c69eebf187a535bfa4ee97a614009b24f7ae" + integrity sha512-psM9QHcHaDr+HZpRuJcE1PXESuGWSCcbiGFFhhwfzdbTxaGDVzuVtdNYliAwcRo3GFg0Bc8MmI+AvIGYIJG04A== dependencies: - "@babel/helper-module-imports" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-remap-async-to-generator" "^7.10.1" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-remap-async-to-generator" "^7.12.13" -"@babel/plugin-transform-block-scoped-functions@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.1.tgz#146856e756d54b20fff14b819456b3e01820b85d" - integrity sha512-B7K15Xp8lv0sOJrdVAoukKlxP9N59HS48V1J3U/JGj+Ad+MHq+am6xJVs85AgXrQn4LV8vaYFOB+pr/yIuzW8Q== +"@babel/plugin-transform-block-scoped-functions@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz#a9bf1836f2a39b4eb6cf09967739de29ea4bf4c4" + integrity sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-block-scoping@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.1.tgz#47092d89ca345811451cd0dc5d91605982705d5e" - integrity sha512-8bpWG6TtF5akdhIm/uWTyjHqENpy13Fx8chg7pFH875aNLwX8JxIxqm08gmAT+Whe6AOmaTeLPe7dpLbXt+xUw== +"@babel/plugin-transform-block-scoping@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.13.tgz#f36e55076d06f41dfd78557ea039c1b581642e61" + integrity sha512-Pxwe0iqWJX4fOOM2kEZeUuAxHMWb9nK+9oh5d11bsLoB0xMg+mkDpt0eYuDZB7ETrY9bbcVlKUGTOGWy7BHsMQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - lodash "^4.17.13" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-classes@^7.9.5": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.1.tgz#6e11dd6c4dfae70f540480a4702477ed766d733f" - integrity sha512-P9V0YIh+ln/B3RStPoXpEQ/CoAxQIhRSUn7aXqQ+FZJ2u8+oCtjIXR3+X0vsSD8zv+mb56K7wZW1XiDTDGiDRQ== +"@babel/plugin-transform-classes@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.13.tgz#9728edc1838b5d62fc93ad830bd523b1fcb0e1f6" + integrity sha512-cqZlMlhCC1rVnxE5ZGMtIb896ijL90xppMiuWXcwcOAuFczynpd3KYemb91XFFPi3wJSe/OcrX9lXoowatkkxA== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/helper-define-map" "^7.10.1" - "@babel/helper-function-name" "^7.10.1" - "@babel/helper-optimise-call-expression" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-replace-supers" "^7.10.1" - "@babel/helper-split-export-declaration" "^7.10.1" + "@babel/helper-annotate-as-pure" "^7.12.13" + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-optimise-call-expression" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-replace-supers" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.1.tgz#59aa399064429d64dce5cf76ef9b90b7245ebd07" - integrity sha512-mqSrGjp3IefMsXIenBfGcPXxJxweQe2hEIwMQvjtiDQ9b1IBvDUjkAtV/HMXX47/vXf14qDNedXsIiNd1FmkaQ== +"@babel/plugin-transform-computed-properties@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.13.tgz#6a210647a3d67f21f699cfd2a01333803b27339d" + integrity sha512-dDfuROUPGK1mTtLKyDPUavmj2b6kFu82SmgpztBFEO974KMjJT+Ytj3/oWsTUMBmgPcp9J5Pc1SlcAYRpJ2hRA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-destructuring@^7.9.5": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.1.tgz#abd58e51337815ca3a22a336b85f62b998e71907" - integrity sha512-V/nUc4yGWG71OhaTH705pU8ZSdM6c1KmmLP8ys59oOYbT7RpMYAR3MsVOt6OHL0WzG7BlTU076va9fjJyYzJMA== +"@babel/plugin-transform-destructuring@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.13.tgz#fc56c5176940c5b41735c677124d1d20cecc9aeb" + integrity sha512-Dn83KykIFzjhA3FDPA1z4N+yfF3btDGhjnJwxIj0T43tP0flCujnU8fKgEkf0C1biIpSv9NZegPBQ1J6jYkwvQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-dotall-regex@^7.4.4", "@babel/plugin-transform-dotall-regex@^7.8.3": +"@babel/plugin-transform-dotall-regex@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz#3f1601cc29905bfcb67f53910f197aeafebb25ad" + integrity sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-transform-dotall-regex@^7.4.4": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.1.tgz#920b9fec2d78bb57ebb64a644d5c2ba67cc104ee" integrity sha512-19VIMsD1dp02RvduFUmfzj8uknaO3uiHHF0s3E1OHnVsNj8oge8EQ5RzHRbJjGSetRnkEuBYO7TG1M5kKjGLOA== @@ -868,257 +940,269 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.1" "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-duplicate-keys@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.1.tgz#c900a793beb096bc9d4d0a9d0cde19518ffc83b9" - integrity sha512-wIEpkX4QvX8Mo9W6XF3EdGttrIPZWozHfEaDTU0WJD/TDnXMvdDh30mzUl/9qWhnf7naicYartcEfUghTCSNpA== +"@babel/plugin-transform-duplicate-keys@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz#6f06b87a8b803fd928e54b81c258f0a0033904de" + integrity sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-exponentiation-operator@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.1.tgz#279c3116756a60dd6e6f5e488ba7957db9c59eb3" - integrity sha512-lr/przdAbpEA2BUzRvjXdEDLrArGRRPwbaF9rvayuHRvdQ7lUTTkZnhZrJ4LE2jvgMRFF4f0YuPQ20vhiPYxtA== +"@babel/plugin-transform-exponentiation-operator@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz#4d52390b9a273e651e4aba6aee49ef40e80cd0a1" + integrity sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-for-of@^7.9.0": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.1.tgz#ff01119784eb0ee32258e8646157ba2501fcfda5" - integrity sha512-US8KCuxfQcn0LwSCMWMma8M2R5mAjJGsmoCBVwlMygvmDUMkTCykc84IqN1M7t+agSfOmLYTInLCHJM+RUoz+w== +"@babel/plugin-transform-for-of@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.13.tgz#561ff6d74d9e1c8879cb12dbaf4a14cd29d15cf6" + integrity sha512-xCbdgSzXYmHGyVX3+BsQjcd4hv4vA/FDy7Kc8eOpzKmBBPEOTurt0w5fCRQaGl+GSBORKgJdstQ1rHl4jbNseQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-function-name@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.1.tgz#4ed46fd6e1d8fde2a2ec7b03c66d853d2c92427d" - integrity sha512-//bsKsKFBJfGd65qSNNh1exBy5Y9gD9ZN+DvrJ8f7HXr4avE5POW6zB7Rj6VnqHV33+0vXWUwJT0wSHubiAQkw== +"@babel/plugin-transform-function-name@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz#bb024452f9aaed861d374c8e7a24252ce3a50051" + integrity sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ== dependencies: - "@babel/helper-function-name" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-literals@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.1.tgz#5794f8da82846b22e4e6631ea1658bce708eb46a" - integrity sha512-qi0+5qgevz1NHLZroObRm5A+8JJtibb7vdcPQF1KQE12+Y/xxl8coJ+TpPW9iRq+Mhw/NKLjm+5SHtAHCC7lAw== +"@babel/plugin-transform-literals@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz#2ca45bafe4a820197cf315794a4d26560fe4bdb9" + integrity sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-member-expression-literals@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.1.tgz#90347cba31bca6f394b3f7bd95d2bbfd9fce2f39" - integrity sha512-UmaWhDokOFT2GcgU6MkHC11i0NQcL63iqeufXWfRy6pUOGYeCGEKhvfFO6Vz70UfYJYHwveg62GS83Rvpxn+NA== +"@babel/plugin-transform-member-expression-literals@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz#5ffa66cd59b9e191314c9f1f803b938e8c081e40" + integrity sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-modules-amd@^7.9.6": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.1.tgz#65950e8e05797ebd2fe532b96e19fc5482a1d52a" - integrity sha512-31+hnWSFRI4/ACFr1qkboBbrTxoBIzj7qA69qlq8HY8p7+YCzkCT6/TvQ1a4B0z27VeWtAeJd6pr5G04dc1iHw== +"@babel/plugin-transform-modules-amd@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.13.tgz#43db16249b274ee2e551e2422090aa1c47692d56" + integrity sha512-JHLOU0o81m5UqG0Ulz/fPC68/v+UTuGTWaZBUwpEk1fYQ1D9LfKV6MPn4ttJKqRo5Lm460fkzjLTL4EHvCprvA== dependencies: - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-module-transforms" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.9.6": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.1.tgz#d5ff4b4413ed97ffded99961056e1fb980fb9301" - integrity sha512-AQG4fc3KOah0vdITwt7Gi6hD9BtQP/8bhem7OjbaMoRNCH5Djx42O2vYMfau7QnAzQCa+RJnhJBmFFMGpQEzrg== +"@babel/plugin-transform-modules-commonjs@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.13.tgz#5043b870a784a8421fa1fd9136a24f294da13e50" + integrity sha512-OGQoeVXVi1259HjuoDnsQMlMkT9UkZT9TpXAsqWplS/M0N1g3TJAn/ByOCeQu7mfjc5WpSsRU+jV1Hd89ts0kQ== dependencies: - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-simple-access" "^7.10.1" + "@babel/helper-module-transforms" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-simple-access" "^7.12.13" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.9.6": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.1.tgz#9962e4b0ac6aaf2e20431ada3d8ec72082cbffb6" - integrity sha512-ewNKcj1TQZDL3YnO85qh9zo1YF1CHgmSTlRQgHqe63oTrMI85cthKtZjAiZSsSNjPQ5NCaYo5QkbYqEw1ZBgZA== +"@babel/plugin-transform-modules-systemjs@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.13.tgz#351937f392c7f07493fc79b2118201d50404a3c5" + integrity sha512-aHfVjhZ8QekaNF/5aNdStCGzwTbU7SI5hUybBKlMzqIMC7w7Ho8hx5a4R/DkTHfRfLwHGGxSpFt9BfxKCoXKoA== dependencies: - "@babel/helper-hoist-variables" "^7.10.1" - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-hoist-variables" "^7.12.13" + "@babel/helper-module-transforms" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-validator-identifier" "^7.12.11" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-umd@^7.9.0": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.1.tgz#ea080911ffc6eb21840a5197a39ede4ee67b1595" - integrity sha512-EIuiRNMd6GB6ulcYlETnYYfgv4AxqrswghmBRQbWLHZxN4s7mupxzglnHqk9ZiUpDI4eRWewedJJNj67PWOXKA== +"@babel/plugin-transform-modules-umd@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.13.tgz#26c66f161d3456674e344b4b1255de4d530cfb37" + integrity sha512-BgZndyABRML4z6ibpi7Z98m4EVLFI9tVsZDADC14AElFaNHHBcJIovflJ6wtCqFxwy2YJ1tJhGRsr0yLPKoN+w== dependencies: - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-module-transforms" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c" - integrity sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw== +"@babel/plugin-transform-named-capturing-groups-regex@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz#2213725a5f5bbbe364b50c3ba5998c9599c5c9d9" + integrity sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-create-regexp-features-plugin" "^7.12.13" -"@babel/plugin-transform-new-target@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.1.tgz#6ee41a5e648da7632e22b6fb54012e87f612f324" - integrity sha512-MBlzPc1nJvbmO9rPr1fQwXOM2iGut+JC92ku6PbiJMMK7SnQc1rytgpopveE3Evn47gzvGYeCdgfCDbZo0ecUw== +"@babel/plugin-transform-new-target@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz#e22d8c3af24b150dd528cbd6e685e799bf1c351c" + integrity sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-object-super@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.1.tgz#2e3016b0adbf262983bf0d5121d676a5ed9c4fde" - integrity sha512-WnnStUDN5GL+wGQrJylrnnVlFhFmeArINIR9gjhSeYyvroGhBrSAXYg/RHsnfzmsa+onJrTJrEClPzgNmmQ4Gw== +"@babel/plugin-transform-object-super@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz#b4416a2d63b8f7be314f3d349bd55a9c1b5171f7" + integrity sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-replace-supers" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-replace-supers" "^7.12.13" -"@babel/plugin-transform-parameters@^7.10.1", "@babel/plugin-transform-parameters@^7.9.5": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.1.tgz#b25938a3c5fae0354144a720b07b32766f683ddd" - integrity sha512-tJ1T0n6g4dXMsL45YsSzzSDZCxiHXAQp/qHrucOq5gEHncTA3xDxnd5+sZcoQp+N1ZbieAaB8r/VUCG0gqseOg== +"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.13.tgz#461e76dfb63c2dfd327b8a008a9e802818ce9853" + integrity sha512-e7QqwZalNiBRHCpJg/P8s/VJeSRYgmtWySs1JwvfwPqhBbiWfOcHDKdeAi6oAyIimoKWBlwc8oTgbZHdhCoVZA== dependencies: - "@babel/helper-get-function-arity" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-property-literals@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.1.tgz#cffc7315219230ed81dc53e4625bf86815b6050d" - integrity sha512-Kr6+mgag8auNrgEpbfIWzdXYOvqDHZOF0+Bx2xh4H2EDNwcbRb9lY6nkZg8oSjsX+DH9Ebxm9hOqtKW+gRDeNA== +"@babel/plugin-transform-property-literals@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz#4e6a9e37864d8f1b3bc0e2dce7bf8857db8b1a81" + integrity sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-regenerator@^7.8.7": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.1.tgz#10e175cbe7bdb63cc9b39f9b3f823c5c7c5c5490" - integrity sha512-B3+Y2prScgJ2Bh/2l9LJxKbb8C8kRfsG4AdPT+n7ixBHIxJaIG8bi8tgjxUMege1+WqSJ+7gu1YeoMVO3gPWzw== +"@babel/plugin-transform-regenerator@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.13.tgz#b628bcc9c85260ac1aeb05b45bde25210194a2f5" + integrity sha512-lxb2ZAvSLyJ2PEe47hoGWPmW22v7CtSl9jW8mingV4H2sEX/JOcrAj2nPuGWi56ERUm2bUpjKzONAuT6HCn2EA== dependencies: regenerator-transform "^0.14.2" -"@babel/plugin-transform-reserved-words@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.1.tgz#0fc1027312b4d1c3276a57890c8ae3bcc0b64a86" - integrity sha512-qN1OMoE2nuqSPmpTqEM7OvJ1FkMEV+BjVeZZm9V9mq/x1JLKQ4pcv8riZJMNN3u2AUGl0ouOMjRr2siecvHqUQ== +"@babel/plugin-transform-reserved-words@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz#7d9988d4f06e0fe697ea1d9803188aa18b472695" + integrity sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-runtime@7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.6.tgz#3ba804438ad0d880a17bca5eaa0cdf1edeedb2fd" - integrity sha512-qcmiECD0mYOjOIt8YHNsAP1SxPooC/rDmfmiSK9BNY72EitdSc7l44WTEklaWuFtbOEBjNhWWyph/kOImbNJ4w== +"@babel/plugin-transform-runtime@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.1.tgz#04b792057eb460389ff6a4198e377614ea1e7ba5" + integrity sha512-Ac/H6G9FEIkS2tXsZjL4RAdS3L3WHxci0usAnz7laPWUmFiGtj7tIASChqKZMHTSQTQY6xDbOq+V1/vIq3QrWg== dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" resolve "^1.8.1" semver "^5.5.1" -"@babel/plugin-transform-shorthand-properties@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.1.tgz#e8b54f238a1ccbae482c4dce946180ae7b3143f3" - integrity sha512-AR0E/lZMfLstScFwztApGeyTHJ5u3JUKMjneqRItWeEqDdHWZwAOKycvQNCasCK/3r5YXsuNG25funcJDu7Y2g== +"@babel/plugin-transform-shorthand-properties@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz#db755732b70c539d504c6390d9ce90fe64aff7ad" + integrity sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-spread@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.1.tgz#0c6d618a0c4461a274418460a28c9ccf5239a7c8" - integrity sha512-8wTPym6edIrClW8FI2IoaePB91ETOtg36dOkj3bYcNe7aDMN2FXEoUa+WrmPc4xa1u2PQK46fUX2aCb+zo9rfw== +"@babel/plugin-transform-spread@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.13.tgz#ca0d5645abbd560719c354451b849f14df4a7949" + integrity sha512-dUCrqPIowjqk5pXsx1zPftSq4sT0aCeZVAxhdgs3AMgyaDmoUT0G+5h3Dzja27t76aUEIJWlFgPJqJ/d4dbTtg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" -"@babel/plugin-transform-sticky-regex@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.1.tgz#90fc89b7526228bed9842cff3588270a7a393b00" - integrity sha512-j17ojftKjrL7ufX8ajKvwRilwqTok4q+BjkknmQw9VNHnItTyMP5anPFzxFJdCQs7clLcWpCV3ma+6qZWLnGMA== +"@babel/plugin-transform-sticky-regex@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz#760ffd936face73f860ae646fb86ee82f3d06d1f" + integrity sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-regex" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-template-literals@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.1.tgz#914c7b7f4752c570ea00553b4284dad8070e8628" - integrity sha512-t7B/3MQf5M1T9hPCRG28DNGZUuxAuDqLYS03rJrIk2prj/UV7Z6FOneijhQhnv/Xa039vidXeVbvjK2SK5f7Gg== +"@babel/plugin-transform-template-literals@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.13.tgz#655037b07ebbddaf3b7752f55d15c2fd6f5aa865" + integrity sha512-arIKlWYUgmNsF28EyfmiQHJLJFlAJNYkuQO10jL46ggjBpeb2re1P9K9YGxNJB45BqTbaslVysXDYm/g3sN/Qg== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-typeof-symbol@^7.8.4": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.1.tgz#60c0239b69965d166b80a84de7315c1bc7e0bb0e" - integrity sha512-qX8KZcmbvA23zDi+lk9s6hC1FM7jgLHYIjuLgULgc8QtYnmB3tAVIYkNoKRQ75qWBeyzcoMoK8ZQmogGtC/w0g== +"@babel/plugin-transform-typeof-symbol@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz#785dd67a1f2ea579d9c2be722de8c84cb85f5a7f" + integrity sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-unicode-regex@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.1.tgz#6b58f2aea7b68df37ac5025d9c88752443a6b43f" - integrity sha512-Y/2a2W299k0VIUdbqYm9X2qS6fE0CUBhhiPpimK6byy7OJ/kORLlIX+J6UrjgNu5awvs62k+6RSslxhcvVw2Tw== +"@babel/plugin-transform-unicode-escapes@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz#840ced3b816d3b5127dd1d12dcedc5dead1a5e74" + integrity sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/preset-env@7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.9.6.tgz#df063b276c6455ec6fcfc6e53aacc38da9b0aea6" - integrity sha512-0gQJ9RTzO0heXOhzftog+a/WyOuqMrAIugVYxMYf83gh1CQaQDjMtsOpqOwXyDL/5JcWsrCm8l4ju8QC97O7EQ== +"@babel/plugin-transform-unicode-regex@^7.12.1": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz#b52521685804e155b1202e83fc188d34bb70f5ac" + integrity sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA== dependencies: - "@babel/compat-data" "^7.9.6" - "@babel/helper-compilation-targets" "^7.9.6" - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-proposal-async-generator-functions" "^7.8.3" - "@babel/plugin-proposal-dynamic-import" "^7.8.3" - "@babel/plugin-proposal-json-strings" "^7.8.3" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-proposal-numeric-separator" "^7.8.3" - "@babel/plugin-proposal-object-rest-spread" "^7.9.6" - "@babel/plugin-proposal-optional-catch-binding" "^7.8.3" - "@babel/plugin-proposal-optional-chaining" "^7.9.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.8.3" + "@babel/helper-create-regexp-features-plugin" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/preset-env@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.12.1.tgz#9c7e5ca82a19efc865384bb4989148d2ee5d7ac2" + integrity sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg== + dependencies: + "@babel/compat-data" "^7.12.1" + "@babel/helper-compilation-targets" "^7.12.1" + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-validator-option" "^7.12.1" + "@babel/plugin-proposal-async-generator-functions" "^7.12.1" + "@babel/plugin-proposal-class-properties" "^7.12.1" + "@babel/plugin-proposal-dynamic-import" "^7.12.1" + "@babel/plugin-proposal-export-namespace-from" "^7.12.1" + "@babel/plugin-proposal-json-strings" "^7.12.1" + "@babel/plugin-proposal-logical-assignment-operators" "^7.12.1" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.12.1" + "@babel/plugin-proposal-numeric-separator" "^7.12.1" + "@babel/plugin-proposal-object-rest-spread" "^7.12.1" + "@babel/plugin-proposal-optional-catch-binding" "^7.12.1" + "@babel/plugin-proposal-optional-chaining" "^7.12.1" + "@babel/plugin-proposal-private-methods" "^7.12.1" + "@babel/plugin-proposal-unicode-property-regex" "^7.12.1" "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-class-properties" "^7.12.1" "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - "@babel/plugin-syntax-numeric-separator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" "@babel/plugin-syntax-optional-chaining" "^7.8.0" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - "@babel/plugin-transform-arrow-functions" "^7.8.3" - "@babel/plugin-transform-async-to-generator" "^7.8.3" - "@babel/plugin-transform-block-scoped-functions" "^7.8.3" - "@babel/plugin-transform-block-scoping" "^7.8.3" - "@babel/plugin-transform-classes" "^7.9.5" - "@babel/plugin-transform-computed-properties" "^7.8.3" - "@babel/plugin-transform-destructuring" "^7.9.5" - "@babel/plugin-transform-dotall-regex" "^7.8.3" - "@babel/plugin-transform-duplicate-keys" "^7.8.3" - "@babel/plugin-transform-exponentiation-operator" "^7.8.3" - "@babel/plugin-transform-for-of" "^7.9.0" - "@babel/plugin-transform-function-name" "^7.8.3" - "@babel/plugin-transform-literals" "^7.8.3" - "@babel/plugin-transform-member-expression-literals" "^7.8.3" - "@babel/plugin-transform-modules-amd" "^7.9.6" - "@babel/plugin-transform-modules-commonjs" "^7.9.6" - "@babel/plugin-transform-modules-systemjs" "^7.9.6" - "@babel/plugin-transform-modules-umd" "^7.9.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" - "@babel/plugin-transform-new-target" "^7.8.3" - "@babel/plugin-transform-object-super" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.9.5" - "@babel/plugin-transform-property-literals" "^7.8.3" - "@babel/plugin-transform-regenerator" "^7.8.7" - "@babel/plugin-transform-reserved-words" "^7.8.3" - "@babel/plugin-transform-shorthand-properties" "^7.8.3" - "@babel/plugin-transform-spread" "^7.8.3" - "@babel/plugin-transform-sticky-regex" "^7.8.3" - "@babel/plugin-transform-template-literals" "^7.8.3" - "@babel/plugin-transform-typeof-symbol" "^7.8.4" - "@babel/plugin-transform-unicode-regex" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.12.1" + "@babel/plugin-transform-arrow-functions" "^7.12.1" + "@babel/plugin-transform-async-to-generator" "^7.12.1" + "@babel/plugin-transform-block-scoped-functions" "^7.12.1" + "@babel/plugin-transform-block-scoping" "^7.12.1" + "@babel/plugin-transform-classes" "^7.12.1" + "@babel/plugin-transform-computed-properties" "^7.12.1" + "@babel/plugin-transform-destructuring" "^7.12.1" + "@babel/plugin-transform-dotall-regex" "^7.12.1" + "@babel/plugin-transform-duplicate-keys" "^7.12.1" + "@babel/plugin-transform-exponentiation-operator" "^7.12.1" + "@babel/plugin-transform-for-of" "^7.12.1" + "@babel/plugin-transform-function-name" "^7.12.1" + "@babel/plugin-transform-literals" "^7.12.1" + "@babel/plugin-transform-member-expression-literals" "^7.12.1" + "@babel/plugin-transform-modules-amd" "^7.12.1" + "@babel/plugin-transform-modules-commonjs" "^7.12.1" + "@babel/plugin-transform-modules-systemjs" "^7.12.1" + "@babel/plugin-transform-modules-umd" "^7.12.1" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.12.1" + "@babel/plugin-transform-new-target" "^7.12.1" + "@babel/plugin-transform-object-super" "^7.12.1" + "@babel/plugin-transform-parameters" "^7.12.1" + "@babel/plugin-transform-property-literals" "^7.12.1" + "@babel/plugin-transform-regenerator" "^7.12.1" + "@babel/plugin-transform-reserved-words" "^7.12.1" + "@babel/plugin-transform-shorthand-properties" "^7.12.1" + "@babel/plugin-transform-spread" "^7.12.1" + "@babel/plugin-transform-sticky-regex" "^7.12.1" + "@babel/plugin-transform-template-literals" "^7.12.1" + "@babel/plugin-transform-typeof-symbol" "^7.12.1" + "@babel/plugin-transform-unicode-escapes" "^7.12.1" + "@babel/plugin-transform-unicode-regex" "^7.12.1" "@babel/preset-modules" "^0.1.3" - "@babel/types" "^7.9.6" - browserslist "^4.11.1" + "@babel/types" "^7.12.1" core-js-compat "^3.6.2" - invariant "^2.2.2" - levenary "^1.1.1" semver "^5.5.0" "@babel/preset-modules@^0.1.3": @@ -1132,10 +1216,10 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/runtime@7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f" - integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ== +"@babel/runtime@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.1.tgz#b4116a6b6711d010b2dad3b7b6e43bf1b9954740" + integrity sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA== dependencies: regenerator-runtime "^0.13.4" @@ -1146,14 +1230,14 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/template@7.8.6": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" - integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== +"@babel/template@7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" + integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/parser" "^7.8.6" - "@babel/types" "^7.8.6" + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" "@babel/template@^7.1.0", "@babel/template@^7.4.0": version "7.6.0" @@ -1164,14 +1248,14 @@ "@babel/parser" "^7.6.0" "@babel/types" "^7.6.0" -"@babel/template@^7.10.1", "@babel/template@^7.8.6": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" - integrity sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig== +"@babel/template@^7.10.4", "@babel/template@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" + integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== dependencies: - "@babel/code-frame" "^7.10.1" - "@babel/parser" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/code-frame" "^7.12.13" + "@babel/parser" "^7.12.13" + "@babel/types" "^7.12.13" "@babel/template@^7.8.3": version "7.8.3" @@ -1182,20 +1266,20 @@ "@babel/parser" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/traverse@^7.10.1", "@babel/traverse@^7.9.6": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.1.tgz#bbcef3031e4152a6c0b50147f4958df54ca0dd27" - integrity sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ== +"@babel/traverse@^7.12.1", "@babel/traverse@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.13.tgz#689f0e4b4c08587ad26622832632735fb8c4e0c0" + integrity sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA== dependencies: - "@babel/code-frame" "^7.10.1" - "@babel/generator" "^7.10.1" - "@babel/helper-function-name" "^7.10.1" - "@babel/helper-split-export-declaration" "^7.10.1" - "@babel/parser" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.12.13" + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/parser" "^7.12.13" + "@babel/types" "^7.12.13" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.13" + lodash "^4.17.19" "@babel/traverse@^7.4.3": version "7.6.2" @@ -1236,7 +1320,7 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@^7.10.1", "@babel/types@^7.10.2", "@babel/types@^7.8.6", "@babel/types@^7.9.6": +"@babel/types@^7.10.1", "@babel/types@^7.8.6": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.2.tgz#30283be31cad0dbf6fb00bd40641ca0ea675172d" integrity sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng== @@ -1245,6 +1329,15 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@babel/types@^7.10.4", "@babel/types@^7.12.1", "@babel/types@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.13.tgz#8be1aa8f2c876da11a9cf650c0ecf656913ad611" + integrity sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@babel/types@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" @@ -1259,56 +1352,46 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== -"@jsdevtools/coverage-istanbul-loader@3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.3.tgz#102e414b02ae2f0b3c7fd45a705601e1fd4867c5" - integrity sha512-TAdNkeGB5Fe4Og+ZkAr1Kvn9by2sfL44IAHFtxlh1BA1XJ5cLpO9iSNki5opWESv3l3vSHsZ9BNKuqFKbEbFaA== +"@jsdevtools/coverage-istanbul-loader@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz#2a4bc65d0271df8d4435982db4af35d81754ee26" + integrity sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA== dependencies: convert-source-map "^1.7.0" - istanbul-lib-instrument "^4.0.1" - loader-utils "^1.4.0" + istanbul-lib-instrument "^4.0.3" + loader-utils "^2.0.0" merge-source-map "^1.1.0" - schema-utils "^2.6.4" + schema-utils "^2.7.0" -"@ngtools/webpack@10.0.0-rc.2": - version "10.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-10.0.0-rc.2.tgz#3abecf33a7710550e8ab7a6cb39ded291f495a70" - integrity sha512-ALtsFeLmfxpJnc5XvItJRJt7zDI+ggOWF8dMeuYdNIHny8w+EXtZ57h3iB6s9AE9ig9GY/n12Ax4L9OYS4VK5A== +"@ngtools/webpack@11.0.0-rc.1": + version "11.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-11.0.0-rc.1.tgz#61015463ca0d1cb93c8a50f3835fa243a753168b" + integrity sha512-asWV2fwYPmCWq1V5VnCIGD3t2f/UHFwAcauN3b5/iZxmQodXZJxhjKJAaUeCNRawKhxIiEIvfTclShJ6ii7X1w== dependencies: - "@angular-devkit/core" "10.0.0-rc.2" - enhanced-resolve "4.1.1" - rxjs "6.5.5" - webpack-sources "1.4.3" + "@angular-devkit/core" "11.0.0-rc.1" + enhanced-resolve "5.3.1" + webpack-sources "2.0.1" -"@npmcli/ci-detect@^1.0.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@npmcli/ci-detect/-/ci-detect-1.2.0.tgz#0df142a1ac3bba6cbf2e9da1a6994cd898e32c95" - integrity sha512-JtktVH7ASBVIWsQTFlFpeOzhBJskvoBCTfeeRhhZy7ybATcUvwiwotZ8j5rkqUUyB69lIy/AvboiiiGBjYBKBA== - -"@npmcli/git@^2.0.1": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-2.0.2.tgz#41d75caf59e4799c62b354a3e4eed3b0e64310c0" - integrity sha512-uv9+EuP5YWluNPgkEOL+iyB/+MVt4U5PMBCfl+I8korKluFdiSp7RxjXYzpWM/wU4wXaROAUFiOiCMmBftonjw== +"@nodelib/fs.scandir@2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" + integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA== dependencies: - "@npmcli/promise-spawn" "^1.1.0" - lru-cache "^5.1.1" - mkdirp "^1.0.3" - npm-pick-manifest "^6.0.0" - promise-inflight "^1.0.1" - promise-retry "^1.1.1" - semver "^7.3.2" - unique-filename "^1.1.1" - which "^2.0.2" + "@nodelib/fs.stat" "2.0.4" + run-parallel "^1.1.9" -"@npmcli/installed-package-contents@^1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.5.tgz#cc78565e55d9f14d46acf46a96f70934e516fa3d" - integrity sha512-aKIwguaaqb6ViwSOFytniGvLPb9SMCUm39TgM3SfUo7n0TxUMbwoXfpwyvQ4blm10lzbAwTsvjr7QZ85LvTi4A== +"@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655" + integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063" + integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow== dependencies: - npm-bundled "^1.1.1" - npm-normalize-package-bin "^1.0.1" - read-package-json-fast "^1.1.1" - readdir-scoped-modules "^1.1.0" + "@nodelib/fs.scandir" "2.1.4" + fastq "^1.6.0" "@npmcli/move-file@^1.0.1": version "1.0.1" @@ -1317,41 +1400,29 @@ dependencies: mkdirp "^1.0.4" -"@npmcli/promise-spawn@^1.1.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.2.0.tgz#167d70b926f771c8bd8b9183bfc8b5aec29d7e45" - integrity sha512-nFtqjVETliApiRdjbYwKwhlSHx2ZMagyj5b9YbNt0BWeeOVxJd47ZVE2u16vxDHyTOZvk+YLV7INwfAE9a2uow== +"@schematics/angular@11.0.0-rc.1": + version "11.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-11.0.0-rc.1.tgz#53f58643a80f0f13f3f12237600818fea3faf552" + integrity sha512-gZgUhw7+9aCM7O8+hhavf2Dl5dDXbljVRuSFeek1AWb1joo55w4WtFqI7y6Qrre82ri803V40kP6tI2z2lD+Nw== dependencies: - infer-owner "^1.0.4" + "@angular-devkit/core" "11.0.0-rc.1" + "@angular-devkit/schematics" "11.0.0-rc.1" + jsonc-parser "2.3.1" -"@schematics/angular@9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-9.1.0.tgz#fc0ad9099d3c3be3044d6b3f260cac6eb9f3e564" - integrity sha512-qkehaITQ1S1udfnnBY5CXGWnk1iVFI8cZayjLUlRfD5w+6v9if3VIuqPssX96MqvkbjyRu1N214+ieaawzLmuA== +"@schematics/update@0.1100.0-rc.1": + version "0.1100.0-rc.1" + resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1100.0-rc.1.tgz#51b9cf48891f7165992074477466d15f2f41a4a9" + integrity sha512-ZJseWJxTelTwQ6/2CMaNW9gQffmdo2mJiDyBFrvjwyqcMNVIH2tpIlDLyvhQ3duBWheYDHYAWLZOh5wiJPks9A== dependencies: - "@angular-devkit/core" "9.1.0" - "@angular-devkit/schematics" "9.1.0" - -"@schematics/update@0.901.0": - version "0.901.0" - resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.901.0.tgz#3e08231354f2c414c7d5b3bde1d9f7c08c664a74" - integrity sha512-u2VESL1dgOSGZK/wcWEz0WcCU/yv764zhzCQerCwUtbV1CISSSDZ6x+prVYDXOdxWBGtDos2MbCF3GEJJI1T+w== - dependencies: - "@angular-devkit/core" "9.1.0" - "@angular-devkit/schematics" "9.1.0" + "@angular-devkit/core" "11.0.0-rc.1" + "@angular-devkit/schematics" "11.0.0-rc.1" "@yarnpkg/lockfile" "1.1.0" ini "1.3.5" npm-package-arg "^8.0.0" - pacote "11.1.4" - rxjs "6.5.4" - semver "7.1.3" + pacote "9.5.12" + semver "7.3.2" semver-intersect "1.4.0" -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== - "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -1376,13 +1447,11 @@ resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.4.2.tgz#49f672de24043b3c1fb919901fd3cd36f027bc93" integrity sha512-SaSSGOzwUnBEn64c+HTyVTJhRf8F1CXZLnxYx2ww3UrgGBmEEw38RSux2l3fYiT9brVLP67DU5omWA6V9OHI5Q== -"@types/jasmine@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/types_jasmine_archive.tar.gz": +"@types/jasmine@file:../../node_modules/@types/jasmine": version "3.5.10" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/types_jasmine_archive.tar.gz#ca48d30e927e65a202c112ad650224b451180394" -"@types/jasminewd2@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/types_jasminewd2_archive.tar.gz": +"@types/jasminewd2@file:../../node_modules/@types/jasminewd2": version "2.0.8" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/types_jasminewd2_archive.tar.gz#c85feba063048c94779ecb1532291c239861fbf2" dependencies: "@types/jasmine" "*" @@ -1391,6 +1460,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== +"@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6": + version "7.0.7" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" + integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -1401,9 +1475,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.11.tgz#be879b52031cfb5d295b047f5462d8ef1a716446" integrity sha512-Otxmr2rrZLKRYIybtdG/sgeO+tHY20GxeDjcGmUnmmlCWyEnv2a2x1ZXBo3BTec4OiTXMQCiazB8NMBf0iRlFw== -"@types/node@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/types_node_archive.tar.gz": +"@types/node@file:../../node_modules/@types/node": version "12.12.34" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/types_node_archive.tar.gz#a11f7eb01f5edd2e2150c069bcf1314d591cf50d" + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/q@^0.0.32": version "0.0.32" @@ -1606,10 +1684,18 @@ resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.6.0.tgz#004e8e553b4cd53d538bd38eac7bcbf58a867fe3" integrity sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg== -abab@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" - integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== +JSONStream@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +abab@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== abbrev@1: version "1.1.1" @@ -1629,16 +1715,13 @@ acorn@^6.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== -adjust-sourcemap-loader@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-2.0.0.tgz#6471143af75ec02334b219f54bc7970c52fb29a4" - integrity sha512-4hFsTsn58+YjrU9qKzML2JSSDqKvN8mUGQ0nNIrfPi8hmIONT4L3uUaT6MKdMsZ9AjsU6D2xDkZxCkbQPxChrA== +adjust-sourcemap-loader@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz#5ae12fb5b7b1c585e80bbb5a63ec163a1a45e61e" + integrity sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw== dependencies: - assert "1.4.1" - camelcase "5.0.0" - loader-utils "1.2.3" - object-path "0.11.4" - regex-parser "2.2.10" + loader-utils "^2.0.0" + regex-parser "^2.2.11" adm-zip@^0.4.9: version "0.4.13" @@ -1650,32 +1733,30 @@ after@0.8.2: resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= -agent-base@5: - version "5.1.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" - integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== - -agent-base@6: - version "6.0.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.0.tgz#5d0101f19bbfaed39980b22ae866de153b93f09a" - integrity sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw== - dependencies: - debug "4" - -agent-base@^4.3.0: +agent-base@4, agent-base@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== dependencies: es6-promisify "^5.0.0" -agentkeepalive@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.1.3.tgz#360a09d743a1f4fde749f9ba07caa6575d08259a" - integrity sha512-wn8fw19xKZwdGPO47jivonaHRTd+nGOMP1z11sgGeQzDy2xd5FG0R67dIMcKHDE2cJ5y+YXV30XVGUBPRSY7Hg== +agent-base@5: + version "5.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" + integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== + +agent-base@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== + dependencies: + es6-promisify "^5.0.0" + +agentkeepalive@^3.4.1: + version "3.5.2" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67" + integrity sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ== dependencies: - debug "^4.1.0" - depd "^1.1.2" humanize-ms "^1.2.1" aggregate-error@^3.0.0: @@ -1696,20 +1777,15 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== -ajv@6.12.0: - version "6.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7" - integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@6.12.2, ajv@^6.12.2: - version "6.12.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" - integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== +ajv@6.12.6, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" @@ -1736,6 +1812,16 @@ ajv@^6.1.0, ajv@^6.10.2, ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^6.12.2: + version "6.12.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" + integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -1918,6 +2004,11 @@ array-union@^1.0.1: dependencies: array-uniq "^1.0.1" +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + array-uniq@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" @@ -1938,11 +2029,6 @@ arrify@^1.0.0: resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= -asap@^2.0.0, asap@~2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= - asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" @@ -1964,13 +2050,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= -assert@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" - integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE= - dependencies: - util "0.10.3" - assert@^1.1.1: version "1.5.0" resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" @@ -2016,17 +2095,17 @@ atob@^2.1.1: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@9.8.0: - version "9.8.0" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.0.tgz#68e2d2bef7ba4c3a65436f662d0a56a741e56511" - integrity sha512-D96ZiIHXbDmU02dBaemyAg53ez+6F5yZmapmgKcjm35yEe1uVDYI8hGW3VYoGRaG290ZFf91YxHrR518vC0u/A== +autoprefixer@9.8.6: + version "9.8.6" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" + integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== dependencies: browserslist "^4.12.0" - caniuse-lite "^1.0.30001061" - chalk "^2.4.2" + caniuse-lite "^1.0.30001109" + colorette "^1.2.1" normalize-range "^0.1.2" num2fraction "^1.2.2" - postcss "^7.0.30" + postcss "^7.0.32" postcss-value-parser "^4.1.0" aws-sign2@~0.7.0: @@ -2162,6 +2241,11 @@ bluebird@^3.3.0, bluebird@^3.5.5: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.0.tgz#56a6a886e03f6ae577cffedeb524f8f2450293cf" integrity sha512-aBQ1FxIa7kSWCcmKHlcHFlT2jt6J/l4FzC7KcPELkOJOsPOb/bccdhmIrKDfXhwFrmc7vDoDrrepFvGqjyXGJg== +bluebird@^3.5.1, bluebird@^3.5.3: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" @@ -2237,7 +2321,7 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.2, braces@~3.0.2: +braces@^3.0.1, braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -2317,7 +2401,7 @@ browserslist@^4.0.0: electron-to-chromium "^1.3.341" node-releases "^1.1.47" -browserslist@^4.11.1, browserslist@^4.12.0, browserslist@^4.8.5, browserslist@^4.9.1: +browserslist@^4.12.0, browserslist@^4.8.5, browserslist@^4.9.1: version "4.12.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.12.0.tgz#06c6d5715a1ede6c51fc39ff67fd647f740b656d" integrity sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg== @@ -2327,6 +2411,17 @@ browserslist@^4.11.1, browserslist@^4.12.0, browserslist@^4.8.5, browserslist@^4 node-releases "^1.1.53" pkg-up "^2.0.0" +browserslist@^4.14.5: + version "4.16.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" + integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== + dependencies: + caniuse-lite "^1.0.30001181" + colorette "^1.2.1" + electron-to-chromium "^1.3.649" + escalade "^3.1.1" + node-releases "^1.1.70" + browserstack@^1.5.1: version "1.5.3" resolved "https://registry.yarnpkg.com/browserstack/-/browserstack-1.5.3.tgz#93ab48799a12ef99dbd074dd595410ddb196a7ac" @@ -2414,22 +2509,22 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -cacache@15.0.3: - version "15.0.3" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.3.tgz#2225c2d1dd8e872339950d6a39c051e0e9334392" - integrity sha512-bc3jKYjqv7k4pWh7I/ixIjfcjPul4V4jme/WbjvwGS5LzoPL/GzXr4C5EgPNLO/QEZl9Oi61iGitYEdwcrwLCQ== +cacache@15.0.5, cacache@^15.0.5: + version "15.0.5" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" + integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A== dependencies: + "@npmcli/move-file" "^1.0.1" chownr "^2.0.0" fs-minipass "^2.0.0" glob "^7.1.4" infer-owner "^1.0.4" - lru-cache "^5.1.1" + lru-cache "^6.0.0" minipass "^3.1.1" minipass-collect "^1.0.2" minipass-flush "^1.0.5" minipass-pipeline "^1.2.2" mkdirp "^1.0.3" - move-file "^2.0.0" p-map "^4.0.0" promise-inflight "^1.0.1" rimraf "^3.0.2" @@ -2437,6 +2532,27 @@ cacache@15.0.3: tar "^6.0.2" unique-filename "^1.1.1" +cacache@^12.0.0: + version "12.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" + integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + infer-owner "^1.0.3" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + cacache@^12.0.2: version "12.0.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390" @@ -2458,50 +2574,6 @@ cacache@^12.0.2: unique-filename "^1.1.1" y18n "^4.0.0" -cacache@^12.0.3: - version "12.0.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" - integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== - dependencies: - bluebird "^3.5.5" - chownr "^1.1.1" - figgy-pudding "^3.5.1" - glob "^7.1.4" - graceful-fs "^4.1.15" - infer-owner "^1.0.3" - lru-cache "^5.1.1" - mississippi "^3.0.0" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - promise-inflight "^1.0.1" - rimraf "^2.6.3" - ssri "^6.0.1" - unique-filename "^1.1.1" - y18n "^4.0.0" - -cacache@^15.0.0, cacache@^15.0.3: - version "15.0.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.4.tgz#b2c23cf4ac4f5ead004fb15a0efb0a20340741f1" - integrity sha512-YlnKQqTbD/6iyoJvEY3KJftjrdBYroCbxxYXzhOzsFLWlp6KX4BOlEf4mTx0cMUfVaTS3ENL2QtDWeRYoGLkkw== - dependencies: - "@npmcli/move-file" "^1.0.1" - chownr "^2.0.0" - fs-minipass "^2.0.0" - glob "^7.1.4" - infer-owner "^1.0.4" - lru-cache "^5.1.1" - minipass "^3.1.1" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^1.0.3" - p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^8.0.0" - tar "^6.0.2" - unique-filename "^1.1.1" - cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -2541,12 +2613,12 @@ callsites@^2.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= -camelcase@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" - integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camelcase@5.3.1, camelcase@^5.0.0, camelcase@^5.3.1: +camelcase@5.3.1, camelcase@^5.0.0: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== @@ -2556,6 +2628,11 @@ camelcase@^4.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= +camelcase@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + caniuse-api@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" @@ -2571,11 +2648,16 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001023: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001023.tgz#b82155827f3f5009077bdd2df3d8968bcbcc6fc4" integrity sha512-C5TDMiYG11EOhVOA62W1p3UsJ2z4DsHtMBQtjzp3ZsUglcQn62WOUgW0y795c7A5uZ+GCEIvzkMatLIlAsbNTA== -caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001043, caniuse-lite@^1.0.30001061: +caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001043: version "1.0.30001083" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001083.tgz#52410c20c6f029f604f0d45eca0439a82e712442" integrity sha512-CnYJ27awX4h7yj5glfK7r1TOI13LBytpLzEgfj0s4mY75/F8pnQcYjL+oVpmS38FB59+vU0gscQ9D8tc+lIXvA== +caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001181: + version "1.0.30001185" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001185.tgz#3482a407d261da04393e2f0d61eefbc53be43b95" + integrity sha512-Fpi4kVNtNvJ15H0F6vwmXtb3tukv3Zg3qhKkOGUq7KJ1J6b9kf4dnNgtEAFXhRsJo0gNj9W60+wBvn0JcTvdTg== + canonical-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-1.0.0.tgz#fcb470c23958def85081856be7a86e904f180d1d" @@ -2615,10 +2697,10 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" @@ -2677,10 +2759,10 @@ chokidar@^3.0.0: optionalDependencies: fsevents "~2.1.1" -chokidar@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.0.tgz#b30611423ce376357c765b9b8f904b9fba3c0be8" - integrity sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ== +chokidar@^3.4.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" + integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== dependencies: anymatch "~3.1.1" braces "~3.0.2" @@ -2688,16 +2770,16 @@ chokidar@^3.4.0: is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.4.0" + readdirp "~3.5.0" optionalDependencies: - fsevents "~2.1.2" + fsevents "~2.3.1" chownr@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== -chownr@^1.1.4: +chownr@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== @@ -2754,15 +2836,15 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-spinners@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77" - integrity sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ== +cli-spinners@^2.4.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.5.0.tgz#12763e47251bf951cb75c201dfa58ff1bcb2d047" + integrity sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ== -cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" - integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== clipboardy@1.2.3: version "1.2.3" @@ -2781,14 +2863,14 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" + wrap-ansi "^7.0.0" clone-deep@^4.0.1: version "4.0.1" @@ -2804,11 +2886,6 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= -clone@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= - coa@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" @@ -2886,6 +2963,11 @@ color@^3.0.0: color-convert "^1.9.1" color-string "^1.5.2" +colorette@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" + integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== + colors@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" @@ -3103,23 +3185,22 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -copy-webpack-plugin@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz#5481a03dea1123d88a988c6ff8b78247214f0b88" - integrity sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg== +copy-webpack-plugin@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.2.1.tgz#8015e4d5c5e637ab7b39c76daa9e03c7a4bf1ae5" + integrity sha512-VH2ZTMIBsx4p++Lmpg77adZ0KUyM5gFR/9cuTrbneNnJlcQXUFvsNariPqq2dq2kV3F2skHiDGPQCyKWy1+U0Q== dependencies: - cacache "^12.0.3" - find-cache-dir "^2.1.0" - glob-parent "^3.1.0" - globby "^7.1.1" - is-glob "^4.0.1" - loader-utils "^1.2.3" - minimatch "^3.0.4" + cacache "^15.0.5" + fast-glob "^3.2.4" + find-cache-dir "^3.3.1" + glob-parent "^5.1.1" + globby "^11.0.1" + loader-utils "^2.0.0" normalize-path "^3.0.0" - p-limit "^2.2.1" - schema-utils "^1.0.0" - serialize-javascript "^2.1.2" - webpack-log "^2.0.0" + p-limit "^3.0.2" + schema-utils "^3.0.0" + serialize-javascript "^5.0.1" + webpack-sources "^1.4.3" core-js-compat@^3.6.2: version "3.6.5" @@ -3129,19 +3210,10 @@ core-js-compat@^3.6.2: browserslist "^4.8.5" semver "7.0.0" -core-js@3.6.4: - version "3.6.4" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647" - integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw== - -core-js@^3.1.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.3.3.tgz#b7048d3c6c1a52b5fe55a729c1d4ccdffe0891bb" - integrity sha512-0xmD4vUJRY8nfLyV9zcpC17FtSie5STXzw+HyYw2t8IIvmDnbq7RJUULECCo+NstpJtwK9kx8S+898iyqgeUow== - -"core-js@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/core-js_archive.tar.gz": - version "2.6.11" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/core-js_archive.tar.gz#eb1e4b03600cc04f9f034cefb62586b91fc2a88b" +core-js@3.6.5: + version "3.6.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" + integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -3158,6 +3230,17 @@ cosmiconfig@^5.0.0: js-yaml "^3.13.1" parse-json "^4.0.0" +cosmiconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" + integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + create-ecdh@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" @@ -3239,24 +3322,23 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" -css-loader@3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.5.3.tgz#95ac16468e1adcd95c844729e0bb167639eb0bcf" - integrity sha512-UEr9NH5Lmi7+dguAm+/JSPovNjYbm2k3TK58EiwQHzOHH5Jfq1Y+XoP2bQO6TMn7PptMd0opxxedAWcaSTRKHw== +css-loader@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.0.0.tgz#f0a48dfacc3ab9936a05ee16a09e7f313872e117" + integrity sha512-9g35eXRBgjvswyJWoqq/seWp+BOxvUl8IinVNTsUBFFxtwfEYvlmEn6ciyn0liXGbGh5HyJjPGCuobDSfqMIVg== dependencies: - camelcase "^5.3.1" + camelcase "^6.1.0" cssesc "^3.0.0" - icss-utils "^4.1.1" - loader-utils "^1.2.3" - normalize-path "^3.0.0" - postcss "^7.0.27" - postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^3.0.2" - postcss-modules-scope "^2.2.0" - postcss-modules-values "^3.0.0" - postcss-value-parser "^4.0.3" - schema-utils "^2.6.6" - semver "^6.3.0" + icss-utils "^5.0.0" + loader-utils "^2.0.0" + postcss "^8.1.1" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.1.0" + schema-utils "^3.0.0" + semver "^7.3.2" css-parse@~2.0.0: version "2.0.0" @@ -3444,15 +3526,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-urls@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" - integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== - dependencies: - abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" - date-format@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf" @@ -3470,13 +3543,27 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@4.1.1, debug@^4.1.0, debug@^4.1.1: +debug@3.1.0, debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@4, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== dependencies: ms "^2.1.1" +debug@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + dependencies: + ms "2.1.2" + debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" @@ -3484,18 +3571,6 @@ debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: dependencies: ms "^2.1.1" -debug@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - -debuglog@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" - integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= - decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -3610,7 +3685,7 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -depd@^1.1.2, depd@~1.1.2: +depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= @@ -3643,13 +3718,10 @@ detect-node@^2.0.4: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== -dezalgo@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" - integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY= - dependencies: - asap "^2.0.0" - wrappy "1" +devtools-protocol@0.0.809251: + version "0.0.809251" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.809251.tgz#300b3366be107d5c46114ecb85274173e3999518" + integrity sha512-pf+2OY6ghMDPjKkzSWxHMq+McD+9Ojmq5XVRYpv/kPd9sTMQxzEt21592a31API8qRjro0iYYOc3ag46qF/1FA== di@^0.0.1: version "0.0.1" @@ -3675,12 +3747,12 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" -dir-glob@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" - integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: - path-type "^3.0.0" + path-type "^4.0.0" dns-equal@^1.0.0: version "1.0.0" @@ -3783,6 +3855,11 @@ electron-to-chromium@^1.3.413: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.473.tgz#d0cd5fe391046fb70674ec98149f0f97609d29b8" integrity sha512-smevlzzMNz3vMz6OLeeCq5HRWEj2AcgccNPYnAx4Usx0IOciq9DU36RJcICcS09hXoY7t7deRfVYKD14IrGb9A== +electron-to-chromium@^1.3.649: + version "1.3.657" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.657.tgz#a9c307f2612681245738bb8d36d997cbb568d481" + integrity sha512-/9ROOyvEflEbaZFUeGofD+Tqs/WynbSTbNgNF+/TJJxH1ePD/e6VjZlDJpW3FFFd3nj5l3Hd8ki2vRwy+gyRFw== + elliptic@^6.0.0: version "6.5.1" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.1.tgz#c380f5f909bf1b9b4428d028cd18d3b0efd6b52b" @@ -3821,12 +3898,12 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -encoding@^0.1.12: - version "0.1.12" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" - integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s= +encoding@^0.1.11: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== dependencies: - iconv-lite "~0.4.13" + iconv-lite "^0.6.2" end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" @@ -3875,10 +3952,18 @@ engine.io@~3.2.0: engine.io-parser "~2.1.0" ws "~3.3.1" -enhanced-resolve@4.1.1, enhanced-resolve@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66" - integrity sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA== +enhanced-resolve@5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.3.1.tgz#3f988d0d7775bdc2d96ede321dc81f8249492f57" + integrity sha512-G1XD3MRGrGfNcf6Hg0LVZG7GIKcYkbfHa5QMxt1HDUTdYoXH0JR1xXyg+MaKLF73E9A27uWNVxvFivNRYeUB6w== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.0.0" + +enhanced-resolve@^4.3.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" + integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== dependencies: graceful-fs "^4.1.2" memory-fs "^0.5.0" @@ -4002,6 +4087,11 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3: d "^1.0.1" ext "^1.1.2" +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -4246,6 +4336,18 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-glob@^3.1.1, fast-glob@^3.2.4: + version "3.2.5" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" + integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" + fast-json-stable-stringify@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -4268,6 +4370,13 @@ fastparse@^1.1.1: resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== +fastq@^1.6.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.10.1.tgz#8b8f2ac8bf3632d67afcd65dac248d5fdc45385e" + integrity sha512-AWuv6Ery3pM+dY7LYS8YIaCiQvUaos9OB1RyNgaOWnaX+Tik7Onvcsf8x8c+YtDeT0maYLniBip2hox5KtEXXA== + dependencies: + reusify "^1.0.4" + faye-websocket@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" @@ -4289,6 +4398,11 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +figgy-pudding@^3.4.1: + version "3.5.2" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" + integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== + figgy-pudding@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" @@ -4301,13 +4415,13 @@ figures@^3.0.0: dependencies: escape-string-regexp "^1.0.5" -file-loader@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.0.0.tgz#97bbfaab7a2460c07bcbd72d3a6922407f67649f" - integrity sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ== +file-loader@6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.1.1.tgz#a6f29dfb3f5933a1c350b2dbaa20ac5be0539baa" + integrity sha512-Klt8C4BjWSXYQAfhpYYkG4qHNTna4toMHEbWrI5IuVoxbU6uiDKeKAP99R8mmbJi3lvewn/jQBOgU4+NS3tDQw== dependencies: loader-utils "^2.0.0" - schema-utils "^2.6.5" + schema-utils "^3.0.0" fileset@^2.0.3: version "2.0.3" @@ -4379,7 +4493,7 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^4.0.0, find-up@^4.1.0: +find-up@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -4488,13 +4602,6 @@ fs-minipass@^2.0.0: dependencies: minipass "^3.0.0" -fs-minipass@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" @@ -4533,6 +4640,11 @@ fsevents@~2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.0.2, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -4552,12 +4664,17 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" +genfun@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537" + integrity sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA== + gensync@^1.0.0-beta.1: version "1.0.0-beta.1" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -4567,7 +4684,7 @@ get-stream@^3.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= -get-stream@^4.0.0: +get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== @@ -4601,6 +4718,13 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" +glob-parent@^5.1.0, glob-parent@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + glob-parent@~5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" @@ -4620,7 +4744,7 @@ glob@7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.1.6, glob@^7.1.2, glob@^7.1.6: +glob@7.1.6, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -4649,6 +4773,18 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globby@^11.0.1: + version "11.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.2.tgz#1af538b766a3b540ebfb58a32b2e2d5897321d83" + integrity sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + globby@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" @@ -4672,23 +4808,16 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -globby@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" - integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA= - dependencies: - array-union "^1.0.1" - dir-glob "^2.0.0" - glob "^7.1.2" - ignore "^3.3.5" - pify "^3.0.0" - slash "^1.0.0" - graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6: version "4.2.2" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== +graceful-fs@^4.2.4: + version "4.2.5" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.5.tgz#bc18864a6c9fc7b303f2e2abdb9155ad178fbe29" + integrity sha512-kBBSQbz2K0Nyn+31j/w36fUfxkBW9/gfwRWdUY1ULReH3iokVJgddZAFcD1D0xlgTmFxJCbUkUclAlc6/IDJkw== + handle-thing@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" @@ -4718,6 +4847,14 @@ har-validator@~5.1.0: ajv "^6.5.5" har-schema "^2.0.0" +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -4835,6 +4972,11 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== +hosted-git-info@^2.7.1: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + hosted-git-info@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.2.tgz#8b7e3bd114b59b51786f8bade0f39ddc80275a97" @@ -4842,6 +4984,13 @@ hosted-git-info@^3.0.2: dependencies: lru-cache "^5.1.1" +hosted-git-info@^3.0.6: + version "3.0.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.8.tgz#6e35d4cc87af2c5f816e4cb9ce350ba87a3f370d" + integrity sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw== + dependencies: + lru-cache "^6.0.0" + hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" @@ -4872,10 +5021,10 @@ html-entities@^1.3.1: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44" integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA== -http-cache-semantics@^4.0.4: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== +http-cache-semantics@^3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" + integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== http-deceiver@^1.2.7: version "1.2.7" @@ -4919,14 +5068,13 @@ http-errors@~1.7.2: resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q= -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" + agent-base "4" + debug "3.1.0" http-proxy-middleware@0.19.1: version "0.19.1" @@ -4969,6 +5117,14 @@ https-proxy-agent@^2.2.1: agent-base "^4.3.0" debug "^3.1.0" +https-proxy-agent@^2.2.3: + version "2.2.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" + integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + https-proxy-agent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b" @@ -4977,14 +5133,6 @@ https-proxy-agent@^4.0.0: agent-base "5" debug "4" -https-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== - dependencies: - agent-base "6" - debug "4" - humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -4992,26 +5140,24 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.2.tgz#af6d628dccfb463b7364d97f715e4b74b8c8c2b8" - integrity sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag== +iconv-lite@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01" + integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ== dependencies: - safer-buffer ">= 2.1.2 < 3" + safer-buffer ">= 2.1.2 < 3.0.0" -icss-utils@^4.0.0, icss-utils@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" - integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== - dependencies: - postcss "^7.0.14" +icss-utils@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== ieee754@^1.1.4: version "1.1.13" @@ -5030,17 +5176,10 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" -ignore-walk@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" - integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== - dependencies: - minimatch "^3.0.4" - -ignore@^3.3.5: - version "3.3.10" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" - integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== +ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== image-size@~0.5.0: version "0.5.5" @@ -5052,13 +5191,6 @@ immediate@~3.0.5: resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= -import-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" - integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk= - dependencies: - import-from "^2.1.0" - import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" @@ -5067,12 +5199,13 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-from@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" - integrity sha1-M1238qev/VOqpHHUuAId7ja387E= +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: - resolve-from "^3.0.0" + parent-module "^1.0.0" + resolve-from "^4.0.0" import-local@^2.0.0: version "2.0.0" @@ -5135,21 +5268,21 @@ ini@1.3.5, ini@^1.3.4, ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== -inquirer@7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" - integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== +inquirer@7.3.3: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== dependencies: ansi-escapes "^4.2.1" - chalk "^3.0.0" + chalk "^4.1.0" cli-cursor "^3.1.0" - cli-width "^2.0.0" + cli-width "^3.0.0" external-editor "^3.0.3" figures "^3.0.0" - lodash "^4.17.15" + lodash "^4.17.19" mute-stream "0.0.8" run-async "^2.4.0" - rxjs "^6.5.3" + rxjs "^6.6.0" string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" @@ -5162,13 +5295,6 @@ internal-ip@^4.3.0: default-gateway "^4.2.0" ipaddr.js "^1.9.0" -invariant@^2.2.2, invariant@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - dependencies: - loose-envify "^1.0.0" - ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -5269,6 +5395,13 @@ is-color-stop@^1.0.0: rgb-regex "^1.0.1" rgba-regex "^1.0.0" +is-core-module@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" + integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -5369,11 +5502,6 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== -is-lambda@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" - integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= - is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -5429,11 +5557,6 @@ is-path-inside@^2.1.0: dependencies: path-is-inside "^1.0.2" -is-plain-obj@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= - is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -5589,7 +5712,7 @@ istanbul-lib-instrument@^3.3.0: istanbul-lib-coverage "^2.0.5" semver "^6.0.0" -istanbul-lib-instrument@^4.0.1: +istanbul-lib-instrument@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== @@ -5657,15 +5780,25 @@ jasminewd2@^2.1.0: resolved "https://registry.yarnpkg.com/jasminewd2/-/jasminewd2-2.2.0.tgz#e37cf0b17f199cce23bea71b2039395246b4ec4e" integrity sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4= -jest-worker@26.0.0, jest-worker@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.0.0.tgz#4920c7714f0a96c6412464718d0c58a3df3fb066" - integrity sha512-pPaYa2+JnwmiZjK9x7p9BoZht+47ecFCDFA/CJxspHzeDvQcfVBLWzCiWyo+EGrSiQMWZtCFo9iSvMZnAAo8vw== +jest-worker@26.5.0: + version "26.5.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.5.0.tgz#87deee86dbbc5f98d9919e0dadf2c40e3152fa30" + integrity sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug== dependencies: + "@types/node" "*" merge-stream "^2.0.0" supports-color "^7.0.0" -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: +jest-worker@^26.5.0: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -5693,15 +5826,15 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= -json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: +json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== -json-parse-even-better-errors@^2.0.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.2.0.tgz#304d29aa54bb01156a1328c454034ff0ac8a7bf4" - integrity sha512-2tLgY7LRNZ9Hd6gmCuBG5/OjRHQpSgJQqJoYyLLOhUgn8LdOYrjaZLcxkWnDads+AD/haWWioPNziXQcgvQJ/g== +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" @@ -5744,6 +5877,11 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" +jsonc-parser@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" + integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -5751,7 +5889,7 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" -jsonparse@^1.3.1: +jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= @@ -5871,22 +6009,25 @@ kind-of@^6.0.0, kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== -less-loader@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-6.1.0.tgz#59fd591df408ced89a40fce11a2aea449b005631" - integrity sha512-/jLzOwLyqJ7Kt3xg5sHHkXtOyShWwFj410K9Si9WO+/h8rmYxxkSR0A3/hFEntWudE20zZnWMtpMYnLzqTVdUA== - dependencies: - clone "^2.1.2" - less "^3.11.1" - loader-utils "^2.0.0" - schema-utils "^2.6.6" +klona@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" + integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== -less@^3.11.1: - version "3.11.3" - resolved "https://registry.yarnpkg.com/less/-/less-3.11.3.tgz#2d853954fcfe0169a8af869620bcaa16563dcc1c" - integrity sha512-VkZiTDdtNEzXA3LgjQiC3D7/ejleBPFVvq+aRI9mIj+Zhmif5TvFPM244bT4rzkvOCvJ9q4zAztok1M7Nygagw== +less-loader@7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-7.0.2.tgz#0d73a49ec32a9d3ff12614598e6e2b47fb2a35c4" + integrity sha512-7MKlgjnkCf63E3Lv6w2FvAEgLMx3d/tNBExITcanAq7ys5U8VPWT3F6xcRjYmdNfkoQ9udoVFb1r2azSiTnD6w== + dependencies: + klona "^2.0.4" + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +less@3.12.2: + version "3.12.2" + resolved "https://registry.yarnpkg.com/less/-/less-3.12.2.tgz#157e6dd32a68869df8859314ad38e70211af3ab4" + integrity sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q== dependencies: - clone "^2.1.2" tslib "^1.10.0" optionalDependencies: errno "^0.1.1" @@ -5894,26 +6035,13 @@ less@^3.11.1: image-size "~0.5.0" make-dir "^2.1.0" mime "^1.4.1" - promise "^7.1.1" - request "^2.83.0" + native-request "^1.0.5" source-map "~0.6.0" -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - -levenary@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77" - integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ== - dependencies: - leven "^3.1.0" - -license-webpack-plugin@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.2.0.tgz#5c964380d7d0e0c27c349d86a6f856c82924590e" - integrity sha512-XPsdL/0brSHf+7dXIlRqotnCQ58RX2au6otkOg4U3dm8uH+Ka/fW4iukEs95uXm+qKe/SBs+s1Ll/aQddKG+tg== +license-webpack-plugin@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.1.tgz#08eddb2f776c7c64c02f308a00e017d6e824d0b6" + integrity sha512-yhqTmlYIEpZWA122lf6E0G8+rkn0AzoQ1OpzUKKs/lXUqG1plmGnwmkuuPlfggzJR5y6DLOdot/Tv00CC51CeQ== dependencies: "@types/webpack-sources" "^0.1.5" webpack-sources "^1.2.0" @@ -5925,6 +6053,11 @@ lie@~3.3.0: dependencies: immediate "~3.0.5" +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -5940,7 +6073,7 @@ loader-runner@^2.4.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== -loader-utils@1.2.3, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3: +loader-utils@1.2.3, loader-utils@^1.1.0, loader-utils@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== @@ -5990,37 +6123,32 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= - lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: +lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -log-symbols@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" - integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== +lodash@^4.17.19: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +log-symbols@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== dependencies: - chalk "^2.4.2" + chalk "^4.0.0" log4js@^4.0.0: version "4.5.1" @@ -6038,13 +6166,6 @@ loglevel@^1.6.8: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171" integrity sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA== -loose-envify@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - lru-cache@4.1.x, lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -6060,6 +6181,13 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + magic-string@0.25.7: version "0.25.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" @@ -6094,26 +6222,22 @@ make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== -make-fetch-happen@^8.0.7: - version "8.0.7" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-8.0.7.tgz#7f98e6e75784c541833d0ffe2f82c31418a87ac2" - integrity sha512-rkDA4c1nMXVqLkfOaM5RK2dxkUndjLOCrPycTDZgbkFDzhmaCO3P1dmCW//yt1I/G1EcedJqMsSjWkV79Hh4hQ== +make-fetch-happen@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz#aa8387104f2687edca01c8687ee45013d02d19bd" + integrity sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag== dependencies: - agentkeepalive "^4.1.0" - cacache "^15.0.0" - http-cache-semantics "^4.0.4" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-lambda "^1.0.1" + agentkeepalive "^3.4.1" + cacache "^12.0.0" + http-cache-semantics "^3.8.1" + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.3" lru-cache "^5.1.1" - minipass "^3.1.3" - minipass-collect "^1.0.2" - minipass-fetch "^1.1.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" + mississippi "^3.0.0" + node-fetch-npm "^2.0.2" promise-retry "^1.1.1" - socks-proxy-agent "^5.0.0" - ssri "^8.0.0" + socks-proxy-agent "^4.0.0" + ssri "^6.0.0" map-cache@^0.2.2: version "0.2.2" @@ -6184,6 +6308,11 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -6208,6 +6337,14 @@ micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -6250,7 +6387,7 @@ mime@1.6.0, mime@^1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.0.3, mime@^2.3.1, mime@^2.4.4: +mime@^2.3.1, mime@^2.4.4: version "2.4.4" resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== @@ -6260,14 +6397,13 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mini-css-extract-plugin@0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz#47f2cf07aa165ab35733b1fc97d4c46c0564339e" - integrity sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A== +mini-css-extract-plugin@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.2.1.tgz#30ea7dee632b3002b0c77aeed447790408cb247e" + integrity sha512-G3yw7/TQaPfkuiR73MDcyiqhyP8SnbmLhUbpC76H+wtQxA6wfKhMCQOCb6wnPK0dQbjORAeOILQqEesg4/wF7A== dependencies: - loader-utils "^1.1.0" - normalize-url "1.9.1" - schema-utils "^1.0.0" + loader-utils "^2.0.0" + schema-utils "^3.0.0" webpack-sources "^1.1.0" minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: @@ -6314,18 +6450,6 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" -minipass-fetch@^1.1.2, minipass-fetch@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.2.1.tgz#1b97ecb559be56b09812d45b2e9509f1f59ece2f" - integrity sha512-ssHt0dkljEDaKmTgQ04DQgx2ag6G2gMPxA5hpcsoeTbfDgRf2fC2gNSRc6kISjD7ckCpHwwQvXxuTBK8402fXg== - dependencies: - minipass "^3.1.0" - minipass-pipeline "^1.2.2" - minipass-sized "^1.0.3" - minizlib "^2.0.0" - optionalDependencies: - encoding "^0.1.12" - minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" @@ -6333,14 +6457,6 @@ minipass-flush@^1.0.5: dependencies: minipass "^3.0.0" -minipass-json-stream@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7" - integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg== - dependencies: - jsonparse "^1.3.1" - minipass "^3.0.0" - minipass-pipeline@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz#3dcb6bb4a546e32969c7ad710f2c79a86abba93a" @@ -6348,14 +6464,7 @@ minipass-pipeline@^1.2.2: dependencies: minipass "^3.0.0" -minipass-sized@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" - integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== - dependencies: - minipass "^3.0.0" - -minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: +minipass@^2.3.5, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== @@ -6370,7 +6479,7 @@ minipass@^3.0.0: dependencies: yallist "^4.0.0" -minipass@^3.0.1, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: +minipass@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== @@ -6384,7 +6493,7 @@ minizlib@^1.2.1: dependencies: minipass "^2.9.0" -minizlib@^2.0.0, minizlib@^2.1.0: +minizlib@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3" integrity sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA== @@ -6421,7 +6530,7 @@ mkdirp-classic@^0.5.2: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1, mkdirp@~0.5.x: +mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -6435,7 +6544,7 @@ mkdirp@^0.5.3: dependencies: minimist "^1.2.5" -mkdirp@^1.0.3, mkdirp@^1.0.4: +mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -6452,13 +6561,6 @@ move-concurrently@^1.0.1: rimraf "^2.5.4" run-queue "^1.0.3" -move-file@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/move-file/-/move-file-2.0.0.tgz#83ffa309b5d7f69d518b28e1333e2ffadf331e3e" - integrity sha512-cdkdhNCgbP5dvS4tlGxZbD+nloio9GIimP57EjqFhwLcMjnU+XJKAZzlmg/TN/AK1LuNAdTSvm3CPPP4Xkv0iQ== - dependencies: - path-exists "^4.0.0" - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -6469,7 +6571,7 @@ ms@2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -ms@^2.0.0, ms@^2.1.1: +ms@2.1.2, ms@^2.0.0, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -6497,6 +6599,11 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== +nanoid@^3.1.20: + version "3.1.20" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" + integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -6514,6 +6621,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +native-request@^1.0.5: + version "1.0.8" + resolved "https://registry.yarnpkg.com/native-request/-/native-request-1.0.8.tgz#8f66bf606e0f7ea27c0e5995eb2f5d03e33ae6fb" + integrity sha512-vU2JojJVelUGp6jRcLwToPoWGxSx23z/0iX+I77J3Ht17rf2INGjrhOoQnjVo60nQd8wVsgzKkPfRXBiVdD2ag== + needle@^2.2.1: version "2.4.0" resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" @@ -6533,6 +6645,11 @@ neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + next-tick@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" @@ -6543,6 +6660,20 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +node-fetch-npm@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz#6507d0e17a9ec0be3bec516958a497cec54bf5a4" + integrity sha512-iOuIQDWDyjhv9qSDrj9aq/klt6F9z1p2otB3AV7v3zBDcL/x+OfGsvGQZZCcMZbUf4Ujw1xGNQkjvGnVT22cKg== + dependencies: + encoding "^0.1.11" + json-parse-better-errors "^1.0.0" + safe-buffer "^5.1.1" + +node-fetch@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + node-forge@0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" @@ -6605,6 +6736,11 @@ node-releases@^1.1.53: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.58.tgz#8ee20eef30fa60e52755fcc0942def5a734fe935" integrity sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg== +node-releases@^1.1.70: + version "1.1.70" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.70.tgz#66e0ed0273aa65666d7fe78febe7634875426a08" + integrity sha512-Slf2s69+2/uAD79pVVQo8uSiC34+g8GWY8UH2Qtqv34ZfhYrxpYpfzs9Js9d6O0mbDmALuxaTlplnBTnSELcrw== + nopt@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" @@ -6613,7 +6749,7 @@ nopt@^4.0.1: abbrev "1" osenv "^0.1.4" -normalize-package-data@^2.0.0, normalize-package-data@^2.3.2: +normalize-package-data@^2.3.2, normalize-package-data@^2.4.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -6640,16 +6776,6 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= -normalize-url@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" - integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw= - dependencies: - object-assign "^4.0.1" - prepend-http "^1.0.0" - query-string "^4.1.0" - sort-keys "^1.0.0" - normalize-url@^3.0.0: version "3.3.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" @@ -6660,13 +6786,6 @@ npm-bundled@^1.0.1: resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== -npm-bundled@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" - integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== - dependencies: - npm-normalize-package-bin "^1.0.1" - npm-install-checks@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" @@ -6674,12 +6793,31 @@ npm-install-checks@^4.0.0: dependencies: semver "^7.1.1" -npm-normalize-package-bin@^1.0.1: +npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== -npm-package-arg@8.0.1, npm-package-arg@^8.0.0, npm-package-arg@^8.0.1: +npm-package-arg@8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.0.tgz#b5f6319418c3246a1c38e1a8fbaa06231bc5308f" + integrity sha512-/ep6QDxBkm9HvOhOg0heitSd7JHA1U7y1qhhlRlteYYAi9Pdb/ZV7FW5aHpkrpM8+P+4p/jjR8zCyKPBMBjSig== + dependencies: + hosted-git-info "^3.0.6" + semver "^7.0.0" + validate-npm-package-name "^3.0.0" + +npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.1.tgz#02168cb0a49a2b75bf988a28698de7b529df5cb7" + integrity sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg== + dependencies: + hosted-git-info "^2.7.1" + osenv "^0.1.5" + semver "^5.6.0" + validate-npm-package-name "^3.0.0" + +npm-package-arg@^8.0.0: version "8.0.1" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.0.1.tgz#9d76f8d7667b2373ffda60bb801a27ef71e3e270" integrity sha512-/h5Fm6a/exByzFSTm7jAyHbgOqErl9qSNJDQF32Si/ZzgwT2TERVxRxn3Jurw1wflgyVVAxnFR4fRHPM7y1ClQ== @@ -6688,6 +6826,15 @@ npm-package-arg@8.0.1, npm-package-arg@^8.0.0, npm-package-arg@^8.0.1: semver "^7.0.0" validate-npm-package-name "^3.0.0" +npm-packlist@^1.1.12: + version "1.4.8" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" + integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + npm-normalize-package-bin "^1.0.1" + npm-packlist@^1.1.6: version "1.4.4" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44" @@ -6696,26 +6843,7 @@ npm-packlist@^1.1.6: ignore-walk "^3.0.1" npm-bundled "^1.0.1" -npm-packlist@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.1.2.tgz#a3045b52aefc37e7a5e86a55e6ca8cb1e909e25a" - integrity sha512-eByPaP+wsKai0BJX5pmb58d3mfR0zUATcnyuvSxIudTEn+swCPFLxh7srCmqB4hr7i9V24/DPjjq5b2qUtbgXQ== - dependencies: - glob "^7.1.6" - ignore-walk "^3.0.3" - npm-bundled "^1.1.1" - npm-normalize-package-bin "^1.0.1" - -npm-pick-manifest@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.0.0.tgz#bfde7abe95f2670aed1629a3c18245ccb3cc2eb8" - integrity sha512-PdJpXMvjqt4nftNEDpCgjBUF8yI3Q3MyuAmVB9nemnnCg32F4BPL/JFBfdj8DubgHCYUFQhtLWmBPvdsFtjWMg== - dependencies: - npm-install-checks "^4.0.0" - npm-package-arg "^8.0.0" - semver "^7.0.0" - -npm-pick-manifest@^6.0.0: +npm-pick-manifest@6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.0.tgz#2befed87b0fce956790f62d32afb56d7539c022a" integrity sha512-ygs4k6f54ZxJXrzT0x34NybRlLeZ4+6nECAIbr2i0foTnijtS1TJiyzpqtuUAJOps/hO0tNDr8fRV5g+BtRlTw== @@ -6724,19 +6852,27 @@ npm-pick-manifest@^6.0.0: npm-package-arg "^8.0.0" semver "^7.0.0" -npm-registry-fetch@^8.0.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-8.1.0.tgz#1d5c229b82412414b9c63cde040b51981db76904" - integrity sha512-RkcugRDye2j6yEiHGMyAdKQoipgp8VToSIjm+TFLhVraXOkC/WU2kjE2URcYBpcJ4hs++VFBKo6+Zg4wmrS+Qw== +npm-pick-manifest@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz#f4d9e5fd4be2153e5f4e5f9b7be8dc419a99abb7" + integrity sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw== dependencies: - "@npmcli/ci-detect" "^1.0.0" + figgy-pudding "^3.5.1" + npm-package-arg "^6.0.0" + semver "^5.4.1" + +npm-registry-fetch@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-4.0.7.tgz#57951bf6541e0246b34c9f9a38ab73607c9449d7" + integrity sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ== + dependencies: + JSONStream "^1.3.4" + bluebird "^3.5.1" + figgy-pudding "^3.4.1" lru-cache "^5.1.1" - make-fetch-happen "^8.0.7" - minipass "^3.1.3" - minipass-fetch "^1.1.2" - minipass-json-stream "^1.0.1" - minizlib "^2.0.0" - npm-package-arg "^8.0.0" + make-fetch-happen "^5.0.0" + npm-package-arg "^6.1.0" + safe-buffer "^5.2.0" npm-run-all@4.1.5: version "4.1.5" @@ -6831,11 +6967,6 @@ object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object-path@0.11.4: - version "0.11.4" - resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.4.tgz#370ae752fbf37de3ea70a861c23bba8915691949" - integrity sha1-NwrnUvvzfePqcKhhwju6iRVpGUk= - object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" @@ -6909,18 +7040,10 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -open@7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/open/-/open-7.0.3.tgz#db551a1af9c7ab4c7af664139930826138531c48" - integrity sha512-sP2ru2v0P290WFfv49Ap8MF6PkzGNnGlAwHweB4WR4mr5d2d0woiCluUeJ218w7/+PmoBy9JmYgD5A4mLcWOFA== - dependencies: - is-docker "^2.0.0" - is-wsl "^2.1.1" - -open@7.0.4: - version "7.0.4" - resolved "https://registry.yarnpkg.com/open/-/open-7.0.4.tgz#c28a9d315e5c98340bf979fdcb2e58664aa10d83" - integrity sha512-brSA+/yq+b08Hsr4c8fsEW2CRzk1BmfN3SAK/5VCHQ9bdoZJ4qa/+AfR0xHjlbbZUyPkUHs1b8x1RqdyZdkVqQ== +open@7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/open/-/open-7.3.0.tgz#45461fdee46444f3645b6e14eb3ca94b82e1be69" + integrity sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw== dependencies: is-docker "^2.0.0" is-wsl "^2.1.1" @@ -6940,16 +7063,16 @@ optimist@^0.6.1, optimist@~0.6.0: minimist "~0.0.1" wordwrap "~0.0.2" -ora@4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/ora/-/ora-4.0.3.tgz#752a1b7b4be4825546a7a3d59256fa523b6b6d05" - integrity sha512-fnDebVFyz309A73cqCipVL1fBZewq4vwgSHfxh43vVy31mbyoQ8sCH3Oeaog/owYOs/lLlGVPCISQonTneg6Pg== +ora@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.1.0.tgz#b188cf8cd2d4d9b13fd25383bc3e5cba352c94f8" + integrity sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w== dependencies: - chalk "^3.0.0" + chalk "^4.1.0" cli-cursor "^3.1.0" - cli-spinners "^2.2.0" + cli-spinners "^2.4.0" is-interactive "^1.0.0" - log-symbols "^3.0.0" + log-symbols "^4.0.0" mute-stream "0.0.8" strip-ansi "^6.0.0" wcwidth "^1.0.1" @@ -6976,7 +7099,7 @@ os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -osenv@^0.1.4: +osenv@^0.1.4, osenv@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== @@ -7003,12 +7126,12 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^2.2.1, p-limit@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: - p-try "^2.0.0" + yocto-queue "^0.1.0" p-locate@^2.0.0: version "2.0.0" @@ -7060,34 +7183,41 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -pacote@11.1.4: - version "11.1.4" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.1.4.tgz#5529a453c59881b7f059da8af6903b0f79c124b2" - integrity sha512-eUGJvSSpWFZKn3z8gig/HgnBmUl6gIWByIIaHzSyEr3tOWX0w8tFEADXtpu8HGv5E0ShCeTP6enRq8iHKCHSvw== +pacote@9.5.12: + version "9.5.12" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.5.12.tgz#1e11dd7a8d736bcc36b375a9804d41bb0377bf66" + integrity sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ== dependencies: - "@npmcli/git" "^2.0.1" - "@npmcli/installed-package-contents" "^1.0.5" - "@npmcli/promise-spawn" "^1.1.0" - cacache "^15.0.0" - chownr "^1.1.4" - fs-minipass "^2.1.0" + bluebird "^3.5.3" + cacache "^12.0.2" + chownr "^1.1.2" + figgy-pudding "^3.5.1" + get-stream "^4.1.0" + glob "^7.1.3" infer-owner "^1.0.4" lru-cache "^5.1.1" - minipass "^3.0.1" - minipass-fetch "^1.2.1" - mkdirp "^1.0.3" - npm-package-arg "^8.0.1" - npm-packlist "^2.1.0" - npm-pick-manifest "^6.0.0" - npm-registry-fetch "^8.0.0" + make-fetch-happen "^5.0.0" + minimatch "^3.0.4" + minipass "^2.3.5" + mississippi "^3.0.0" + mkdirp "^0.5.1" + normalize-package-data "^2.4.0" + npm-normalize-package-bin "^1.0.0" + npm-package-arg "^6.1.0" + npm-packlist "^1.1.12" + npm-pick-manifest "^3.0.0" + npm-registry-fetch "^4.0.0" + osenv "^0.1.5" promise-inflight "^1.0.1" promise-retry "^1.1.1" - read-package-json-fast "^1.1.3" - rimraf "^2.7.1" - semver "^7.1.3" - ssri "^8.0.0" - tar "^6.0.1" - which "^2.0.2" + protoduck "^5.0.1" + rimraf "^2.6.2" + safe-buffer "^5.1.2" + semver "^5.6.0" + ssri "^6.0.1" + tar "^4.4.10" + unique-filename "^1.1.1" + which "^1.3.1" pako@~1.0.2, pako@~1.0.5: version "1.0.10" @@ -7103,6 +7233,13 @@ parallel-transform@^1.1.0: inherits "^2.0.3" readable-stream "^2.1.5" +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parse-asn1@^5.0.0: version "5.1.5" resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e" @@ -7123,10 +7260,35 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse5@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" - integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse5-html-rewriting-stream@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz#de1820559317ab4e451ea72dba05fddfd914480b" + integrity sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg== + dependencies: + parse5 "^6.0.1" + parse5-sax-parser "^6.0.1" + +parse5-sax-parser@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz#98b4d366b5b266a7cd90b4b58906667af882daba" + integrity sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg== + dependencies: + parse5 "^6.0.1" + +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== parseqs@0.0.5: version "0.0.5" @@ -7209,6 +7371,11 @@ path-type@^3.0.0: dependencies: pify "^3.0.0" +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -7235,7 +7402,7 @@ picomatch@^2.0.4: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== -picomatch@^2.2.1: +picomatch@^2.0.5, picomatch@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== @@ -7279,7 +7446,7 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" -pkg-dir@^4.1.0: +pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== @@ -7381,23 +7548,16 @@ postcss-import@12.0.1: read-cache "^1.0.0" resolve "^1.1.7" -postcss-load-config@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.0.tgz#c84d692b7bb7b41ddced94ee62e8ab31b417b003" - integrity sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q== +postcss-loader@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.0.4.tgz#b2d005b52e008a44991cf8123bee207e635eb53e" + integrity sha512-pntA9zIR14drQo84yGTjQJg1m7T0DkXR4vXYHBngiRZdJtEeCrojL6lOpqUanMzG375lIJbT4Yug85zC/AJWGw== dependencies: - cosmiconfig "^5.0.0" - import-cwd "^2.0.0" - -postcss-loader@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" - integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== - dependencies: - loader-utils "^1.1.0" - postcss "^7.0.0" - postcss-load-config "^2.0.0" - schema-utils "^1.0.0" + cosmiconfig "^7.0.0" + klona "^2.0.4" + loader-utils "^2.0.0" + schema-utils "^3.0.0" + semver "^7.3.2" postcss-merge-longhand@^4.0.11: version "4.0.11" @@ -7461,38 +7621,33 @@ postcss-minify-selectors@^4.0.2: postcss "^7.0.0" postcss-selector-parser "^3.0.0" -postcss-modules-extract-imports@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" - integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== - dependencies: - postcss "^7.0.5" - -postcss-modules-local-by-default@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz#e8a6561be914aaf3c052876377524ca90dbb7915" - integrity sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ== - dependencies: - icss-utils "^4.1.1" - postcss "^7.0.16" - postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.0.0" - -postcss-modules-scope@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" - integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== - dependencies: - postcss "^7.0.6" - postcss-selector-parser "^6.0.0" - -postcss-modules-values@^3.0.0: +postcss-modules-extract-imports@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" - integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== + +postcss-modules-local-by-default@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" + integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== dependencies: - icss-utils "^4.0.0" - postcss "^7.0.6" + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" postcss-normalize-charset@^4.0.1: version "4.0.1" @@ -7622,7 +7777,7 @@ postcss-selector-parser@^5.0.0-rc.4: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: +postcss-selector-parser@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== @@ -7631,6 +7786,16 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: indexes-of "^1.0.1" uniq "^1.0.1" +postcss-selector-parser@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" + integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== + dependencies: + cssesc "^3.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + util-deprecate "^1.0.2" + postcss-svgo@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" @@ -7655,7 +7820,7 @@ postcss-value-parser@^3.0.0, postcss-value-parser@^3.2.3, postcss-value-parser@^ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== -postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.3, postcss-value-parser@^4.1.0: +postcss-value-parser@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== @@ -7669,10 +7834,10 @@ postcss@7.0.21: source-map "^0.6.1" supports-color "^6.1.0" -postcss@7.0.31: - version "7.0.31" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.31.tgz#332af45cb73e26c0ee2614d7c7fb02dfcc2bd6dd" - integrity sha512-a937VDHE1ftkjk+8/7nj/mrjtmkn69xxzJgRETXdAUU+IgOYPQNJF17haGWbeDxSyk++HA14UA98FurvPyBJOA== +postcss@7.0.32: + version "7.0.32" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" + integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== dependencies: chalk "^2.4.2" source-map "^0.6.1" @@ -7687,10 +7852,10 @@ postcss@^7.0.0, postcss@^7.0.1: source-map "^0.6.1" supports-color "^6.1.0" -postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.27, postcss@^7.0.30, postcss@^7.0.6: - version "7.0.32" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" - integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== +postcss@^7.0.32: + version "7.0.35" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24" + integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg== dependencies: chalk "^2.4.2" source-map "^0.6.1" @@ -7705,10 +7870,14 @@ postcss@^7.0.5: source-map "^0.6.1" supports-color "^6.1.0" -prepend-http@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= +postcss@^8.1.1: + version "8.2.5" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.5.tgz#3c75149ada4e93db9521913654c0144517f77c9a" + integrity sha512-wMcb7BpDcm3gxQOQx46NDNT36Kk0Ao6PJLLI2ed5vehbbbxCEuslSQzbQ2sfSKy+gkYxhWcGWSeaK+gwm4KIZg== + dependencies: + colorette "^1.2.1" + nanoid "^3.1.20" + source-map "^0.6.1" private@^0.1.8: version "0.1.8" @@ -7743,16 +7912,15 @@ promise-retry@^1.1.1: err-code "^1.0.0" retry "^0.10.0" -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" - integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== +protoduck@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/protoduck/-/protoduck-5.0.1.tgz#03c3659ca18007b69a50fd82a7ebcc516261151f" + integrity sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg== dependencies: - asap "~2.0.3" + genfun "^5.0.0" -"protractor@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/protractor_archive.tar.gz": +"protractor@file:../../node_modules/protractor": version "5.4.3" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/protractor_archive.tar.gz#6dcc8c321fcde8864b4919f8e053be8ab94812b0" dependencies: "@types/q" "^0.0.32" "@types/selenium-webdriver" "^3.0.0" @@ -7798,6 +7966,11 @@ psl@^1.1.24: resolved "https://registry.yarnpkg.com/psl/-/psl-1.4.0.tgz#5dd26156cdb69fa1fdb8ab1991667d3f80ced7c2" integrity sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw== +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + public-encrypt@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" @@ -7850,14 +8023,15 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -"puppeteer@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/puppeteer_archive.tar.gz": - version "3.3.0" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/puppeteer_archive.tar.gz#756d459464dfc545615eeb50948b71d76c2f1e1f" +"puppeteer@file:../../node_modules/puppeteer": + version "5.4.1" dependencies: debug "^4.1.0" + devtools-protocol "0.0.809251" extract-zip "^2.0.0" https-proxy-agent "^4.0.0" - mime "^2.0.3" + node-fetch "^2.6.1" + pkg-dir "^4.2.0" progress "^2.0.1" proxy-from-env "^1.0.0" rimraf "^3.0.2" @@ -7890,14 +8064,6 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== -query-string@^4.1.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" - integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s= - dependencies: - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -7948,13 +8114,13 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -raw-loader@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.1.tgz#14e1f726a359b68437e183d5a5b7d33a3eba6933" - integrity sha512-baolhQBSi3iNh1cglJjA0mYzga+wePk7vdEX//1dTFd+v4TsQlQE0jitJSNF1OIP82rdYulH7otaVmdlDaJ64A== +raw-loader@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" + integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA== dependencies: loader-utils "^2.0.0" - schema-utils "^2.6.5" + schema-utils "^3.0.0" rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: version "1.2.8" @@ -7973,35 +8139,6 @@ read-cache@^1.0.0: dependencies: pify "^2.3.0" -read-package-json-fast@^1.1.1, read-package-json-fast@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-1.1.3.tgz#3b78464ea8f3c4447f3358635390b6946dc0737e" - integrity sha512-MmFqiyfCXV2Dmm4jH24DEGhxdkUDFivJQj4oPZQPOKywxR7HWBE6WnMWDAapfFHi3wm1b+mhR+XHlUH0CL8axg== - dependencies: - json-parse-even-better-errors "^2.0.1" - npm-normalize-package-bin "^1.0.1" - -read-package-json@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.0.tgz#e3d42e6c35ea5ae820d9a03ab0c7291217fc51d5" - integrity sha512-KLhu8M1ZZNkMcrq1+0UJbR8Dii8KZUqB0Sha4mOx/bknfKI/fyrQVrG/YIt2UOtG667sD8+ee4EXMM91W9dC+A== - dependencies: - glob "^7.1.1" - json-parse-better-errors "^1.0.1" - normalize-package-data "^2.0.0" - slash "^1.0.0" - optionalDependencies: - graceful-fs "^4.1.2" - -read-package-tree@5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636" - integrity sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw== - dependencies: - read-package-json "^2.0.0" - readdir-scoped-modules "^1.0.0" - util-promisify "^2.1.0" - read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -8042,16 +8179,6 @@ readable-stream@^3.1.1, readable-stream@^3.4.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readdir-scoped-modules@^1.0.0, readdir-scoped-modules@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" - integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== - dependencies: - debuglog "^1.0.1" - dezalgo "^1.0.0" - graceful-fs "^4.1.2" - once "^1.3.0" - readdirp@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" @@ -8075,10 +8202,10 @@ readdirp@~3.2.0: dependencies: picomatch "^2.0.4" -readdirp@~3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" - integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== dependencies: picomatch "^2.2.1" @@ -8087,13 +8214,6 @@ reflect-metadata@^0.1.2: resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== -regenerate-unicode-properties@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" - integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA== - dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" @@ -8106,7 +8226,12 @@ regenerate@^1.2.1, regenerate@^1.4.0: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== -regenerator-runtime@0.13.5, regenerator-runtime@^0.13.4: +regenerator-runtime@0.13.7: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + +regenerator-runtime@^0.13.4: version "0.13.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== @@ -8127,10 +8252,10 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regex-parser@2.2.10: - version "2.2.10" - resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.10.tgz#9e66a8f73d89a107616e63b39d4deddfee912b37" - integrity sha512-8t6074A68gHfU8Neftl0Le6KTDwfGAj7IyjPIMSfikI2wJUTHDMaIq42bUsfVnj8mhx0R+45rdUXHGpN164avA== +regex-parser@^2.2.11: + version "2.2.11" + resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58" + integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== regexp.prototype.flags@^1.2.0: version "1.2.0" @@ -8148,18 +8273,6 @@ regexpu-core@^1.0.0: regjsgen "^0.2.0" regjsparser "^0.1.4" -regexpu-core@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6" - integrity sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg== - dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^8.1.0" - regjsgen "^0.5.0" - regjsparser "^0.6.0" - unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.1.0" - regexpu-core@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938" @@ -8172,6 +8285,18 @@ regexpu-core@^4.7.0: unicode-match-property-ecmascript "^1.0.4" unicode-match-property-value-ecmascript "^1.2.0" +regexpu-core@^4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" + integrity sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.2.0" + regjsgen "^0.5.1" + regjsparser "^0.6.4" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.2.0" + registry-auth-token@3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" @@ -8192,11 +8317,6 @@ regjsgen@^0.2.0: resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc= -regjsgen@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd" - integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA== - regjsgen@^0.5.1: version "0.5.2" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" @@ -8209,13 +8329,6 @@ regjsparser@^0.1.4: dependencies: jsesc "~0.5.0" -regjsparser@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c" - integrity sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ== - dependencies: - jsesc "~0.5.0" - regjsparser@^0.6.4: version "0.6.4" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272" @@ -8238,7 +8351,7 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -request@^2.83.0, request@^2.87.0, request@^2.88.0: +request@^2.87.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== @@ -8264,6 +8377,32 @@ request@^2.83.0, request@^2.87.0, request@^2.88.0: tunnel-agent "^0.6.0" uuid "^3.3.2" +request@^2.88.2: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -8291,12 +8430,17 @@ resolve-from@^3.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" integrity sha1-six699nWiBvItuZTM17rywoYh0g= -resolve-url-loader@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.1.1.tgz#28931895fa1eab9be0647d3b2958c100ae3c0bf0" - integrity sha512-K1N5xUjj7v0l2j/3Sgs5b8CjrrgtC70SmdCuZiJ8tSyb5J+uk3FoeZ4b7yTnH6j7ngI+Bc5bldHJIa8hYdu2gQ== +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-url-loader@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.1.2.tgz#235e2c28e22e3e432ba7a5d4e305c59a58edfc08" + integrity sha512-QEb4A76c8Mi7I3xNKXlRKQSlLBwjUV/ULFMP+G7n3/7tJZ8MG5wsZ3ucxP1Jz8Vevn6fnJsxDx9cIls+utGzPQ== dependencies: - adjust-sourcemap-loader "2.0.0" + adjust-sourcemap-loader "3.0.0" camelcase "5.3.1" compose-function "3.0.3" convert-source-map "1.7.0" @@ -8312,6 +8456,14 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= +resolve@1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.18.1.tgz#018fcb2c5b207d2a6424aee361c5a266da8f4130" + integrity sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA== + dependencies: + is-core-module "^2.0.0" + path-parse "^1.0.6" + resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2: version "1.12.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" @@ -8349,6 +8501,11 @@ retry@^0.12.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + rework-visit@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/rework-visit/-/rework-visit-1.0.0.tgz#9945b2803f219e2f7aca00adb8bc9f640f842c9a" @@ -8384,7 +8541,7 @@ rimraf@3.0.2, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1, rimraf@^2.6.3, rimraf@^2.7.1: +rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -8399,10 +8556,10 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rollup@2.10.9: - version "2.10.9" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.10.9.tgz#17dcc6753c619efcc1be2cf61d73a87827eebdf9" - integrity sha512-dY/EbjiWC17ZCUSyk14hkxATAMAShkMsD43XmZGWjLrgFj15M3Dw2kEkA9ns64BiLFm9PKN6vTQw8neHwK74eg== +rollup@2.32.1: + version "2.32.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.32.1.tgz#625a92c54f5b4d28ada12d618641491d4dbb548c" + integrity sha512-Op2vWTpvK7t6/Qnm1TTh7VjEZZkN8RWgf0DHbkKzQBwNf748YhXbozHVefqpPp/Fuyk/PQPAnYsBxAEtlMvpUw== optionalDependencies: fsevents "~2.1.2" @@ -8411,6 +8568,11 @@ run-async@^2.4.0: resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== +run-parallel@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef" + integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw== + run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" @@ -8418,16 +8580,15 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@6.5.4, "rxjs@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/rxjs_archive.tar.gz": - version "6.5.4" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/rxjs_archive.tar.gz#6b18c390755121e1fb6a7ddf070a6ec0137a3210" +rxjs@6.6.3, rxjs@^6.6.0: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" + integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== dependencies: tslib "^1.9.0" -rxjs@6.5.5, rxjs@^6.5.3: - version "6.5.5" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" - integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== +"rxjs@file:../../node_modules/rxjs": + version "6.5.4" dependencies: tslib "^1.9.0" @@ -8441,6 +8602,11 @@ safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== +safe-buffer@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -8448,26 +8614,26 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@^2.1.2, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@^2.1.2, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sass-loader@8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-8.0.2.tgz#debecd8c3ce243c76454f2e8290482150380090d" - integrity sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ== +sass-loader@10.0.4: + version "10.0.4" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.0.4.tgz#ec7181096947d078d60a1d76d527f47c19b151d8" + integrity sha512-zhdZ8qvZM4iL5XjLVEjJLvKWvC+MB+hHgzL2x/Nf7UHpUNmPYsJvypW79bW39g4LZ603dH/dRSsRYzJJIljtdA== dependencies: - clone-deep "^4.0.1" - loader-utils "^1.2.3" - neo-async "^2.6.1" - schema-utils "^2.6.1" - semver "^6.3.0" + klona "^2.0.4" + loader-utils "^2.0.0" + neo-async "^2.6.2" + schema-utils "^3.0.0" + semver "^7.3.2" -sass@1.26.5: - version "1.26.5" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.26.5.tgz#2d7aecfbbabfa298567c8f06615b6e24d2d68099" - integrity sha512-FG2swzaZUiX53YzZSjSakzvGtlds0lcbF+URuU9mxOv7WBh7NhXEVDa4kPKN4hN6fC2TkOTOKqiqp6d53N9X5Q== +sass@1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.27.0.tgz#0657ff674206b95ec20dc638a93e179c78f6ada2" + integrity sha512-0gcrER56OkzotK/GGwgg4fPrKuiFlPNitO7eUJ18Bs+/NBlofJfMxmxqpqJxjae9vu0Wq8TZzrSyxZal00WDig== dependencies: chokidar ">=2.0.0 <4.0.0" @@ -8492,15 +8658,7 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.6.1, schema-utils@^2.6.4: - version "2.6.4" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.4.tgz#a27efbf6e4e78689d91872ee3ccfa57d7bdd0f53" - integrity sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ== - dependencies: - ajv "^6.10.2" - ajv-keywords "^3.4.1" - -schema-utils@^2.6.5, schema-utils@^2.6.6: +schema-utils@^2.6.5: version "2.7.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== @@ -8509,6 +8667,24 @@ schema-utils@^2.6.5, schema-utils@^2.6.6: ajv "^6.12.2" ajv-keywords "^3.4.1" +schema-utils@^2.7.0: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== + dependencies: + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" + +schema-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" + integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== + dependencies: + "@types/json-schema" "^7.0.6" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -8555,12 +8731,7 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@7.1.3: - version "7.1.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6" - integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA== - -semver@7.3.2, semver@^7.0.0, semver@^7.1.1, semver@^7.1.3, semver@^7.3.2: +semver@7.3.2, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2: version "7.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== @@ -8589,18 +8760,20 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" -serialize-javascript@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" - integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== - -serialize-javascript@^3.0.0, serialize-javascript@^3.1.0: +serialize-javascript@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.1.0.tgz#8bf3a9170712664ef2561b44b691eafe399214ea" integrity sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg== dependencies: randombytes "^2.1.0" +serialize-javascript@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + dependencies: + randombytes "^2.1.0" + serve-handler@6.1.2: version "6.1.2" resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.2.tgz#f05b0421a313fff2d257838cba00cbcc512cd2b6" @@ -8732,10 +8905,10 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" - integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== smart-buffer@^4.1.0: version "4.1.0" @@ -8839,45 +9012,38 @@ sockjs@0.3.20: uuid "^3.4.0" websocket-driver "0.6.5" -socks-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz#7c0f364e7b1cf4a7a437e71253bed72e9004be60" - integrity sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA== +socks-proxy-agent@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" + integrity sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg== dependencies: - agent-base "6" - debug "4" - socks "^2.3.3" + agent-base "~4.2.1" + socks "~2.3.2" -socks@^2.3.3: - version "2.4.1" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.4.1.tgz#cea68a280a3bf7cb6333dbb40cfb243d10725e9d" - integrity sha512-8mWHeYC1OA0500qzb+sqwm0Hzi8oBpeuI1JugoBVMEJtJvxSgco8xFSK+NRnZcHeeWjTbF82KUDo5sXH22TY5A== +socks@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.3.tgz#01129f0a5d534d2b897712ed8aceab7ee65d78e3" + integrity sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA== dependencies: ip "1.1.5" smart-buffer "^4.1.0" -sort-keys@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" - integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= - dependencies: - is-plain-obj "^1.0.0" - -source-list-map@^2.0.0: +source-list-map@^2.0.0, source-list-map@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-loader@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-1.0.0.tgz#240b88575a9b0d27214aeecbd4e7686af95cfa56" - integrity sha512-ZayyQCSCrQazN50aCvuS84lJT4xc1ZAcykH5blHaBdVveSwjiFK8UGMPvao0ho54DTb0Jf7m57uRRG/YYUZ2Fg== +source-map-loader@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-1.1.2.tgz#5b782bf08496d3a7f355e1780df0e25190a80991" + integrity sha512-bjf6eSENOYBX4JZDfl9vVLNsGAQ6Uz90fLmOazcmMcyDYOBFsGxPNn83jXezWLY9bJsVAo1ObztxPcV8HAbjVA== dependencies: - data-urls "^2.0.0" - iconv-lite "^0.5.1" + abab "^2.0.5" + iconv-lite "^0.6.2" loader-utils "^2.0.0" - schema-utils "^2.6.6" - source-map "^0.6.0" + schema-utils "^3.0.0" + source-map "^0.6.1" + whatwg-mimetype "^2.3.0" source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.2" @@ -8890,7 +9056,7 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@0.5.19: +source-map-support@0.5.19, source-map-support@~0.5.19: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -8923,7 +9089,7 @@ source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, sourc resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@0.7.3, source-map@^0.7.3: +source-map@0.7.3, source-map@^0.7.3, source-map@~0.7.2: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== @@ -9031,7 +9197,7 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -ssri@^6.0.1: +ssri@^6.0.0, ssri@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== @@ -9106,11 +9272,6 @@ streamroller@^1.0.6: fs-extra "^7.0.1" lodash "^4.17.14" -strict-uri-encode@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" - integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= - string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -9253,13 +9414,13 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -style-loader@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.2.1.tgz#c5cbbfbf1170d076cfdd86e0109c5bba114baa1a" - integrity sha512-ByHSTQvHLkWE9Ir5+lGbVOXhxX10fbprhLvdg96wedFZb4NDekDPxVKv5Fwmio+QcMlkkNfuK+5W1peQ5CUhZg== +style-loader@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c" + integrity sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ== dependencies: loader-utils "^2.0.0" - schema-utils "^2.6.6" + schema-utils "^3.0.0" stylehacks@^4.0.0: version "4.0.3" @@ -9270,27 +9431,29 @@ stylehacks@^4.0.0: postcss "^7.0.0" postcss-selector-parser "^3.0.0" -stylus-loader@3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-3.0.2.tgz#27a706420b05a38e038e7cacb153578d450513c6" - integrity sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA== +stylus-loader@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-4.1.1.tgz#0e94f5d6274932a2dad054d1a736b32146ac7a99" + integrity sha512-Vnm7J/nIs/P6swIrdwJW/dflhsCOiFmb1U3PeQ6phRtg1soPLN4uKnnL7AtGIJDe173elbtYIXVzmCyF493CfA== dependencies: - loader-utils "^1.0.2" - lodash.clonedeep "^4.5.0" - when "~3.6.x" + fast-glob "^3.2.4" + klona "^2.0.4" + loader-utils "^2.0.0" + normalize-path "^3.0.0" + schema-utils "^3.0.0" -stylus@0.54.7: - version "0.54.7" - resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.7.tgz#c6ce4793965ee538bcebe50f31537bfc04d88cd2" - integrity sha512-Yw3WMTzVwevT6ZTrLCYNHAFmanMxdylelL3hkWNgPMeTCpMwpV3nXjpOHuBXtFv7aiO2xRuQS6OoAdgkNcSNug== +stylus@0.54.8: + version "0.54.8" + resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.8.tgz#3da3e65966bc567a7b044bfe0eece653e099d147" + integrity sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg== dependencies: css-parse "~2.0.0" debug "~3.1.0" - glob "^7.1.3" - mkdirp "~0.5.x" + glob "^7.1.6" + mkdirp "~1.0.4" safer-buffer "^2.1.2" sax "~1.2.4" - semver "^6.0.0" + semver "^6.3.0" source-map "^0.7.3" supports-color@^2.0.0: @@ -9338,16 +9501,21 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" -symbol-observable@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" - integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== +symbol-observable@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" + integrity sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA== tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== +tapable@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" + integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== + tar-fs@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.0.tgz#d1cdd121ab465ee0eb9ccde2d35049d3f3daf0d5" @@ -9369,7 +9537,7 @@ tar-stream@^2.0.0: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^4: +tar@^4, tar@^4.4.10: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== @@ -9382,7 +9550,7 @@ tar@^4: safe-buffer "^5.1.2" yallist "^3.0.3" -tar@^6.0.1, tar@^6.0.2: +tar@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.2.tgz#5df17813468a6264ff14f766886c622b84ae2f39" integrity sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg== @@ -9401,19 +9569,19 @@ term-size@^1.2.0: dependencies: execa "^0.7.0" -terser-webpack-plugin@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-3.0.1.tgz#31928c9330a582fb5ec6f90805337289b85cb8fe" - integrity sha512-eFDtq8qPUEa9hXcUzTwKXTnugIVtlqc1Z/ZVhG8LmRT3lgRY13+pQTnFLY2N7ATB6TKCHuW/IGjoAnZz9wOIqw== +terser-webpack-plugin@4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz#28daef4a83bd17c1db0297070adc07fc8cfc6a9a" + integrity sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ== dependencies: - cacache "^15.0.3" + cacache "^15.0.5" find-cache-dir "^3.3.1" - jest-worker "^26.0.0" - p-limit "^2.3.0" - schema-utils "^2.6.6" - serialize-javascript "^3.0.0" + jest-worker "^26.5.0" + p-limit "^3.0.2" + schema-utils "^3.0.0" + serialize-javascript "^5.0.1" source-map "^0.6.1" - terser "^4.6.13" + terser "^5.3.4" webpack-sources "^1.4.3" terser-webpack-plugin@^1.4.3: @@ -9431,14 +9599,14 @@ terser-webpack-plugin@^1.4.3: webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser@4.7.0, terser@^4.6.13: - version "4.7.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.7.0.tgz#15852cf1a08e3256a80428e865a2fa893ffba006" - integrity sha512-Lfb0RiZcjRDXCC3OSHJpEkxJ9Qeqs6mp2v4jf2MHfy8vGERmVDuvjXdd/EnP5Deme5F2yBRBymKmKHCBg2echw== +terser@5.3.7: + version "5.3.7" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.7.tgz#798a4ae2e7ff67050c3e99fcc4e00725827d97e2" + integrity sha512-lJbKdfxWvjpV330U4PBZStCT9h3N9A4zZVA5Y4k9sCWXknrpdyxi1oMsRKLmQ/YDMDxSBKIh88v0SkdhdqX06w== dependencies: commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" + source-map "~0.7.2" + source-map-support "~0.5.19" terser@^4.1.2: version "4.3.8" @@ -9449,6 +9617,20 @@ terser@^4.1.2: source-map "~0.6.1" source-map-support "~0.5.12" +terser@^5.3.4: + version "5.5.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.1.tgz#540caa25139d6f496fdea056e414284886fb2289" + integrity sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ== + dependencies: + commander "^2.20.0" + source-map "~0.7.2" + source-map-support "~0.5.19" + +text-table@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -9457,7 +9639,7 @@ through2@^2.0.0: readable-stream "~2.3.6" xtend "~4.0.1" -through@X.X.X, through@^2.3.6, through@^2.3.8: +"through@>=2.2.7 <3", through@X.X.X, through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -9553,11 +9735,12 @@ tough-cookie@~2.4.3: psl "^1.1.24" punycode "^1.4.1" -tr46@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479" - integrity sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg== +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== dependencies: + psl "^1.1.28" punycode "^2.1.1" tree-kill@1.2.2: @@ -9581,10 +9764,10 @@ ts-pnp@^1.1.6: resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== -tslib@2.0.0, tslib@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.0.tgz#18d13fc2dce04051e20f074cc8387fd8089ce4f3" - integrity sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g== +tslib@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" + integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== tslib@^1.10.0: version "1.13.0" @@ -9596,9 +9779,13 @@ tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== -"tslib@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/tslib_archive.tar.gz": +tslib@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.0.tgz#18d13fc2dce04051e20f074cc8387fd8089ce4f3" + integrity sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g== + +"tslib@file:../../node_modules/tslib": version "2.0.0" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/tslib_archive.tar.gz#10a662f835f9eee914ce6236d88a92171bc30cf1" tslint@5.18.0: version "5.18.0" @@ -9671,9 +9858,13 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -"typescript@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/typescript_archive.tar.gz": - version "3.9.5" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/typescript_archive.tar.gz#8123551072ef0f906ff63d920bdda3dc1cb23fb9" +typescript@4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.5.tgz#ae9dddfd1069f1cb5beb3ef3b2170dd7c1332389" + integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ== + +"typescript@file:../../node_modules/typescript": + version "4.1.2" uglify-js@^3.1.4: version "3.6.1" @@ -9709,11 +9900,6 @@ unicode-match-property-ecmascript@^1.0.4: unicode-canonical-property-names-ecmascript "^1.0.4" unicode-property-aliases-ecmascript "^1.0.4" -unicode-match-property-value-ecmascript@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" - integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== - unicode-match-property-value-ecmascript@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" @@ -9758,13 +9944,13 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" -universal-analytics@0.4.20: - version "0.4.20" - resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.20.tgz#d6b64e5312bf74f7c368e3024a922135dbf24b03" - integrity sha512-gE91dtMvNkjO+kWsPstHRtSwHXz0l2axqptGYp5ceg4MsuurloM0PU3pdOfpb5zBXUvyjT4PwhWK2m39uczZuw== +universal-analytics@0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.23.tgz#d915e676850c25c4156762471bdd7cf2eaaca8ac" + integrity sha512-lgMIH7XBI6OgYn1woDEmxhGdj8yDefMKg7GkWdeATAlQZFrMrNyxSkpDzY57iY0/6fdlzTbBV03OawvvzG+q7A== dependencies: - debug "^3.0.0" - request "^2.88.0" + debug "^4.1.1" + request "^2.88.2" uuid "^3.0.0" universalify@^0.1.0: @@ -9844,18 +10030,11 @@ useragent@2.3.0: lru-cache "4.1.x" tmp "0.0.x" -util-deprecate@^1.0.1, util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util-promisify@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/util-promisify/-/util-promisify-2.1.0.tgz#3c2236476c4d32c5ff3c47002add7c13b9a82a53" - integrity sha1-PCI2R2xNMsX/PEcAKt18E7moKlM= - dependencies: - object.getownpropertydescriptors "^2.0.3" - util.promisify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" @@ -9883,10 +10062,10 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -uuid@7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.2.tgz#7ff5c203467e91f5e0d85cfcbaaf7d2ebbca9be6" - integrity sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw== +uuid@8.3.1: + version "8.3.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31" + integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg== uuid@^3.0.0, uuid@^3.3.2: version "3.3.3" @@ -9942,23 +10121,23 @@ void-elements@^2.0.0: resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= -watchpack-chokidar2@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0" - integrity sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA== +watchpack-chokidar2@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" + integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== dependencies: chokidar "^2.1.8" -watchpack@^1.6.1: - version "1.7.2" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.2.tgz#c02e4d4d49913c3e7e122c3325365af9d331e9aa" - integrity sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g== +watchpack@^1.7.4: + version "1.7.5" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" + integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== dependencies: graceful-fs "^4.1.2" neo-async "^2.5.0" optionalDependencies: - chokidar "^3.4.0" - watchpack-chokidar2 "^2.0.0" + chokidar "^3.4.1" + watchpack-chokidar2 "^2.0.1" wbuf@^1.1.0, wbuf@^1.7.3: version "1.7.3" @@ -9982,9 +10161,8 @@ webdriver-js-extender@2.1.0: "@types/selenium-webdriver" "^3.0.0" selenium-webdriver "^3.0.1" -webdriver-manager@^12.0.6, "webdriver-manager@file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/webdriver-manager_archive.tar.gz": - version "12.1.7" - resolved "file:/private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/npm/webdriver-manager_archive.tar.gz#f686648431b69523ae0940222ffd6e6f9ba4bfab" +webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" @@ -9998,11 +10176,6 @@ webdriver-manager@^12.0.6, "webdriver-manager@file:/private/var/tmp/_bazel_pete/ semver "^5.3.0" xml2js "^0.4.17" -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== - webpack-dev-middleware@3.7.2, webpack-dev-middleware@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" @@ -10061,14 +10234,23 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" -webpack-merge@4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" - integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== +webpack-merge@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.2.0.tgz#31cbcc954f8f89cd4b06ca8d97a38549f7f3f0c9" + integrity sha512-QBglJBg5+lItm3/Lopv8KDDK01+hjdg2azEwi/4vKJ8ZmGPdtJsTpjtNNOW3a4WiqzXdCATtTudOZJngE7RKkA== dependencies: - lodash "^4.17.15" + clone-deep "^4.0.1" + wildcard "^2.0.0" -webpack-sources@1.4.3, webpack-sources@^1.1.0, webpack-sources@^1.2.0, webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: +webpack-sources@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.0.1.tgz#1467f6e692ddce91e88b8044c44347b1087bbd4f" + integrity sha512-A9oYz7ANQBK5EN19rUXbvNgfdfZf5U2gP0769OXsj9CvYkCR6OHOsd6OKyEy4H38GGxpsQPKIL83NC64QY6Xmw== + dependencies: + source-list-map "^2.0.1" + source-map "^0.6.1" + +webpack-sources@^1.1.0, webpack-sources@^1.2.0, webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== @@ -10076,17 +10258,17 @@ webpack-sources@1.4.3, webpack-sources@^1.1.0, webpack-sources@^1.2.0, webpack-s source-list-map "^2.0.0" source-map "~0.6.1" -webpack-subresource-integrity@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-1.4.1.tgz#e8bf918b444277df46a66cd84542cbcdc5a6272d" - integrity sha512-XMLFInbGbB1HV7K4vHWANzc1CN0t/c4bBvnlvGxGwV45yE/S/feAXIm8dJsCkzqWtSKnmaEgTp/meyeThxG4Iw== +webpack-subresource-integrity@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-1.5.1.tgz#6f44ea99987266b70c4ec42ac51064d33e982277" + integrity sha512-uekbQ93PZ9e7BFB8Hl9cFIVYQyQqiXp2ExKk9Zv+qZfH/zHXHrCFAfw1VW0+NqWbTWrs/HnuDrto3+tiPXh//Q== dependencies: webpack-sources "^1.3.0" -webpack@4.43.0: - version "4.43.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.43.0.tgz#c48547b11d563224c561dad1172c8aa0b8a678e6" - integrity sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g== +webpack@4.44.2: + version "4.44.2" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.2.tgz#6bfe2b0af055c8b2d1e90ed2cd9363f841266b72" + integrity sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-module-context" "1.9.0" @@ -10096,7 +10278,7 @@ webpack@4.43.0: ajv "^6.10.2" ajv-keywords "^3.4.1" chrome-trace-event "^1.0.2" - enhanced-resolve "^4.1.0" + enhanced-resolve "^4.3.0" eslint-scope "^4.0.3" json-parse-better-errors "^1.0.2" loader-runner "^2.4.0" @@ -10109,7 +10291,7 @@ webpack@4.43.0: schema-utils "^1.0.0" tapable "^1.1.3" terser-webpack-plugin "^1.4.3" - watchpack "^1.6.1" + watchpack "^1.7.4" webpack-sources "^1.4.1" websocket-driver@0.6.5: @@ -10138,39 +10320,18 @@ whatwg-mimetype@^2.3.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url@^8.0.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.1.0.tgz#c628acdcf45b82274ce7281ee31dd3c839791771" - integrity sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^2.0.2" - webidl-conversions "^5.0.0" - -when@~3.6.x: - version "3.6.4" - resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e" - integrity sha1-RztRfsFZ4rhQBUl6E5g/CVQS404= - which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@^1.2.1, which@^1.2.9: +which@^1.2.1, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" -which@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" @@ -10185,6 +10346,11 @@ widest-line@^2.0.0: dependencies: string-width "^2.1.1" +wildcard@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" + integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== + wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" @@ -10197,10 +10363,10 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" -worker-plugin@4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/worker-plugin/-/worker-plugin-4.0.3.tgz#7c42e600d5931ad154d3d5f187a32446df64db0f" - integrity sha512-7hFDYWiKcE3yHZvemsoM9lZis/PzurHAEX1ej8PLCu818Rt6QqUAiDdxHPCKZctzmhqzPpcFSgvMCiPbtooqAg== +worker-plugin@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/worker-plugin/-/worker-plugin-5.0.0.tgz#113b5fe1f4a5d6a957cecd29915bedafd70bb537" + integrity sha512-AXMUstURCxDD6yGam2r4E34aJg6kW85IiaeX72hi+I1cxyaMUtrvVY6sbfpGKAj5e7f68Acl62BjQF5aOOx2IQ== dependencies: loader-utils "^1.1.0" @@ -10213,10 +10379,10 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" @@ -10277,6 +10443,11 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== +y18n@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" + integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== + yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" @@ -10292,6 +10463,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" + integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== + yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" @@ -10300,30 +10476,10 @@ yargs-parser@^13.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^18.1.0: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs@15.3.0: - version "15.3.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.0.tgz#403af6edc75b3ae04bf66c94202228ba119f0976" - integrity sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.0" +yargs-parser@^20.2.2: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== yargs@^13.3.2: version "13.3.2" @@ -10341,6 +10497,19 @@ yargs@^13.3.2: y18n "^4.0.0" yargs-parser "^13.1.2" +yargs@^16.1.1: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + yauzl@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" @@ -10359,8 +10528,13 @@ yn@^3.0.0: resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== -"zone.js@file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/zone.js/npm_package_archive.tar.gz": - version "0.10.3" - resolved "file:../../../../../../../../private/var/tmp/_bazel_pete/f9acfb7f019473a10a34c8c30adc55ea/execroot/angular/bazel-out/darwin-fastbuild/bin/integration/ivy-i18n_test.debug.sh.runfiles/angular/packages/zone.js/npm_package_archive.tar.gz#3834c82c35574433a110f24c3acf4417af8a08d8" +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +"zone.js@file:../../dist/zone.js-dist/archive/zone.js.tgz": + version "0.11.3" + resolved "file:../../dist/zone.js-dist/archive/zone.js.tgz#0fcaa1c4fff0fbb3d78479accc409378bdf4d777" dependencies: tslib "^2.0.0" diff --git a/integration/ng_elements/e2e/app.e2e-spec.ts b/integration/ng_elements/e2e/app.e2e-spec.ts index e8d1d31301..0d7e65ffbc 100644 --- a/integration/ng_elements/e2e/app.e2e-spec.ts +++ b/integration/ng_elements/e2e/app.e2e-spec.ts @@ -1,4 +1,4 @@ -import {browser, by, element, ElementFinder,ExpectedConditions as EC } from 'protractor'; +import {browser, by, element, ElementFinder, ExpectedConditions as EC} from 'protractor'; browser.waitForAngularEnabled(false); describe('Element E2E Tests', function () { diff --git a/integration/ng_elements/src/app.ts b/integration/ng_elements/src/app.ts index ceace4488c..8064cce22f 100644 --- a/integration/ng_elements/src/app.ts +++ b/integration/ng_elements/src/app.ts @@ -1,8 +1,8 @@ -import { Injector, NgModule } from '@angular/core'; -import { createCustomElement } from '@angular/elements'; -import { BrowserModule } from '@angular/platform-browser'; +import {Injector, NgModule} from '@angular/core'; +import {createCustomElement} from '@angular/elements'; +import {BrowserModule} from '@angular/platform-browser'; -import { HelloWorldComponent, HelloWorldOnpushComponent, HelloWorldShadowComponent, TestCardComponent } from './elements'; +import {HelloWorldComponent, HelloWorldOnpushComponent, HelloWorldShadowComponent, TestCardComponent} from './elements'; @NgModule({ diff --git a/integration/ng_elements/src/main.ts b/integration/ng_elements/src/main.ts index 143f5ef6df..608f5bd5aa 100644 --- a/integration/ng_elements/src/main.ts +++ b/integration/ng_elements/src/main.ts @@ -1,5 +1,5 @@ -import { platformBrowser } from '@angular/platform-browser'; -import { AppModuleNgFactory } from './app.ngfactory'; +import {platformBrowser} from '@angular/platform-browser'; +import {AppModuleNgFactory} from './app.ngfactory'; platformBrowser().bootstrapModuleFactory(AppModuleNgFactory, {ngZone: 'noop'}); diff --git a/integration/ng_elements/yarn.lock b/integration/ng_elements/yarn.lock index 9e4cde8848..e40f3788da 100644 --- a/integration/ng_elements/yarn.lock +++ b/integration/ng_elements/yarn.lock @@ -3267,7 +3267,7 @@ webdriver-js-extender@2.1.0: selenium-webdriver "^3.0.1" webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": - version "12.1.7" + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" diff --git a/integration/ng_update_migrations/package.json b/integration/ng_update_migrations/package.json index b9190e3993..34f7778ae5 100644 --- a/integration/ng_update_migrations/package.json +++ b/integration/ng_update_migrations/package.json @@ -42,5 +42,9 @@ "karma-jasmine-html-reporter": "1.4.2", "protractor": "file:../../node_modules/protractor", "typescript": "file:../../node_modules/typescript" + }, + "//resolutions-comment": "Ensure a single version of webdriver-manager which comes from root node_modules that has already run webdriver-manager update", + "resolutions": { + "**/webdriver-manager": "file:../../node_modules/webdriver-manager" } } diff --git a/integration/ng_update_migrations/src/environments/environment.ts b/integration/ng_update_migrations/src/environments/environment.ts index 7b4f817adb..30d7bccb19 100644 --- a/integration/ng_update_migrations/src/environments/environment.ts +++ b/integration/ng_update_migrations/src/environments/environment.ts @@ -13,4 +13,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/integration/ng_update_migrations/src/polyfills.ts b/integration/ng_update_migrations/src/polyfills.ts index f39dafba9c..b2e18b7be3 100644 --- a/integration/ng_update_migrations/src/polyfills.ts +++ b/integration/ng_update_migrations/src/polyfills.ts @@ -55,7 +55,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** diff --git a/integration/ng_update_migrations/src/test.ts b/integration/ng_update_migrations/src/test.ts index 16317897b1..a6f15af366 100644 --- a/integration/ng_update_migrations/src/test.ts +++ b/integration/ng_update_migrations/src/test.ts @@ -1,6 +1,6 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/integration/ng_update_migrations/yarn.lock b/integration/ng_update_migrations/yarn.lock index 8892b66479..d9c0e7e1c4 100644 --- a/integration/ng_update_migrations/yarn.lock +++ b/integration/ng_update_migrations/yarn.lock @@ -8349,10 +8349,8 @@ webdriver-js-extender@2.1.0: "@types/selenium-webdriver" "^3.0.0" selenium-webdriver "^3.0.1" -webdriver-manager@^12.0.6: - version "12.1.6" - resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.1.6.tgz#9e5410c506d1a7e0a7aa6af91ba3d5bb37f362b6" - integrity sha512-B1mOycNCrbk7xODw7Jgq/mdD3qzPxMaTsnKIQDy2nXlQoyjTrJTTD0vRpEZI9b8RibPEyQvh9zIZ0M1mpOxS3w== +webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" diff --git a/integration/ngcc/package.json b/integration/ngcc/package.json index 75a47d83cc..a8981aad5a 100644 --- a/integration/ngcc/package.json +++ b/integration/ngcc/package.json @@ -26,6 +26,10 @@ "lite-server": "2.2.2", "protractor": "file:../../node_modules/protractor" }, + "//resolutions-comment": "Ensure a single version of webdriver-manager which comes from root node_modules that has already run webdriver-manager update", + "resolutions": { + "**/webdriver-manager": "file:../../node_modules/webdriver-manager" + }, "scripts": { "test": "./test.sh" } diff --git a/integration/ngcc/src/main.ts b/integration/ngcc/src/main.ts index a74df56cf4..2a2b5cf7b1 100644 --- a/integration/ngcc/src/main.ts +++ b/integration/ngcc/src/main.ts @@ -1,6 +1,6 @@ -import { Component, NgModule, ɵrenderComponent as renderComponent } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { MatButtonModule } from '@angular/material/button'; +import {Component, NgModule, ɵrenderComponent as renderComponent} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {MatButtonModule} from '@angular/material/button'; @Component({ selector: 'hello-world', diff --git a/integration/ngcc/test.sh b/integration/ngcc/test.sh index 93448d1db3..58d52c7f08 100755 --- a/integration/ngcc/test.sh +++ b/integration/ngcc/test.sh @@ -6,8 +6,6 @@ # Each statement should be followed by an `assert*` or `exit 1` statement. set +e -x -PATH=$PATH:$(npm bin) - function assertFailed { if [[ $? -eq 0 ]]; then echo "FAIL: $1"; @@ -81,13 +79,13 @@ assertSucceeded "Expected 'ngcc' to log 'Compiling'." # Did it compile @angular/core/ApplicationModule correctly? - grep "ApplicationModule.ɵmod = ɵɵdefineNgModule" node_modules/@angular/core/fesm2015/core.js + grep "ApplicationModule.ɵmod = /\*@__PURE__\*/ ɵɵdefineNgModule" node_modules/@angular/core/fesm2015/core.js assertSucceeded "Expected 'ngcc' to correctly compile 'ApplicationModule' in '@angular/core' (fesm2015)." - grep "ApplicationModule.ɵmod = ɵɵdefineNgModule" node_modules/@angular/core/bundles/core.umd.js + grep "ApplicationModule.ɵmod = /\*@__PURE__\*/ ɵɵdefineNgModule" node_modules/@angular/core/bundles/core.umd.js assertSucceeded "Expected 'ngcc' to correctly compile 'ApplicationModule' in '@angular/core' (main)." - grep "ApplicationModule.ɵmod = ɵngcc0.ɵɵdefineNgModule" node_modules/@angular/core/esm2015/src/application_module.js + grep "ApplicationModule.ɵmod = /\*@__PURE__\*/ ɵngcc0.ɵɵdefineNgModule" node_modules/@angular/core/esm2015/src/application_module.js assertSucceeded "Expected 'ngcc' to correctly compile 'ApplicationModule' in '@angular/core' (esm2015)." # Did it place the `setClassMetadata` call correctly? @@ -99,23 +97,23 @@ assertSucceeded "Expected 'ngcc' to log 'Compiling'." grep "import [*] as ɵngcc0 from './src/r3_symbols';" node_modules/@angular/core/core.d.ts assertSucceeded "Expected 'ngcc' to add an import for 'src/r3_symbols' in '@angular/core' typings." - grep "static ɵinj: ɵngcc0.ɵɵInjectorDef;" node_modules/@angular/core/core.d.ts + grep "static ɵinj: ɵngcc0.ɵɵInjectorDeclaration;" node_modules/@angular/core/core.d.ts assertSucceeded "Expected 'ngcc' to add a definition for 'ApplicationModule.ɵinj' in '@angular/core' typings." # Did it generate a base factory call for synthesized constructors correctly? - grep "const ɵMatTable_BaseFactory = /\*@__PURE__\*/ ɵngcc0.ɵɵgetInheritedFactory(MatTable);" node_modules/@angular/material/esm2015/table/table.js + grep "/\*@__PURE__\*/ function () { let ɵMatTable_BaseFactory; return function MatTable_Factory(t) { return (ɵMatTable_BaseFactory || (ɵMatTable_BaseFactory = ɵngcc0.ɵɵgetInheritedFactory(MatTable)))(t || MatTable); }; }();" node_modules/@angular/material/esm2015/table/table.js assertSucceeded "Expected 'ngcc' to generate a base factory for 'MatTable' in '@angular/material' (esm2015)." - grep "var ɵMatTable_BaseFactory = /\*@__PURE__\*/ ɵngcc0.ɵɵgetInheritedFactory(MatTable);" node_modules/@angular/material/esm5/table/table.js + grep "/\*@__PURE__\*/ function () { var ɵMatTable_BaseFactory; return function MatTable_Factory(t) { return (ɵMatTable_BaseFactory || (ɵMatTable_BaseFactory = ɵngcc0.ɵɵgetInheritedFactory(MatTable)))(t || MatTable); }; }();" node_modules/@angular/material/esm5/table/table.js assertSucceeded "Expected 'ngcc' to generate a base factory for 'MatTable' in '@angular/material' (esm5)." # Did it generate an abstract directive definition for undecorated classes with inputs and view queries? - grep "_MatMenuBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: _MatMenuBase" node_modules/@angular/material/esm2015/menu/menu.js + grep "_MatMenuBase.ɵdir = /\*@__PURE__\*/ ɵngcc0.ɵɵdefineDirective({ type: _MatMenuBase" node_modules/@angular/material/esm2015/menu/menu.js assertSucceeded "Expected 'ngcc' to generate an abstract directive definition for 'MatMenuBase' in '@angular/material' (esm2015)." - grep "_MatMenuBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: _MatMenuBase" node_modules/@angular/material/esm5/menu/menu.js + grep "_MatMenuBase.ɵdir = /\*@__PURE__\*/ ɵngcc0.ɵɵdefineDirective({ type: _MatMenuBase" node_modules/@angular/material/esm5/menu/menu.js assertSucceeded "Expected 'ngcc' to generate an abstract directive definition for 'MatMenuBase' in '@angular/material' (esm5)." diff --git a/integration/ngcc/yarn.lock b/integration/ngcc/yarn.lock index ada8bff446..ab9921ebc5 100644 --- a/integration/ngcc/yarn.lock +++ b/integration/ngcc/yarn.lock @@ -3188,10 +3188,8 @@ webdriver-js-extender@2.1.0: "@types/selenium-webdriver" "^3.0.0" selenium-webdriver "^3.0.1" -webdriver-manager@^12.0.6: - version "12.1.7" - resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.1.7.tgz#ed4eaee8f906b33c146e869b55e850553a1b1162" - integrity sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA== +webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" diff --git a/integration/platform-server/yarn.lock b/integration/platform-server/yarn.lock index d7c4d260e2..b69ef506d0 100644 --- a/integration/platform-server/yarn.lock +++ b/integration/platform-server/yarn.lock @@ -4259,7 +4259,7 @@ webdriver-js-extender@2.1.0: selenium-webdriver "^3.0.1" webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": - version "12.1.7" + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" diff --git a/integration/trusted-types/src/app/app.component.html b/integration/trusted-types/src/app/app.component.html index 55cf73d056..5d47b9ff20 100644 --- a/integration/trusted-types/src/app/app.component.html +++ b/integration/trusted-types/src/app/app.component.html @@ -446,28 +446,6 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/integration/trusted-types/src/environments/environment.ts b/integration/trusted-types/src/environments/environment.ts index 7b4f817adb..30d7bccb19 100644 --- a/integration/trusted-types/src/environments/environment.ts +++ b/integration/trusted-types/src/environments/environment.ts @@ -13,4 +13,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/integration/trusted-types/src/polyfills.ts b/integration/trusted-types/src/polyfills.ts index 9b8f300ef6..785ab74a8a 100644 --- a/integration/trusted-types/src/polyfills.ts +++ b/integration/trusted-types/src/polyfills.ts @@ -55,7 +55,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** diff --git a/integration/trusted-types/src/test.ts b/integration/trusted-types/src/test.ts index 50193eb0f2..2042356408 100644 --- a/integration/trusted-types/src/test.ts +++ b/integration/trusted-types/src/test.ts @@ -1,6 +1,6 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/integration/trusted-types/yarn.lock b/integration/trusted-types/yarn.lock index 3f7143db58..7611bd00fd 100644 --- a/integration/trusted-types/yarn.lock +++ b/integration/trusted-types/yarn.lock @@ -8741,7 +8741,7 @@ webdriver-js-extender@2.1.0: selenium-webdriver "^3.0.1" webdriver-manager@^12.0.6, "webdriver-manager@file:../../node_modules/webdriver-manager": - version "12.1.7" + version "12.1.8" dependencies: adm-zip "^0.4.9" chalk "^1.1.1" diff --git a/integration/typings_test_ts40/include-all.ts b/integration/typings_test_ts40/include-all.ts deleted file mode 100644 index cf7c23338a..0000000000 --- a/integration/typings_test_ts40/include-all.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - - -import * as animations from '@angular/animations'; -import * as animationsBrowser from '@angular/animations/browser'; -import * as animationsBrowserTesting from '@angular/animations/browser/testing'; -import * as common from '@angular/common'; -import * as commonHttp from '@angular/common/http'; -import * as commonTesting from '@angular/common/testing'; -import * as commonHttpTesting from '@angular/common/testing'; -import * as compiler from '@angular/compiler'; -import * as compilerTesting from '@angular/compiler/testing'; -import * as core from '@angular/core'; -import * as coreTesting from '@angular/core/testing'; -import * as elements from '@angular/elements'; -import * as forms from '@angular/forms'; -import * as platformBrowser from '@angular/platform-browser'; -import * as platformBrowserDynamic from '@angular/platform-browser-dynamic'; -import * as platformBrowserDynamicTesting from '@angular/platform-browser-dynamic/testing'; -import * as platformBrowserAnimations from '@angular/platform-browser/animations'; -import * as platformBrowserTesting from '@angular/platform-browser/testing'; -import * as platformServer from '@angular/platform-server'; -import * as platformServerTesting from '@angular/platform-server/testing'; -import * as router from '@angular/router'; -import * as routerTesting from '@angular/router/testing'; -import * as routerUpgrade from '@angular/router/upgrade'; -import * as serviceWorker from '@angular/service-worker'; -import * as upgrade from '@angular/upgrade'; -import * as upgradeStatic from '@angular/upgrade/static'; -import * as upgradeTesting from '@angular/upgrade/static/testing'; - -export default { - animations, - animationsBrowser, - animationsBrowserTesting, - common, - commonTesting, - commonHttp, - commonHttpTesting, - compiler, - compilerTesting, - core, - coreTesting, - elements, - forms, - platformBrowser, - platformBrowserTesting, - platformBrowserDynamic, - platformBrowserDynamicTesting, - platformBrowserAnimations, - platformServer, - platformServerTesting, - router, - routerTesting, - routerUpgrade, - serviceWorker, - upgrade, - upgradeStatic, - upgradeTesting, -}; diff --git a/integration/typings_test_ts40/package.json b/integration/typings_test_ts40/package.json deleted file mode 100644 index c4a666ee33..0000000000 --- a/integration/typings_test_ts40/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "angular-integration", - "description": "Assert that users with TypeScript 4.0 can type-check an Angular application", - "version": "0.0.0", - "license": "MIT", - "dependencies": { - "@angular/animations": "file:../../dist/packages-dist/animations", - "@angular/common": "file:../../dist/packages-dist/common", - "@angular/compiler": "file:../../dist/packages-dist/compiler", - "@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli", - "@angular/core": "file:../../dist/packages-dist/core", - "@angular/elements": "file:../../dist/packages-dist/elements", - "@angular/forms": "file:../../dist/packages-dist/forms", - "@angular/platform-browser": "file:../../dist/packages-dist/platform-browser", - "@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic", - "@angular/platform-server": "file:../../dist/packages-dist/platform-server", - "@angular/router": "file:../../dist/packages-dist/router", - "@angular/service-worker": "file:../../dist/packages-dist/service-worker", - "@angular/upgrade": "file:../../dist/packages-dist/upgrade", - "@types/jasmine": "file:../../node_modules/@types/jasmine", - "rxjs": "file:../../node_modules/rxjs", - "typescript": "4.0.2", - "zone.js": "file:../../dist/zone.js-dist/archive/zone.js.tgz" - }, - "scripts": { - "test": "tsc" - } -} diff --git a/integration/typings_test_ts40/tsconfig.json b/integration/typings_test_ts40/tsconfig.json deleted file mode 100644 index 30e25c2209..0000000000 --- a/integration/typings_test_ts40/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "experimentalDecorators": true, - "module": "commonjs", - "moduleResolution": "node", - "outDir": "./dist/out-tsc", - "rootDir": ".", - "target": "es5", - "lib": [ - "es5", - "dom", - "es2015.collection", - "es2015.iterable", - "es2015.promise" - ], - "types": [], - }, - "files": [ - "include-all.ts", - "node_modules/@types/jasmine/index.d.ts" - ] -} diff --git a/integration/typings_test_ts41/include-all.ts b/integration/typings_test_ts41/include-all.ts index 7bf19f646c..86b57f2789 100644 --- a/integration/typings_test_ts41/include-all.ts +++ b/integration/typings_test_ts41/include-all.ts @@ -27,6 +27,7 @@ import * as platformBrowserDynamicTesting from '@angular/platform-browser-dynami import * as platformBrowserAnimations from '@angular/platform-browser/animations'; import * as platformBrowserTesting from '@angular/platform-browser/testing'; import * as platformServer from '@angular/platform-server'; +import * as platformServerInit from '@angular/platform-server/init'; import * as platformServerTesting from '@angular/platform-server/testing'; import * as router from '@angular/router'; import * as routerTesting from '@angular/router/testing'; @@ -56,6 +57,7 @@ export default { platformBrowserDynamicTesting, platformBrowserAnimations, platformServer, + platformServerInit, platformServerTesting, router, routerTesting, diff --git a/karma-js.conf.js b/karma-js.conf.js index 6f97a611f8..d7827983df 100644 --- a/karma-js.conf.js +++ b/karma-js.conf.js @@ -32,6 +32,8 @@ module.exports = function(config) { {pattern: 'node_modules/angular-mocks-1.5/angular-mocks.js', included: false, watched: false}, {pattern: 'node_modules/angular-1.6/angular?(.min).js', included: false, watched: false}, {pattern: 'node_modules/angular-mocks-1.6/angular-mocks.js', included: false, watched: false}, + {pattern: 'node_modules/angular-1.7/angular?(.min).js', included: false, watched: false}, + {pattern: 'node_modules/angular-mocks-1.7/angular-mocks.js', included: false, watched: false}, {pattern: 'node_modules/angular/angular?(.min).js', included: false, watched: false}, {pattern: 'node_modules/angular-mocks/angular-mocks.js', included: false, watched: false}, @@ -83,6 +85,7 @@ module.exports = function(config) { 'dist/all/@angular/compiler/test/render3/**', 'dist/all/@angular/core/test/bundling/**', 'dist/all/@angular/core/test/render3/ivy/**', + 'dist/all/@angular/core/test/render3/jit/**', 'dist/all/@angular/core/test/render3/perf/**', 'dist/all/@angular/elements/schematics/**', 'dist/all/@angular/examples/**/e2e_test/*', diff --git a/modules/benchmarks/src/class_bindings/BUILD.bazel b/modules/benchmarks/src/class_bindings/BUILD.bazel index 48c3cdec70..9832ec19ce 100644 --- a/modules/benchmarks/src/class_bindings/BUILD.bazel +++ b/modules/benchmarks/src/class_bindings/BUILD.bazel @@ -1,6 +1,6 @@ package(default_visibility = ["//modules/benchmarks:__subpackages__"]) -load("@io_bazel_rules_sass//sass:sass.bzl", "sass_binary") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") load("//dev-infra/benchmark/component_benchmark:component_benchmark.bzl", "component_benchmark") sass_binary( diff --git a/modules/playground/e2e_test/example_test.bzl b/modules/playground/e2e_test/example_test.bzl index 41fdc97a35..64da4af375 100644 --- a/modules/playground/e2e_test/example_test.bzl +++ b/modules/playground/e2e_test/example_test.bzl @@ -1,6 +1,6 @@ load("//tools:defaults.bzl", "protractor_web_test_suite", "ts_library") -def example_test(name, srcs, server, data = [], **kwargs): +def example_test(name, srcs, server, data = [], deps = [], **kwargs): ts_library( name = "%s_lib" % name, testonly = True, @@ -12,7 +12,7 @@ def example_test(name, srcs, server, data = [], **kwargs): "@npm//@types/jasminewd2", "@npm//@types/selenium-webdriver", "@npm//protractor", - ], + ] + deps, ) protractor_web_test_suite( diff --git a/modules/playground/e2e_test/sourcemap/BUILD.bazel b/modules/playground/e2e_test/sourcemap/BUILD.bazel index 943adc8d2a..94ec111680 100644 --- a/modules/playground/e2e_test/sourcemap/BUILD.bazel +++ b/modules/playground/e2e_test/sourcemap/BUILD.bazel @@ -8,4 +8,7 @@ example_test( "//modules/playground/src/sourcemap:index.ts", ], server = "//modules/playground/src/sourcemap:devserver", + deps = [ + "@npm//source-map", + ], ) diff --git a/modules/playground/e2e_test/sourcemap/sourcemap_spec.ts b/modules/playground/e2e_test/sourcemap/sourcemap_spec.ts index 2e51457d55..28f1ca4e1a 100644 --- a/modules/playground/e2e_test/sourcemap/sourcemap_spec.ts +++ b/modules/playground/e2e_test/sourcemap/sourcemap_spec.ts @@ -6,11 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ +import {readFileSync} from 'fs'; import {$, browser} from 'protractor'; import {logging} from 'selenium-webdriver'; - -const fs = require('fs'); -const sourceMap = require('source-map'); +import {RawSourceMap, SourceMapConsumer} from 'source-map'; describe('sourcemaps', function() { const URL = '/'; @@ -36,19 +35,17 @@ describe('sourcemaps', function() { const content = - fs.readFileSync(require.resolve('../../src/sourcemap/index.js')).toString('utf8'); + readFileSync(require.resolve('../../src/sourcemap/index.js')).toString('utf8'); const marker = '//# sourceMappingURL=data:application/json;base64,'; const index = content.indexOf(marker); const sourceMapData = Buffer.from(content.substring(index + marker.length), 'base64').toString('utf8'); - const decoder = new sourceMap.SourceMapConsumer(JSON.parse(sourceMapData)); - + const decoder = new SourceMapConsumer(JSON.parse(sourceMapData) as RawSourceMap); const originalPosition = decoder.originalPositionFor({line: errorLine, column: errorColumn}); - - const sourceCodeLines = fs.readFileSync(require.resolve('../../src/sourcemap/index.ts'), { - encoding: 'UTF-8' - }).split('\n'); + const sourceCodeLines = readFileSync(require.resolve('../../src/sourcemap/index.ts'), { + encoding: 'UTF-8' + }).split('\n'); expect(sourceCodeLines[originalPosition.line - 1]) .toMatch(/throw new Error\(\'Sourcemap test\'\)/); }); diff --git a/package.json b/package.json index 4c4fe0ef19..acac2f97d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-srcs", - "version": "11.1.0-next.0", + "version": "12.0.0-next.7", "private": true, "description": "Angular - a web framework for modern web apps", "homepage": "https://github.com/angular/angular", @@ -8,13 +8,23 @@ "license": "MIT", "//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", + "node": ">=10.19.0 <13.0.0", "yarn": ">=1.22.4 <2" }, "repository": { "type": "git", "url": "https://github.com/angular/angular.git" }, + "workspaces": { + "packages": [ + "packages/bazel", + "packages/compiler-cli", + "packages/localize", + "packages/platform-server", + "packages/misc/angular-in-memory-web-api", + "tools/ts-api-guardian" + ] + }, "scripts": { "/": "", "// 1": "Many developer of our checks/scripts/tools have moved to our ng-dev tool", @@ -23,7 +33,6 @@ "/ ": "", "preinstall": "node tools/yarn/check-yarn.js", "postinstall": "husky install && node scripts/webdriver-manager-update.js && node --preserve-symlinks --preserve-symlinks-main ./tools/postinstall-patches.js", - "check-env": "gulp check-env", "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", @@ -49,16 +58,17 @@ "@babel/cli": "^7.8.4", "@babel/core": "^7.8.6", "@babel/generator": "^7.8.6", + "@babel/parser": "^7.0.0", "@babel/preset-env": "^7.10.2", "@babel/template": "^7.8.6", "@babel/traverse": "^7.8.6", "@babel/types": "^7.8.6", - "@bazel/jasmine": "2.2.0", - "@bazel/karma": "2.2.0", - "@bazel/protractor": "2.2.0", - "@bazel/rollup": "2.2.0", - "@bazel/terser": "2.2.0", - "@bazel/typescript": "2.2.0", + "@bazel/concatjs": "3.0.0", + "@bazel/jasmine": "3.2.1", + "@bazel/protractor": "3.2.1", + "@bazel/rollup": "3.2.1", + "@bazel/terser": "3.2.1", + "@bazel/typescript": "3.2.1", "@microsoft/api-extractor": "7.7.11", "@octokit/rest": "16.28.7", "@octokit/types": "^5.0.1", @@ -73,6 +83,7 @@ "@types/chai": "^4.1.2", "@types/convert-source-map": "^1.5.1", "@types/diff": "^3.5.1", + "@types/events": "3.0.0", "@types/fs-extra": "4.0.2", "@types/hammerjs": "2.0.35", "@types/inquirer": "^7.3.0", @@ -90,19 +101,21 @@ "@types/yaml": "^1.9.7", "@types/yargs": "^15.0.5", "@webcomponents/custom-elements": "^1.1.0", - "angular": "npm:angular@1.7", + "angular": "npm:angular@1.8", "angular-1.5": "npm:angular@1.5", "angular-1.6": "npm:angular@1.6", - "angular-mocks": "npm:angular-mocks@1.7", + "angular-1.7": "npm:angular@1.7", + "angular-mocks": "npm:angular-mocks@1.8", "angular-mocks-1.5": "npm:angular-mocks@1.5", "angular-mocks-1.6": "npm:angular-mocks@1.6", + "angular-mocks-1.7": "npm:angular-mocks@1.7", "base64-js": "1.2.1", - "bluebird": "^3.5.5", + "bluebird": "^3.7.2", "brotli": "^1.3.2", "canonical-path": "1.0.0", - "chai": "^4.1.2", + "chai": "4.2.0", "chalk": "^2.3.1", - "chokidar": "^3.0.0", + "chokidar": "^3.5.1", "convert-source-map": "^1.5.1", "core-js": "^2.4.1", "dependency-graph": "^0.7.2", @@ -111,7 +124,7 @@ "fs-extra": "4.0.2", "google-closure-compiler": "20200517.0.0", "hammerjs": "2.0.8", - "http-server": "^0.11.1", + "http-server": "^0.12.3", "incremental-dom": "0.4.1", "jasmine": "^3.5.0", "jasmine-ajax": "^4.0.0", @@ -127,12 +140,12 @@ "magic-string": "0.25.4", "materialize-css": "1.0.0", "minimatch": "^3.0.4", - "minimist": "1.2.0", - "node-fetch": "^2.6.0", + "minimist": "^1.2.5", + "node-fetch": "^2.6.1", "node-uuid": "1.4.8", "nodejs-websocket": "^1.7.2", "protractor": "^5.4.2", - "puppeteer": "5.1.0", + "puppeteer": "5.4.1", "reflect-metadata": "^0.1.3", "requirejs": "^2.3.6", "rollup": "^2.16.1", @@ -141,48 +154,51 @@ "rollup-plugin-sourcemaps": "^0.4.2", "rxjs": "^6.5.3", "selenium-webdriver": "3.5.0", - "shelljs": "^0.8.3", + "shelljs": "^0.8.4", "source-map": "^0.6.1", "source-map-support": "0.5.9", "sourcemap-codec": "^1.4.8", "systemjs": "0.18.10", "terser": "^4.4.0", + "tmp": "0.0.33", "tsickle": "0.38.1", - "tslib": "^2.0.0", + "tslib": "^2.1.0", "tslint": "6.1.3", - "typescript": "~4.1.2", + "typescript": "~4.2.3", "xhr2": "0.2.0", "yaml": "^1.10.0", - "yargs": "^16.1.1" + "yargs": "^16.2.0" }, "// 2": "devDependencies are not used under Bazel. Many can be removed after test.sh is deleted.", "devDependencies": { "@angular/cli": "11.0.0-rc.1", - "@bazel/bazelisk": "^1.4.0", + "@bazel/bazelisk": "^1.7.5", "@bazel/buildifier": "^0.29.0", "@bazel/ibazel": "^0.12.3", "@octokit/graphql": "^4.3.1", "@types/cli-progress": "^3.4.2", + "@types/conventional-commits-parser": "^3.0.1", + "@types/git-raw-commits": "^2.0.0", "@types/minimist": "^1.2.0", "@yarnpkg/lockfile": "^1.1.0", - "browserstacktunnel-wrapper": "2.0.1", - "check-side-effects": "0.0.21", + "browserstacktunnel-wrapper": "^2.0.4", + "check-side-effects": "0.0.23", "clang-format": "^1.4.0", "cldr": "4.10.0", - "cldr-data-downloader": "0.3.2", + "cldr-data-downloader": "^0.3.5", "cldrjs": "0.5.0", "cli-progress": "^3.7.0", "conventional-changelog": "^2.0.3", + "conventional-commits-parser": "^3.2.1", "entities": "1.1.1", "firebase-tools": "^7.11.0", "firefox-profile": "1.0.3", + "git-raw-commits": "^2.0.10", "glob": "7.1.2", - "gulp": "3.9.1", - "gulp-conventional-changelog": "^2.0.3", + "gulp": "^4.0.2", + "gulp-conventional-changelog": "^2.0.35", "husky": "5.0.1", "inquirer": "^7.3.3", - "inquirer-autocomplete-prompt": "^1.0.2", - "jpm": "1.3.1", "karma-browserstack-launcher": "^1.3.0", "karma-sauce-launcher": "^2.0.2", "madge": "^3.6.0", @@ -206,8 +222,8 @@ "// 4": "Overwrite graceful-fs to a version that does not rely on the 'natives' package. This fixes gulp for >= 10.13, more information: #28213", "// 5": "Ensure a single version of webdriver-manager so it is hoisted as the integration tests depend on it being found at ../../node_modules/webdriver-manager", "resolutions": { - "**/graceful-fs": "4.2.2", - "**/webdriver-manager": "12.1.7" + "**/graceful-fs": "4.2.4", + "**/webdriver-manager": "12.1.8" }, "cldr-data-coverage": "full" } diff --git a/packages/animations/browser/src/dsl/animation_transition_factory.ts b/packages/animations/browser/src/dsl/animation_transition_factory.ts index 8c2a8cb073..80c93757d5 100644 --- a/packages/animations/browser/src/dsl/animation_transition_factory.ts +++ b/packages/animations/browser/src/dsl/animation_transition_factory.ts @@ -9,7 +9,7 @@ import {AnimationOptions, ɵStyleData} from '@angular/animations'; import {AnimationDriver} from '../render/animation_driver'; import {getOrSetAsInMap} from '../render/shared'; -import {copyObj, interpolateParams, iteratorToArray, mergeAnimationOptions} from '../util'; +import {copyObj, interpolateParams, iteratorToArray} from '../util'; import {StyleAst, TransitionAst} from './animation_ast'; import {buildAnimationTimelines} from './animation_timeline_builder'; 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 3653a20d5a..c4a6d20987 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 @@ -21,7 +21,6 @@ const TAB_SPACE = ' '; export class CssKeyframesDriver implements AnimationDriver { private _count = 0; private readonly _head: any = document.querySelector('head'); - private _warningIssued = false; validateStyleProperty(prop: string): boolean { return validateStyleProperty(prop); @@ -79,8 +78,8 @@ export class CssKeyframesDriver implements AnimationDriver { animate( element: any, keyframes: ɵStyleData[], duration: number, delay: number, easing: string, previousPlayers: AnimationPlayer[] = [], scrubberAccessRequested?: boolean): AnimationPlayer { - if (scrubberAccessRequested) { - this._notifyFaultyScrubber(); + if ((typeof ngDevMode === 'undefined' || ngDevMode) && scrubberAccessRequested) { + notifyFaultyScrubber(); } const previousCssKeyframePlayers = previousPlayers.filter( @@ -117,15 +116,6 @@ export class CssKeyframesDriver implements AnimationDriver { player.onDestroy(() => removeElement(kfElm)); return player; } - - private _notifyFaultyScrubber() { - if (!this._warningIssued) { - console.warn( - '@angular/animations: please load the web-animations.js polyfill to allow programmatic access...\n', - ' visit https://bit.ly/IWukam to learn more about using the web-animation-js polyfill.'); - this._warningIssued = true; - } - } } function flattenKeyframesIntoStyles(keyframes: null|{[key: string]: any}| @@ -146,3 +136,12 @@ function flattenKeyframesIntoStyles(keyframes: null|{[key: string]: any}| function removeElement(node: any) { node.parentNode.removeChild(node); } + +let warningIssued = false; +function notifyFaultyScrubber(): void { + if (warningIssued) return; + console.warn( + '@angular/animations: please load the web-animations.js polyfill to allow programmatic access...\n', + ' visit https://bit.ly/IWukam to learn more about using the web-animation-js polyfill.'); + warningIssued = true; +} diff --git a/packages/animations/browser/src/render/transition_animation_engine.ts b/packages/animations/browser/src/render/transition_animation_engine.ts index 79703bc234..d6f7547991 100644 --- a/packages/animations/browser/src/render/transition_animation_engine.ts +++ b/packages/animations/browser/src/render/transition_animation_engine.ts @@ -370,7 +370,11 @@ export class AnimationTransitionNamespace { prepareLeaveAnimationListeners(element: any) { const listeners = this._elementListeners.get(element); - if (listeners) { + const elementStates = this._engine.statesByElement.get(element); + + // if this statement fails then it means that the element was picked up + // by an earlier flush (or there are no listeners at all to track the leave). + if (listeners && elementStates) { const visitedTriggers = new Set(); listeners.forEach(listener => { const triggerName = listener.name; @@ -379,7 +383,6 @@ export class AnimationTransitionNamespace { const trigger = this._triggers[triggerName]; const transition = trigger.fallbackTransition; - 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); @@ -400,7 +403,6 @@ export class AnimationTransitionNamespace { removeNode(element: any, context: any): void { const engine = this._engine; - if (element.childElementCount) { this._signalRemovalForInnerTriggers(element, context); } 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 15df9eef13..b0b0caa661 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 @@ -156,6 +156,9 @@ export class WebAnimationsPlayer implements AnimationPlayer { } setPosition(p: number): void { + if (this.domPlayer === undefined) { + this.init(); + } this.domPlayer.currentTime = p * this.time; } diff --git a/packages/animations/browser/src/util.ts b/packages/animations/browser/src/util.ts index 2f9efc914d..24c51b1547 100644 --- a/packages/animations/browser/src/util.ts +++ b/packages/animations/browser/src/util.ts @@ -252,23 +252,6 @@ export function iteratorToArray(iterator: any): any[] { return arr; } -export function mergeAnimationOptions( - source: AnimationOptions, destination: AnimationOptions): AnimationOptions { - if (source.params) { - const p0 = source.params; - if (!destination.params) { - destination.params = {}; - } - const p1 = destination.params; - Object.keys(p0).forEach(param => { - if (!p1.hasOwnProperty(param)) { - p1[param] = p0[param]; - } - }); - } - return destination; -} - const DASH_CASE_REGEXP = /-+([a-z0-9])/g; export function dashCaseToCamelCase(input: string): string { return input.replace(DASH_CASE_REGEXP, (...m: any[]) => m[1].toUpperCase()); 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 8c8155aafd..b5543dd5fe 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 @@ -67,6 +67,27 @@ import {WebAnimationsPlayer} from '../../../src/render/web_animations/web_animat player.finish(); expect(log).toEqual(['started', 'done']); }); + + it('should allow setting position before animation is started', () => { + const player = new WebAnimationsPlayer(element, [], {duration: 1000}); + + player.setPosition(0.5); + const p = innerPlayer!; + expect(p.log).toEqual(['pause']); + expect(p.currentTime).toEqual(500); + }); + + it('should continue playing animations from setPosition', () => { + const player = new WebAnimationsPlayer(element, [], {duration: 1000}); + + player.play(); + const p = innerPlayer!; + expect(p.log).toEqual(['play']); + + player.setPosition(0.5); + expect(p.currentTime).toEqual(500); + expect(p.log).toEqual(['play']); + }); }); } diff --git a/packages/animations/package.json b/packages/animations/package.json index ab9225695b..7a9a1c50f3 100644 --- a/packages/animations/package.json +++ b/packages/animations/package.json @@ -5,7 +5,7 @@ "author": "angular", "license": "MIT", "dependencies": { - "tslib": "^2.0.0" + "tslib": "^2.1.0" }, "peerDependencies": { "@angular/core": "0.0.0-PLACEHOLDER" diff --git a/packages/animations/src/animation_builder.ts b/packages/animations/src/animation_builder.ts index a3956387e3..9e4435ec64 100644 --- a/packages/animations/src/animation_builder.ts +++ b/packages/animations/src/animation_builder.ts @@ -13,37 +13,23 @@ import {AnimationPlayer} from './players/animation_player'; * Angular component or directive. * Provided by the `BrowserAnimationsModule` or `NoopAnimationsModule`. * - * 一种可注入服务,可在 Angular 组件或指令中以编程的方式生成动画序列。由 `BrowserAnimationsModule` 或 `NoopAnimationsModule` 提供。 - * * @usageNotes * * To use this service, add it to your component or directive as a dependency. * The service is instantiated along with your component. * - * 要使用此服务,请将其作为依赖项添加到你的组件或指令中。该服务与你的组件一起实例化。 - * * Apps do not typically need to create their own animation players, but if you * do need to, follow these steps: * - * 应用通常不需要创建自己的动画播放器,但是如果需要,请按照以下步骤操作: - * * 1. Use the `build()` method to create a programmatic animation using the * `animate()` function. The method returns an `AnimationFactory` instance. * - * 使用 `build()` 方法创建一个用 `animate()` 函数创建的程序性动画。该方法返回一个 `AnimationFactory` 实例。 - * * 2. Use the factory object to create an `AnimationPlayer` and attach it to a DOM element. * - * 使用工厂对象创建 `AnimationPlayer` 并将其附加到 DOM 元素。 - * * 3. Use the player object to control the animation programmatically. * - * 使用播放器对象以编程方式控制动画。 - * * For example: * - * 例如: - * * ```ts * // import the service from BrowserAnimationsModule * import {AnimationBuilder} from '@angular/animations'; @@ -71,17 +57,8 @@ import {AnimationPlayer} from './players/animation_player'; export abstract class AnimationBuilder { /** * Builds a factory for producing a defined animation. - * - * 建立一个工厂来产生所定义的动画。 - * * @param animation A reusable animation definition. - * - * 可复用的动画定义。 - * * @returns A factory object that can create a player for the defined animation. - * - * 可以为所定义的动画创建播放器的工厂对象。 - * * @see `animate()` */ abstract build(animation: AnimationMetadata|AnimationMetadata[]): AnimationFactory; @@ -90,8 +67,6 @@ export abstract class AnimationBuilder { /** * A factory object returned from the `AnimationBuilder`.`build()` method. * - * 从 `AnimationBuilder`.`build()` 方法返回的工厂对象。。 - * * @publicApi */ export abstract class AnimationFactory { @@ -99,18 +74,9 @@ export abstract class AnimationFactory { * Creates an `AnimationPlayer` instance for the reusable animation defined by * the `AnimationBuilder`.`build()` method that created this factory. * Attaches the new player a DOM element. - * - * 创建一个 `AnimationPlayer` 实例,是由 `AnimationBuilder`.`build()` 方法定义的可复用动画。把这个新的播放器实例附加到一个 DOM 元素上。 - * * @param element The DOM element to which to attach the animation. - * - * 要将动画附加到的 DOM 元素。 - * * @param options A set of options that can include a time delay and * additional developer-defined parameters. - * - * 一组选项,可以包括延迟时间和其他由开发人员定义的参数。 - * */ abstract create(element: any, options?: AnimationOptions): AnimationPlayer; } diff --git a/packages/animations/src/animation_event.ts b/packages/animations/src/animation_event.ts index 98aff68d97..c377dada56 100644 --- a/packages/animations/src/animation_event.ts +++ b/packages/animations/src/animation_event.ts @@ -10,8 +10,6 @@ * An instance of this class is returned as an event parameter when an animation * callback is captured for an animation either during the start or done phase. * - * 在开始或完成阶段调用已捕获动画的回调时,将此类的实例作为事件参数返回。 - * * ```typescript * @Component({ * host: { @@ -42,52 +40,31 @@ export interface AnimationEvent { /** * The name of the state from which the animation is triggered. - * - * 触发动画的状态的名称。 - * */ fromState: string; /** * The name of the state in which the animation completes. - * - * 动画完成状态的名称。 - * */ toState: string; /** * The time it takes the animation to complete, in milliseconds. - * - * 动画完成所花费的时间(以毫秒为单位)。 - * */ totalTime: number; /** * The animation phase in which the callback was invoked, one of * "start" or "done". - * - * 调用此回调的动画阶段,是 "start" 或 "done" 之一。 - * */ phaseName: string; /** * The element to which the animation is attached. - * - * 动画附加到的元素。 - * */ element: any; /** * Internal. - * - * 内部。 - * */ triggerName: string; /** * Internal. - * - * 内部。 - * */ disabled: boolean; } diff --git a/packages/animations/src/animation_metadata.ts b/packages/animations/src/animation_metadata.ts index a25270f6fe..2ad8afd1f6 100755 --- a/packages/animations/src/animation_metadata.ts +++ b/packages/animations/src/animation_metadata.ts @@ -8,8 +8,6 @@ /** * Represents a set of CSS styles for use in an animation style. - * - * 表示一组要用在动画样式中的 CSS 样式。 */ export interface ɵStyleData { [key: string]: string|number; @@ -17,10 +15,7 @@ export interface ɵStyleData { /** * Represents animation-step timing parameters for an animation step. - * - * 表示一个动画步骤中的动画时序参数。 - * -* @see `animate()` + * @see `animate()` * * @publicApi */ @@ -29,26 +24,17 @@ export declare type AnimateTimings = { * The full duration of an animation step. A number and optional time unit, * such as "1s" or "10ms" for one second and 10 milliseconds, respectively. * The default unit is milliseconds. - * - * 此动画步骤的完整持续时间。包括一个数字和一个可选的时间单位,比如 "1s" 表示一秒 或 "10ms" 表示 10 毫秒。 - * 默认的单位是毫秒。 */ duration: number, /** * The delay in applying an animation step. A number and optional time unit. * The default unit is milliseconds. - * - * 此动画步骤的延迟时间。包括一个数字和一个可选的时间单位。 - * 默认单位是毫秒。 */ delay: number, /** * An easing style that controls how an animations step accelerates * and decelerates during its run time. An easing function such as `cubic-bezier()`, * or one of the following constants: - * - * 一种缓动风格,用于控制此动画步骤在运行期间如何加速和减速。动画函数或者是像 `cubic-bezier()` 这样的函数,或者是下列常量之一: - * * - `ease-in` * - `ease-out` * - `ease-in-and-out` @@ -59,12 +45,8 @@ export declare type AnimateTimings = { /** * @description Options that control animation styling and timing. * - * 用来控制动画样式和时序的选项。 - * * The following animation functions accept `AnimationOptions` data: * - * 下列动画函数可以接受 `AnimationOptions` 数据: - * * - `transition()` * - `sequence()` * - `{@link animations/group group()}` @@ -76,8 +58,6 @@ export declare type AnimateTimings = { * Programmatic animations built using the `AnimationBuilder` service also * make use of `AnimationOptions`. * - * 利用 `AnimationBuilder` 服务构建程序化动画时也会用到 `AnimationBuilder`。 - * * @publicApi */ export declare interface AnimationOptions { @@ -86,29 +66,19 @@ export declare interface AnimationOptions { * A number and optional time unit, such as "1s" or "10ms" for one second * and 10 milliseconds, respectively.The default unit is milliseconds. * Default value is 0, meaning no delay. - * - * 设置第一个动画动作开始前的延迟时间。 - * 包括一个数字和一个可选的时间单位,比如 "1s" 表示一秒,"10ms" 表示十毫秒。 - * 默认单位是毫秒。 - * 默认值为 0,表示不延迟。 */ 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. - * - * 一组可由开发人员来定义的参数,用于在动画开始时修改样式和时序。 - * 这是一组键值对,它所提供的值会用做默认值。 - */ + */ params?: {[name: string]: any}; } /** * Adds duration options to control animation styling and timing for a child animation. * - * 添加持续时间选项,以控制子动画的动画样式和时序。 - * * @see `animateChild()` * * @publicApi @@ -120,118 +90,75 @@ export declare interface AnimateChildOptions extends AnimationOptions { /** * @description Constants for the categories of parameters that can be defined for animations. * - * 一组可以为动画定义参数类别的常量。 - * * A corresponding function defines a set of parameters for each category, and * collects them into a corresponding `AnimationMetadata` object. * - * 相应的函数为每个类别定义了一组参数,并把它们汇集进相应的 `AnimationMetadata` 对象中。 - * * @publicApi */ export const enum AnimationMetadataType { /** * Associates a named animation state with a set of CSS styles. * See `state()` - * - * 把一个命名的动画状态和一组 CSS 样式关联起来。 - * 参见 `state()`。 */ State = 0, /** * Data for a transition from one animation state to another. * See `transition()` - * - * 用于从一个动画状态过渡到另一个状态的数据。 - * 参见 `transition()` */ Transition = 1, /** * Contains a set of animation steps. * See `sequence()` - * - * 包含一组动画步骤。 - * 参见 `sequence()` */ Sequence = 2, /** * Contains a set of animation steps. * See `{@link animations/group group()}` - * - * 包含一组动画步骤。 - * 参见 `{@link animations/group group()}` */ Group = 3, /** * Contains an animation step. * See `animate()` - * - * 包含一个动画步骤。 - * 参见 `animate()` */ Animate = 4, /** * Contains a set of animation steps. * See `keyframes()` - * - * 包含一组动画步骤。 - * 参见 `keyframes()` */ Keyframes = 5, /** * Contains a set of CSS property-value pairs into a named style. * See `style()` - * - * 包含一组 CSS 属性键值对和命名样式的对照表。 - * 参见 `style()` */ Style = 6, /** * Associates an animation with an entry trigger that can be attached to an element. * See `trigger()` - * - * 把一个动画和一个可附加到元素上的触发器关联起来。 - * 参见 `trigger()` */ Trigger = 7, /** * Contains a re-usable animation. * See `animation()` - * - * 包含一个可复用的动画。 - * 参见 `animation()` */ Reference = 8, /** * Contains data to use in executing child animations returned by a query. * See `animateChild()` - * - * 包含一些数据,用于执行一些由查询返回的自动化。 - * 参见 `animateChild()` */ AnimateChild = 9, /** * Contains animation parameters for a re-usable animation. * See `useAnimation()` - * - * 包含一些供可复用动画使用的动画参数。 - * 参见 `useAnimation()` */ AnimateRef = 10, /** * Contains child-animation query data. * See `query()` - * - * 包含子动画的查询数据。 - * 参见 `query()` */ Query = 11, /** * Contains data for staggering an animation sequence. * See `stagger()` - * - * 包含动画序列的交错数据。 - * 参见 `stagger()` */ Stagger = 12 } @@ -239,8 +166,6 @@ export const enum AnimationMetadataType { /** * Specifies automatic styling. * - * 用于指定自动化样式。 - * * @publicApi */ export const AUTO_STYLE = '*'; @@ -248,8 +173,6 @@ export const AUTO_STYLE = '*'; /** * Base for animation data structures. * - * 动画数据结构的基类。 - * * @publicApi */ export interface AnimationMetadata { @@ -260,30 +183,21 @@ export interface AnimationMetadata { * Contains an animation trigger. Instantiated and returned by the * `trigger()` function. * - * 包含一个动画触发器。由 `trigger()` 函数实例化并返回。 - * * @publicApi */ export interface AnimationTriggerMetadata extends AnimationMetadata { /** * 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. - * - * 一个动画定义对象,包含一组状态和转场声明。 */ definitions: AnimationMetadata[]; /** * An options object containing a delay and * developer-defined parameters that provide styling defaults and * can be overridden on invocation. Default delay is 0. - * - * 一个选项对象,包含延迟和一些由开发人员定义的参数。这些参数用来提供样式的默认值,并且可以在调用时重写。 - * 默认的延迟为 0。 */ options: {params?: {[name: string]: any}}|null; } @@ -292,30 +206,21 @@ export interface AnimationTriggerMetadata extends AnimationMetadata { * Encapsulates an animation state by associating a state name with a set of CSS styles. * Instantiated and returned by the `state()` function. * - * 通过将状态名称和一组 CSS 样式相关联来封装一个动画状态。 - * 由 `state()` 函数实例化并返回。 - * * @publicApi */ export interface AnimationStateMetadata extends AnimationMetadata { /** * The state name, unique within the component. - * - * 状态名,在组件内要唯一。 */ name: string; /** * The CSS styles associated with this state. - * - * 与该状态相关联的一些 CSS 样式。 */ styles: AnimationStyleMetadata; /** * An options object containing * developer-defined parameters that provide styling defaults and * can be overridden on invocation. - * - * 一个配置对象,包含一些由开发人员定义的参数,以提供默认样式,并可以在调用时重写。 */ options?: {params: {[name: string]: any}}; } @@ -324,32 +229,23 @@ export interface AnimationStateMetadata extends AnimationMetadata { * Encapsulates an animation transition. Instantiated and returned by the * `transition()` function. * - * 封装一个转场动画。由 `transition()` 函数实例化并返回。 - * * @publicApi */ export interface AnimationTransitionMetadata extends AnimationMetadata { /** * An expression that describes a state change. - * - * 用于描述状态变更的表达式。 */ expr: string| ((fromState: string, toState: string, element?: any, params?: {[key: string]: any}) => boolean); /** * One or more animation objects to which this transition applies. - * - * 该转场动画所包含的一个或多个动画对象。 */ animation: AnimationMetadata|AnimationMetadata[]; /** * An options object containing a delay and * developer-defined parameters that provide styling defaults and * can be overridden on invocation. Default delay is 0. - * - * 一个配置对象,包含一个延迟和一些由开发人员定义的参数,这些参数用于提供样式的默认值,并可在调用时重写。 - * 默认延迟为 0。 */ options: AnimationOptions|null; } @@ -359,24 +255,17 @@ export interface AnimationTransitionMetadata extends AnimationMetadata { * Instantiated and returned by the `animation()` function, and * passed to the `useAnimation()` function. * - * 封装一个可复用的动画,包括一组独立的动画步骤。由 `animation()` 函数返回,并传给 `useAnimation()` 函数。 - * * @publicApi */ export interface AnimationReferenceMetadata extends AnimationMetadata { /** * One or more animation step objects. - * - * 一个或多个动画步骤对象。 */ animation: AnimationMetadata|AnimationMetadata[]; /** * An options object containing a delay and * developer-defined parameters that provide styling defaults and * can be overridden on invocation. Default delay is 0. - * - * 一个配置对象,包含一个延迟和一些由开发人员定义的参数,这些参数用于提供样式的默认值,并可在调用时重写。 - * 默认延迟为 0。 */ options: AnimationOptions|null; } @@ -385,27 +274,19 @@ export interface AnimationReferenceMetadata extends AnimationMetadata { * Encapsulates an animation query. Instantiated and returned by * the `query()` function. * - * 封装一个动画查询。由 `query()` 函数实例化并返回。 - * * @publicApi */ export interface AnimationQueryMetadata extends AnimationMetadata { /** * The CSS selector for this query. - * - * 该查询的 CSS 选择器。 */ selector: string; /** * One or more animation step objects. - * - * 一个或多个动画步骤对象。 */ animation: AnimationMetadata|AnimationMetadata[]; /** * A query options object. - * - * 一个查询选项对象。 */ options: AnimationQueryOptions|null; } @@ -414,15 +295,11 @@ export interface AnimationQueryMetadata extends AnimationMetadata { * Encapsulates a keyframes sequence. Instantiated and returned by * the `keyframes()` function. * - * 封装一个关键帧序列。由 `keyframes()` 函数进行实例化并返回。 - * * @publicApi */ export interface AnimationKeyframesSequenceMetadata extends AnimationMetadata { /** * An array of animation styles. - * - * 一个由动画样式构成的数组。 */ steps: AnimationStyleMetadata[]; } @@ -431,21 +308,15 @@ export interface AnimationKeyframesSequenceMetadata extends AnimationMetadata { * Encapsulates an animation style. Instantiated and returned by * the `style()` function. * - * 封装一个动画样式。由 `style()` 函数实例化并返回。 - * * @publicApi */ export interface AnimationStyleMetadata extends AnimationMetadata { /** * A set of CSS style properties. - * - * 一组 CSS 样式属性。 */ styles: '*'|{[key: string]: string | number}|Array<{[key: string]: string | number}|'*'>; /** * A percentage of the total animate time at which the style is to be applied. - * - * 应用该样式的那个时间点在总动画时序中的百分比。 */ offset: number|null; } @@ -454,21 +325,15 @@ export interface AnimationStyleMetadata extends AnimationMetadata { * Encapsulates an animation step. Instantiated and returned by * the `animate()` function. * - * 封装一个动画步骤。由 `animate()` 函数进行实例化并返回。 - * * @publicApi */ export interface AnimationAnimateMetadata extends AnimationMetadata { /** * The timing data for the step. - * - * 该步骤的时序数据。 */ timings: string|number|AnimateTimings; /** * A set of styles used in the step. - * - * 用在该步骤中的一组样式。 */ styles: AnimationStyleMetadata|AnimationKeyframesSequenceMetadata|null; } @@ -477,9 +342,6 @@ export interface AnimationAnimateMetadata extends AnimationMetadata { * Encapsulates a child animation, that can be run explicitly when the parent is run. * Instantiated and returned by the `animateChild` function. * - * 封装一个子动画,父动画可以显式的运行它。 - * 由 `animateChild` 函数进行初始化和返回。 - * * @publicApi */ export interface AnimationAnimateChildMetadata extends AnimationMetadata { @@ -487,9 +349,6 @@ export interface AnimationAnimateChildMetadata extends AnimationMetadata { * An options object containing a delay and * developer-defined parameters that provide styling defaults and * can be overridden on invocation. Default delay is 0. - * - * 一个配置对象,包含一个延迟和一些由开发人员定义的参数,这些参数用于提供样式的默认值,并可在调用时重写。 - * 默认延迟为 0。 */ options: AnimationOptions|null; } @@ -498,25 +357,17 @@ export interface AnimationAnimateChildMetadata extends AnimationMetadata { * Encapsulates a reusable animation. * Instantiated and returned by the `useAnimation()` function. * - * 封装一个可复用的动画。 - * 由 `useAnimation()` 函数实例化并返回。 - * * @publicApi */ export interface AnimationAnimateRefMetadata extends AnimationMetadata { /** * An animation reference object. - * - * 一个动画引用对象。 */ animation: AnimationReferenceMetadata; /** * An options object containing a delay and * developer-defined parameters that provide styling defaults and * can be overridden on invocation. Default delay is 0. - * - * 一个配置对象,包含一个延迟和一些由开发人员定义的参数,这些参数用于提供样式的默认值,并可在调用时重写。 - * 默认延迟为 0。 */ options: AnimationOptions|null; } @@ -525,25 +376,17 @@ export interface AnimationAnimateRefMetadata extends AnimationMetadata { * Encapsulates an animation sequence. * Instantiated and returned by the `sequence()` function. * - * 封装一个动画序列。 - * 由 `sequence()` 函数进行实例化并返回。 - * * @publicApi */ export interface AnimationSequenceMetadata extends AnimationMetadata { /** * An array of animation step objects. - * - * 一个由动画步骤对象构成的数组。 */ steps: AnimationMetadata[]; /** * An options object containing a delay and * developer-defined parameters that provide styling defaults and * can be overridden on invocation. Default delay is 0. - * - * 一个配置对象,包含一个延迟和一些由开发人员定义的参数,这些参数用于提供样式的默认值,并可在调用时重写。 - * 默认延迟为 0。 */ options: AnimationOptions|null; } @@ -552,25 +395,17 @@ export interface AnimationSequenceMetadata extends AnimationMetadata { * Encapsulates an animation group. * Instantiated and returned by the `{@link animations/group group()}` function. * - * 封装一个动画组。 - * 由 `{@link animations/group group()}` 函数实例化并返回。 - * * @publicApi */ export interface AnimationGroupMetadata extends AnimationMetadata { /** * One or more animation or style steps that form this group. - * - * 构成该组的一个或多个动画步骤或样式步骤。 */ steps: AnimationMetadata[]; /** * An options object containing a delay and * developer-defined parameters that provide styling defaults and * can be overridden on invocation. Default delay is 0. - * - * 一个配置对象,包含一个延迟和一些由开发人员定义的参数,这些参数用于提供样式的默认值,并可在调用时重写。 - * 默认延迟为 0。 */ options: AnimationOptions|null; } @@ -579,9 +414,6 @@ export interface AnimationGroupMetadata extends AnimationMetadata { * Encapsulates animation query options. * Passed to the `query()` function. * - * 封装一些动画查询选项。 - * 传给 `query()` 函数。 - * * @publicApi */ export declare interface AnimationQueryOptions extends AnimationOptions { @@ -590,18 +422,12 @@ export declare interface AnimationQueryOptions extends AnimationOptions { * A required query throws an error if no elements are retrieved when * the query is executed. An optional query does not. * - * 如果该查询是可选的,则为 `true`,如果必选则为 `false`。默认为 `false`。 - * 当执行该查询时,如果没有取到元素,则必选查询会抛出错误,而可选查询则不会。 */ optional?: boolean; /** * A maximum total number of results to return from the query. * If negative, results are limited from the end of the query list towards the beginning. * By default, results are not limited. - * - * 从查询中返回的结果数上限。 - * 如果为负值,则从查询列表的末尾往前进行截取,直到上限。 - * 默认情况下,结果无上限。 */ limit?: number; } @@ -610,24 +436,15 @@ export declare interface AnimationQueryOptions extends AnimationOptions { * Encapsulates parameters for staggering the start times of a set of animation steps. * Instantiated and returned by the `stagger()` function. * - * 封装一组动画步骤的起始时间的交错参数。 - * 由 `stagger()` 函数实例化并返回。 - * - * * @publicApi **/ export interface AnimationStaggerMetadata extends AnimationMetadata { /** * The timing data for the steps. - * - * 各个步骤的时序数据。 */ timings: string|number; /** * One or more animation steps. - * - * 一个或多个动画步骤。 - * */ animation: AnimationMetadata|AnimationMetadata[]; } @@ -637,51 +454,30 @@ export interface AnimationStaggerMetadata extends AnimationMetadata { * and `transition()` entries to be evaluated when the expression * bound to the trigger changes. * - * 创建一个有名字的动画触发器,包含一个 `state()` 和 `transition()` 的列表,当此触发器的绑定表达式发生变化时,它们就会重新求值。 - * * @param name An identifying string. - * - * 一个标识字符串。 - * * @param definitions An animation definition object, containing an array of `state()` * and `transition()` declarations. * - * 一个动画定义对象,包含由 `state()` 和 `transition()` 声明构成的数组。 - * * @return An object that encapsulates the trigger data. * - * 用于包装该触发器数据的对象。 - * * @usageNotes - * * Define an animation trigger in the `animations` section of `@Component` metadata. * In the template, reference the trigger by name and bind it to a trigger expression that * evaluates to a defined animation state, using the following format: * - * 在 `@Component` 元数据的 `animations` 部分定义一个动画触发器。 - * 在模板中,可以按名字引用该触发器,并把它绑定到一个触发器表达式,该表达式的求值结果是一个已定义的动画状态,格式如下: - * * `[@triggerName]="expression"` * * Animation trigger bindings convert all values to strings, and then match the * previous and current values against any linked transitions. * Booleans can be specified as `1` or `true` and `0` or `false`. * - * 动画触发器绑定会把所有值转换成字符串,然后根据其旧值和当前值匹配出一个转场动画。 - * 对于逻辑值,可以用 `'1'` 或 `'true'` 来代表 `true`,用 `'0'` 或 `'false'` 来代表 `false`。 - * * ### Usage Example * - * ### 用法范例 - * * The following example creates an animation trigger reference based on the provided * name value. * The provided animation value is expected to be an array consisting of state and * transition declarations. * - * 下面的例子使用指定的名字创建了一个动画触发器的引用。 - * 此动画的值应该是一个由状态声明和转场声明组成的数组。 - * * ```typescript * @Component({ * selector: "my-component", @@ -703,22 +499,15 @@ export interface AnimationStaggerMetadata extends AnimationMetadata { * The template associated with this component makes use of the defined trigger * by binding to an element within its template code. * - * 该组件的相关模板通过在代码中把一个已定义的触发器绑定到一个元素上来使用此动画。 - * * ```html * *

    * ``` * * ### Using an inline function - * - * ### 使用内联函数 - * * The `transition` animation method also supports reading an inline function which can decide * if its associated animation should be run. * - * `transition` 动画方法也支持读取内联函数,该函数可以决定是否应该运行相关的动画。 - * * ```typescript * // this method is run each time the `myAnimationTrigger` trigger value changes. * function myInlineMatcherFn(fromState: string, toState: string, element: any, params: {[key: @@ -744,22 +533,14 @@ export interface AnimationStaggerMetadata extends AnimationMetadata { * ``` * * ### Disabling Animations - * - * ### 禁用动画 - * * When true, the special animation control binding `@.disabled` binding prevents * all animations from rendering. * Place the `@.disabled` binding on an element to disable * animations on the element itself, as well as any inner animation triggers * within the element. * - * 当为真时,则一个特殊的动画控制绑定 `@.disabled` 将会在渲染时阻止所有动画。 - * 把 `@.disabled` 绑定放在元素上可以防止触发该元素自身的动画,及其子元素上的所有动画触发器。 - * * The following example shows how to use this feature: * - * 下面的例子展示了如何使用此特性: - * * ```typescript * @Component({ * selector: 'my-component', @@ -783,20 +564,12 @@ export interface AnimationStaggerMetadata extends AnimationMetadata { * When `@.disabled` is true, it prevents the `@childAnimation` trigger from animating, * along with any inner animations. * - * 当 `@.disabled` 为 `true` 时,它会阻止 `@childAnimation` 触发器等任何内部动画。 - * * ### Disable animations application-wide - * - * ### 在整个应用中禁用动画 - * * When an area of the template is set to have animations disabled, * **all** inner components have their animations disabled as well. * This means that you can disable all animations for an app * by placing a host binding set on `@.disabled` on the topmost Angular component. * - * 当模板中的一个区域设置为禁用动画时,其**所有**内部组件上的动画也会禁用。 - * 也就是说,只要你把 Angular 根组件上放一个 `@.disabled` 的宿主绑定即可。 - * * ```typescript * import {Component, HostBinding} from '@angular/core'; * @@ -811,29 +584,17 @@ export interface AnimationStaggerMetadata extends AnimationMetadata { * ``` * * ### Overriding disablement of inner animations - * - * ### 改写内部动画的禁用状态 - * * Despite inner animations being disabled, a parent animation can `query()` * for inner elements located in disabled areas of the template and still animate * them if needed. This is also the case for when a sub animation is * queried by a parent and then later animated using `animateChild()`. * - * 不管内部动画禁用与否,父动画总能 `query()` 模板里已禁用区域中的内部元素,如果需要,也可以播放它们。 - * 还有一种方式是当父动画查询到子动画后,用 `animateChild()` 来播放它。 - * * ### Detecting when an animation is disabled - * - * ### 检测某个动画何时被禁用 - * * If a region of the DOM (or the entire application) has its animations disabled, the animation * trigger callbacks still fire, but for zero seconds. When the callback fires, it provides * an instance of an `AnimationEvent`. If animations are disabled, * the `.disabled` flag on the event is true. * - * 如果 DOM 中的某个区域(或整个应用程序)的动画被禁用时,动画触发器的回调仍然会触发,但持续 0 秒。 - * 当回调被触发时,它会提供一个 `AnimationEvent` 的例子。如果动画被禁用了,则该事件上的 `.disabled` 标志为 `true`。 - * * @publicApi */ export function trigger(name: string, definitions: AnimationMetadata[]): AnimationTriggerMetadata { @@ -843,114 +604,56 @@ export function trigger(name: string, definitions: AnimationMetadata[]): Animati /** * Defines an animation step that combines styling information with timing information. * - * 定义一个动画步骤,它把一些样式信息和时序信息组合在一起。 - * * @param timings Sets `AnimateTimings` for the parent animation. * A string in the format "duration [delay] [easing]". - * - * 为父动画设置 `AnimateTimings`。它的字符串格式为 "持续时间 [延迟] [缓动效果]"。 - * * - Duration and delay are expressed as a number and optional time unit, * such as "1s" or "10ms" for one second and 10 milliseconds, respectively. * The default unit is milliseconds. - * - * 持续时间和延迟都用一个动画和一个可选的时间单位来表示,比如 "1s" 代表一秒,"10ms" 代表十毫秒。 - * 默认单位是毫秒。 - * * - The easing value controls how the animation accelerates and decelerates * during its runtime. Value is one of `ease`, `ease-in`, `ease-out`, * `ease-in-out`, or a `cubic-bezier()` function call. * If not supplied, no easing is applied. * - * 缓动效果的值控制该动画在运行期间如何加速和减速。它的取值是 `ease`、`ease-in`、`ease-out`、 - * `ease-in-out` 之一或一个 `cubic-bezier()` 函数调用。 - * 如果未提供,则没有缓动效果。 - * * For example, the string "1s 100ms ease-out" specifies a duration of * 1000 milliseconds, and delay of 100 ms, and the "ease-out" easing style, * which decelerates near the end of the duration. - * - * 比如,字符串 "1s 100ms ease-out" 指定了一个 1000 毫秒的持续时间,一个 100 毫秒的延迟和一个 "ease-out" 缓动效果,它会快结束时减速。 - * * @param styles Sets AnimationStyles for the parent animation. * A function call to either `style()` or `keyframes()` * that returns a collection of CSS style entries to be applied to the parent animation. * When null, uses the styles from the destination state. * This is useful when describing an animation step that will complete an animation; * see "Animating to the final state" in `transitions()`. - * - * 为父动画设置动画样式。 - * 调用 `style()` 或 `keyframes()` 函数会返回要应用于父动画中的一组 CSS 样式, - * 如果为 `null`,则使用目标状态中的样式,当描述一个某个动画的最后一步时,这很有用。 - * 参见 `transitions()` 中对"播放到最终状态"的说明。 - * * @returns An object that encapsulates the animation step. * - * 一个用于封装动画步骤的对象。 - * * @usageNotes - * * Call within an animation `sequence()`, `{@link animations/group group()}`, or * `transition()` call to specify an animation step * that applies given style data to the parent animation for a given amount of time. * - * 在一个 `sequence()`(动画序列)、`{@link animations/group group()}`(动画分组)或 `transition()`(转场动画)中调用本函数, - * 以定义一个动画步骤,来把指定的样式数据在父动画上播放指定的时长。 - * * ### Syntax Examples - * - * ### 语法范例 - * * **Timing examples** * - * **时序范例** - * * The following examples show various `timings` specifications. - * - * 下面的例子展示了各种 `timings`(时序)规范。 - * * - `animate(500)` : Duration is 500 milliseconds. - * - * `animate(500)`:持续 500 毫秒。 - * * - `animate("1s")` : Duration is 1000 milliseconds. - * - * `animate("1s")`:持续 1000 毫秒。 - * * - `animate("100ms 0.5s")` : Duration is 100 milliseconds, delay is 500 milliseconds. - * - * `animate("100ms 0.5s")`:持续 100 毫秒,延迟 500 毫秒。 - * * - `animate("5s ease-in")` : Duration is 5000 milliseconds, easing in. - * - * `animate("5s ease-in")`:持续 5000 毫秒,缓动进入(ease-in)。 - * * - `animate("5s 10ms cubic-bezier(.17,.67,.88,.1)")` : Duration is 5000 milliseconds, delay is 10 * milliseconds, easing according to a bezier curve. * - * `animate("5s 10ms cubic-bezier(.17,.67,.88,.1)")`:持续 5000 毫秒,延迟 10 毫秒,基于一条 Bezier 曲线进行缓动。 - * * **Style examples** * - * **样式范例** - * * The following example calls `style()` to set a single CSS style. - * - * 下面的例子调用 `style()` 来设置单个的 CSS 样式。 - * * ```typescript * animate(500, style({ background: "red" })) * ``` * The following example calls `keyframes()` to set a CSS style * to different values for successive keyframes. - * - * 下面的例子调用 `keyframes()` 来为各个相邻的关键帧分别设置 CSS 样式。 - * * ```typescript * animate(500, keyframes( * [ - * style({ background: "blue" })), - * style({ background: "red" })) + * style({ background: "blue" }), + * style({ background: "red" }) * ]) * ``` * @@ -966,26 +669,13 @@ export function animate( /** * @description Defines a list of animation steps to be run in parallel. * - * 定义一个可以并行运行的动画步骤列表。 - * * @param steps An array of animation step objects. - * - * 一个由动画步骤对象构成的数组。 - * * - When steps are defined by `style()` or `animate()` * function calls, each call within the group is executed instantly. - * - * 当步骤由 `style()` 或 `animate()` 的函数调用定义时,组中的每个调用都会立即执行。 - * * - To specify offset styles to be applied at a later time, define steps with * `keyframes()`, or use `animate()` calls with a delay value. - * - * 要指定供带有延迟的偏移样式,请使用 `keyframes()` 调用来定义步骤;如果要指定延迟的时长,则改用 `animate()` 调用。 - * * For example: * - * 例如: - * * ```typescript * group([ * animate("1s", style({ background: "black" })), @@ -997,25 +687,16 @@ export function animate( * developer-defined parameters that provide styling defaults and * can be overridden on invocation. * - * 一个配置对象,包含一个延迟和一些由开发人员定义的参数,这些参数用于提供样式的默认值,并可在调用时重写。 - * * @return An object that encapsulates the group data. * - * 一个封装了该组数据的对象。 - * * @usageNotes - * * Grouped animations are useful when a series of styles must be * animated at different starting times and closed off at different ending times. * - * 当一系列样式分别需要在不同的起始时间开始动画并在不同的结束时间停止时,分组动画非常有用。 - * * When called within a `sequence()` or a * `transition()` call, does not continue to the next * instruction until all of the inner animation steps have completed. * - * 当在 `sequence()` 或 `transition()` 中调用它时,除非完成所有内部动画步骤,否则不会执行后续步骤。 - * * @publicApi */ export function group( @@ -1026,21 +707,11 @@ export function group( /** * Defines a list of animation steps to be run sequentially, one by one. * - * 定义一个动画步骤列表,逐个依次运行它们。 - * * @param steps An array of animation step objects. - * - * 一个由动画步骤对象构成的数组。 - * * - Steps defined by `style()` calls apply the styling data immediately. - * - * 由 `style()` 调用定义的步骤会立即应用样式数据。 - * * - Steps defined by `animate()` calls apply the styling data over time * as specified by the timing data. * - * 由 `animate()` 调用定义的步骤会根据时序数据中的规定,在一段时间内应用样式数据。 - * * ```typescript * sequence([ * style({ opacity: 0 }), @@ -1052,29 +723,18 @@ export function group( * developer-defined parameters that provide styling defaults and * can be overridden on invocation. * - * 一个配置对象,包含一个延迟和一些由开发人员定义的参数,这些参数会提供样式的默认值,并可在调用时重写。 - * * @return An object that encapsulates the sequence data. * - * 一个封装了该动画序列数据的对象。 - * * @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. * - * 当你把一个步骤数组传给 `transition()` 调用时,这些步骤默认会顺序执行。 - * 作为对比,`{@link animations/group group()}` 的调用会并行执行各个动画步骤。 - * * 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 * steps have completed. * - * 当在 `{@link animations/group group()}` 或 `transition()` 调用中应用动画序列时, - * 只有当每个内部动画步骤都完成之后,才会继续执行下一个指令。 - * * @publicApi **/ export function sequence( @@ -1087,43 +747,22 @@ export function sequence( * can then be used for an animation `state`, within an animation `sequence`, * or as styling data for calls to `animate()` and `keyframes()`. * - * 声明一个包含 CSS 属性/样式的键值对象,可在动画序列中用作动画状态(`state`),或在调用 `animate()` 和 `keyframes()` 时作为传入的样式数据。 - * * @param tokens A set of CSS styles or HTML styles associated with an animation state. * The value can be any of the following: - * - * 一组 CSS 样式或与动画状态相关联的 HTML 样式。 - * 它可以是下列值之一: - * * - A key-value style pair associating a CSS property with a value. - * - * 一个键值对,把 CSS 属性和值关联起来。 - * * - An array of key-value style pairs. - * - * 一组表示样式的键值对。 - * * - An asterisk (*), to use auto-styling, where styles are derived from the element * being animated and applied to the animation when it starts. * - * 一个星号(`*`),表示自动样式,其样式值会在应用此样式的时刻从目标元素中取得,并用作动画参数。 - * * Auto-styling can be used to define a state that depends on layout or other * environmental factors. * - * 自动样式可用于定义一个需要依赖布局或其它环境因素的状态。 - * * @return An object that encapsulates the style data. * - * 一个封装了样式数据的对象。 - * * @usageNotes - * * The following examples create animation styles that collect a set of * CSS property values: * - * 下面的例子创建了一些带有一组 CSS 属性值的动画样式: - * * ```typescript * // string values for CSS properties * style({ background: "red", color: "blue" }) @@ -1135,8 +774,6 @@ export function sequence( * The following example uses auto-styling to allow a component to animate from * a height of 0 up to the height of the parent element: * - * 下面的例子使用了自动样式,以允许此动画将组件的高度从 0 逐渐增长到其父元素的高度: - * * ``` * style({ height: 0 }), * animate("1s", style({ height: "*" })) @@ -1152,56 +789,30 @@ export function style(tokens: '*'|{[key: string]: string | number}| /** * Declares an animation state within a trigger attached to an element. * - * 在附加到元素的触发器上,声明一个动画状态。 - * * @param name One or more names for the defined state in a comma-separated string. * The following reserved state names can be supplied to define a style for specific use * cases: * - * 所定义的状态的一个或多个名字(用逗号分隔)。 - * 下面这些保留的状态名可用于为一些特殊用例定义样式: - * * - `void` You can associate styles with this name to be used when * the element is detached from the application. For example, when an `ngIf` evaluates * to false, the state of the associated element is void. - * - * `void` 你可以使用该名称关联一些样式,用于定义当该元素从应用中移除时的样式。比如,当 `ngIf` 的值为 `false` 时,相关元素的状态就是 `void`。 - * * - `*` (asterisk) Indicates the default state. You can associate styles with this name * to be used as the fallback when the state that is being animated is not declared * within the trigger. * - * `*`(星号)表示默认状态。当触发器中未声明要设置的动画状态时,就会把该名称所关联的样式用作回退(fallback)值。 - * * @param styles A set of CSS styles associated with this state, created using the * `style()` function. * This set of styles persists on the element once the state has been reached. - * - * 一组与该状态相关的 CSS 样式,使用 `style()` 函数创建。 - * 一旦到达该状态,这组样式就会永久性的应用在该元素上。 - * * @param options Parameters that can be passed to the state when it is invoked. * 0 or more key-value pairs. - * - * 一些在调用它时可传给该状态的参数。 - * 包含 0 或多个键值对。 - * * @return An object that encapsulates the new state data. * - * 一个封装了新状态数据的对象。 - * * @usageNotes - * * Use the `trigger()` function to register states to an animation trigger. * Use the `transition()` function to animate between states. * When a state is active within a component, its associated styles persist on the element, * even when the animation ends. * - * 使用 `trigger()` 函数来为动画触发器注册状态。 - * 使用 `transition()` 函数来在状态之间执行动画。 - * 当某个状态在组件中激活时,它所关联的样式会永久性的作用在该元素上 —— 即使该动画已经结束了。 - * - * * @publicApi **/ export function state( @@ -1213,40 +824,24 @@ export function state( /** * Defines a set of animation styles, associating each style with an optional `offset` value. * - * 可定义一组动画样式,每个样式都关联着一个可选的 `offset` 值。 - * * @param steps A set of animation styles with optional offset data. * The optional `offset` value for a style specifies a percentage of the total animation * time at which that style is applied. - * - * 一组带有可选的偏移(offset)数据的动画样式。 - * 这个可选的 `offset` 值为相应的样式指定一个相对于总体动画时间的百分比,决定何时应用此样式。 - * * @returns An object that encapsulates the keyframes data. * - * 一个封装关键帧数据的对象。 - * * @usageNotes - * * Use with the `animate()` call. Instead of applying animations * from the current state * to the destination state, keyframes describe how each style entry is applied and at what point * within the animation arc. * Compare [CSS Keyframe Animations](https://www.w3schools.com/css/css3_animations.asp). * - * 和 `animate()` 调用一起使用。关键帧动画不会直接在当前状态和目标状态之间应用动画,而是描述在动画弧线的哪个时间点上应用哪个样式。 - * 详情参见 [CSS 关键帧动画](https://www.w3schools.com/css/css3_animations.asp)。 - * * ### Usage * - * ### 用法 - * * In the following example, the offset values describe * when each `backgroundColor` value is applied. The color is red at the start, and changes to * blue when 20% of the total time has elapsed. * - * 下面的例子中,偏移值描述了每个 `backgroundColor` 值应该何时应用上去。开始时的颜色是红色,在总时间的 20% 处变为蓝色。 - * * ```typescript * // the provided offset values * animate("5s", keyframes([ @@ -1260,8 +855,6 @@ export function state( * If there are no `offset` values specified in the style entries, the offsets * are calculated automatically. * - * 如果没有指定 `offset` 值,则会自动计算偏移量。 - * * ```typescript * animate("5s", keyframes([ * style({ backgroundColor: "red" }) // offset = 0 @@ -1284,70 +877,34 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe * When the state criteria of a defined transition are met, the associated animation is * triggered. * - * 声明一个转场动画,以便在满足给定条件时运行一系列动画步骤。该条件是一个逻辑型表达式或一个函数, - * 该函数比较以前和现在的动画状态,如果应该开始转场则返回 `true`。 - * 当满足所定义的转场动画的状态标准时,就会开始执行相关的动画。 - * * @param stateChangeExpr A Boolean expression or function that compares the previous and current * animation states, and returns true if this transition should occur. Note that "true" and "false" * match 1 and 0, respectively. An expression is evaluated each time a state change occurs in the * animation trigger element. * The animation steps run when the expression evaluates to true. * - * 一个逻辑表达式或一个函数,该函数用来比较以前和现在的动画状态,如果应该开始转场,则返回 `true`。注意,"true" 和 "false" 分别对应于 1 和 0。 - * 在动画触发器所在的元素中,每当状态发生变化时该表达式都会求值一次。 - * 当该表达式求值为真时,则执行这些动画步骤。 - * * - A state-change string takes the form "state1 => state2", where each side is a defined animation * state, or an asterix (*) to refer to a dynamic start or end state. - * - * 一个 "state1 => state2" 格式的状态变更字符串,每一侧都是一个事先定义好的动画状态,或者用星号(`*`)来动态获取起始或结束状态。 - * * - The expression string can contain multiple comma-separated statements; * for example "state1 => state2, state3 => state4". - * - * 该表达式字符串可以包含多个逗号分隔的状态,比如 "state1 => state2, state3 => state4"。 - * * - Special values `:enter` and `:leave` initiate a transition on the entry and exit states, * equivalent to "void => *" and "* => void". - * - * 特殊值 `:enter` 表示进入此状态时的转场,等价于 "void => *",`:leave` 表示退出此状态时的转场,等价于 "* => void"。 - * * - Special values `:increment` and `:decrement` initiate a transition when a numeric value has * increased or decreased in value. - * - * 特殊值 `:increment`、`:decrement` 表示数字型值增加或减小时的转场。 - * * - A function is executed each time a state change occurs in the animation trigger element. * The animation steps run when the function returns true. * - * 一个函数,每当动画触发器所在的元素发生了状态变化时就会执行。 - * 当该函数返回 `true` 时,就会执行这些动画步骤。 - * * @param steps One or more animation objects, as returned by the `animate()` or * `sequence()` function, that form a transformation from one state to another. * A sequence is used by default when you pass an array. - * - * 一个或多个由 `animate()` 或 `sequence()` 函数返回的动画对象,用于描述从一个状态到另一个状态的转变过程。 - * 当传入一个数组时,默认当做一个动画序列使用。 - * * @param options An options object that can contain a delay value for the start of the animation, * and additional developer-defined parameters. Provided values for additional parameters are used * as defaults, and override values can be passed to the caller on invocation. - * - * 一个配置对象,可以包含一个开始动画之前的延迟值,和一些由开发人员定义的参数。在这些参数中提供的值会被用作样式的默认值, - * 在调用时调用者可以重写这些值。 - * * @returns An object that encapsulates the transition data. * - * 一个封装了转场数据的对象。 - * * @usageNotes - * * The template associated with a component binds an animation trigger to an element. * - * 与组件关联的模板会把动画触发器绑定到某个元素上。 - * * ```HTML * *
    ...
    @@ -1356,8 +913,6 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe * All transitions are defined within an animation trigger, * along with named states that the transitions change to and from. * - * 所有转场动画以及用于供转场动画使用的命名状态,都是在动画触发器中定义的, - * * ```typescript * trigger("myAnimationTrigger", [ * // define states @@ -1370,18 +925,11 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe * or a `transition()` call, execution does not continue to the next instruction * until each of the inner animation steps have completed. * - * 注意,当你在 `{@link animations/group group()}` 或 `transition()` 中调用 `sequence()` 函数时,除非其内部动画步骤已经执行完了, - * 否则不会继续执行后续步骤。 - * * ### Syntax examples * - * ### 语法范例 - * * The following examples define transitions between the two defined states (and default states), * using various options: * - * 下面的例子中定义了一些在两个已定义状态(和默认状态)之间的转场动画,使用了多种选项: - * * ```typescript * // Transition occurs when the state value * // bound to "myAnimationTrigger" changes from "on" to "off" @@ -1394,13 +942,9 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe * * ### Special values for state-change expressions * - * ### 状态变更表达式的一些特殊值 - * * - Catch-all state change for when an element is inserted into the page and the * destination state is unknown: * - * 当元素插入到页面中,并且目标状态未知时的所有状态变更: - * * ```typescript * transition("void => *", [ * style({ opacity: 0 }), @@ -1410,14 +954,10 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe * * - Capture a state change between any states: * - * 任意两个状态之间的变更: - * * `transition("* => *", animate("1s 0s"))` * * - Entry and exit transitions: * - * 进场和立场时的转场动画: - * * ```typescript * transition(":enter", [ * style({ opacity: 0 }), @@ -1430,8 +970,6 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe * * - Use `:increment` and `:decrement` to initiate transitions: * - * 使用 `:increment` 和 `:decrement` 来开始转场: - * * ```typescript * transition(":increment", group([ * query(':enter', [ @@ -1456,13 +994,9 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe * * ### State-change functions * - * ### 状态变更函数 - * * Here is an example of a `fromState` specified as a state-change function that invokes an * animation when true: * - * 下面的例子把 `fromState` 指定为状态变更函数,当它返回 `true` 时就会执行动画: - * * ```typescript * transition((fromState, toState) => * { @@ -1473,22 +1007,14 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe * * ### Animating to the final state * - * ### 把动画播放到最终状态 - * * If the final step in a transition is a call to `animate()` that uses a timing value * with no style data, that step is automatically considered the final animation arc, * for the element to reach the final state. Angular automatically adds or removes * CSS styles to ensure that the element is in the correct final state. * - * 如果转场动画的最后一步是调用 `animate()`,并且只传入时序参数却不带样式数据,则该步骤会被自动当做动画弧的终点, - * 以便让该元素达到最终状态。 - * Angular 会根据需要自动添加或移除 CSS 样式,以确保该元素处于正确的最终状态。 - * * The following example defines a transition that starts by hiding the element, * then makes sure that it animates properly to whatever state is currently active for trigger: * - * 下面的例子定义了一个转场动画,它先隐藏该元素,然后确保它可以正确设置到触发器处于激活状态时的动画: - * * ```typescript * transition("void => *", [ * style({ opacity: 0 }), @@ -1496,14 +1022,9 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe * ]) * ``` * ### Boolean value matching - * - * ### 逻辑值匹配 - * * If a trigger binding value is a Boolean, it can be matched using a transition expression * that compares true and false or 1 and 0. For example: * - * 如果触发器的绑定值是逻辑型的,它就可以使用一个与 `true`、`false` 或 1、0 进行比较的转场表达式进行匹配。例如: - * * ``` * // in the template *
    ...
    @@ -1529,34 +1050,19 @@ export function transition( * Produces a reusable animation that can be invoked in another animation or sequence, * by calling the `useAnimation()` function. * - * 生成一个可复用的动画,可以在其它动画或序列中通过 `useAnimation()` 函数进行调用。 - * * @param steps One or more animation objects, as returned by the `animate()` * or `sequence()` function, that form a transformation from one state to another. * A sequence is used by default when you pass an array. - * - * 一个或多个由 `animate()` 或 `sequence()` 函数返回的动画对象,用于描述从一个状态到另一个状态的转变过程。 - * 当传入一个数组时,默认当做一个动画序列使用。 - * * @param options An options object that can contain a delay value for the start of the * animation, and additional developer-defined parameters. * Provided values for additional parameters are used as defaults, * and override values can be passed to the caller on invocation. - * - * 一个配置对象,可以包含一个开始动画之前的延迟值,和一些由开发人员定义的参数。在这些参数中提供的值会被用作样式的默认值, - * 在调用时调用者可以重写这些值。 - * * @returns An object that encapsulates the animation data. * - * 一个封装了动画数据的对象。 - * * @usageNotes - * * The following example defines a reusable animation, providing some default parameter * values. * - * 下面的例子定义了一个可复用的动画,提供了一些默认的参数值。 - * * ```typescript * var fadeAnimation = animation([ * style({ opacity: '{{ start }}' }), @@ -1569,8 +1075,6 @@ export function transition( * The following invokes the defined animation with a call to `useAnimation()`, * passing in override parameter values. * - * 下面的例子通过 `useAnimation()` 执行了一个已定义的动画,并传入了一些参数值来改写默认参数。 - * * ```js * useAnimation(fadeAnimation, { * params: { @@ -1585,10 +1089,6 @@ export function transition( * the default values are used. If one or more parameter values are missing before a step is * animated, `useAnimation()` throws an error. * - * 如果本调用传入的参数中缺少了任何一个参数值,则会使用其默认值代替。 - * 如果在某个动画步骤开始播放前缺少了一个或多个参数值,则会抛出一个错误。 - * - * * @publicApi */ export function animation( @@ -1600,34 +1100,20 @@ export function animation( /** * Executes a queried inner animation element within an animation sequence. * - * 在一个动画序列中执行一个查询到的内部动画元素。 - * * @param options An options object that can contain a delay value for the start of the * animation, and additional override values for developer-defined parameters. - * - * 一个配置对象,它可以包含一个开始动画之前的延迟数,和一些由开发人员的定义的改写参数值。 - * * @return An object that encapsulates the child animation data. * - * 一个对象,封装了子动画数据。 - * * @usageNotes - * * Each time an animation is triggered in Angular, the parent animation * has priority and any child animations are blocked. In order * for a child animation to run, the parent animation must query each of the elements * containing child animations, and run them using this function. * - * 每当 Angular 触发动画时,总是父动画优先,而子动画被阻塞。 - * 为了执行子动画,父动画必须查询每个包含子动画的元素,并使用该函数运行它们。 - * * Note that this feature is designed to be used with `query()` and it will only work * with animations that are assigned using the Angular animation library. CSS keyframes * and transitions are not handled by this API. * - * 注意,设计该特性是为了和 `query()` 一起使用的,所以它只处理使用 Angular 动画库生成的动画。 - * 本 API 不会处理 CSS 关键帧动画和转场动画。 - * * @publicApi */ export function animateChild(options: AnimateChildOptions|null = null): @@ -1638,22 +1124,11 @@ export function animateChild(options: AnimateChildOptions|null = null): /** * Starts a reusable animation that is created using the `animation()` function. * - * 启动一个使用 `animation()` 函数创建的可复用动画。 - * * @param animation The reusable animation to start. - * - * 要启动的可复用动画。 - * * @param options An options object that can contain a delay value for the start of * the animation, and additional override values for developer-defined parameters. - * - * 一个配置对象,它包含一个启动动画之前的延迟值,和一些由开发人员定义的改写参数值。 - * * @return An object that contains the animation parameters. * - * 一个包含动画参数的对象。 - * - * * @publicApi */ export function useAnimation( @@ -1666,54 +1141,23 @@ export function useAnimation( * Finds one or more inner elements within the current element that is * being animated within a sequence. Use with `animate()`. * - * 在动画序列中正在播放的元素中查找一个或多个内部元素。和 `animateChild()` 一起使用。 - * * @param selector The element to query, or a set of elements that contain Angular-specific * characteristics, specified with one or more of the following tokens. - * - * 要查询的元素,或一组具有 Angular 中定义的某些特征的一组元素,可以用如下令牌(token)进行指定: - * * - `query(":enter")` or `query(":leave")` : Query for newly inserted/removed elements. - * - * `query(":enter")` 或 `query(":leave")`:查询新插入或移除的元素。 - * * - `query(":animating")` : Query all currently animating elements. - * - * `query(":animating")`:查询所有正在播放动画的元素。 - * * - `query("@triggerName")` : Query elements that contain an animation trigger. - * - * `query("@triggerName")`:查询包含指定动画触发器的元素。 - * * - `query("@*")` : Query all elements that contain an animation triggers. - * - * `query("@*")`:查询所有包含任意动画触发器的元素。 - * * - `query(":self")` : Include the current element into the animation sequence. * - * `query(":self")`:把当前元素包含到动画序列中。 - * * @param animation One or more animation steps to apply to the queried element or elements. * An array is treated as an animation sequence. - * - * 要应用到所查询到的单个或一组元素上的一个或多个动画步骤。 - * 该数组会被视为一个动画序列。 - * * @param options An options object. Use the 'limit' field to limit the total number of * items to collect. - * - * 一个配置对象。使用 `limit` 字段来限制要收集的条目的数量上限。 - * * @return An object that encapsulates the query data. * - * 一个封装了查询数据的对象。 - * * @usageNotes - * * Tokens can be merged into a combined query selector string. For example: * - * 多个令牌可以合并成复合查询选择器。比如: - * * ```typescript * query(':self, .record:enter, .record:leave, @subTrigger', [...]) * ``` @@ -1722,9 +1166,6 @@ export function useAnimation( * `element.querySelectorAll`. Use the `limit` field of an options object to limit * the total number of items to be collected. For example: * - * `query()` 函数会收集多个元素,其内部是用 `element.querySelectorAll` 实现的。 - * 用配置对象中的 `limit` 字段可以限制要收集的总数。比如: - * * ```js * query('div', [ * animate(...), @@ -1735,8 +1176,6 @@ export function useAnimation( * By default, throws an error when zero items are found. Set the * `optional` flag to ignore this error. For example: * - * 默认情况下,当没有找到条目时就会抛出错误。设置 `optional` 标志可以忽略此错误。比如: - * * ```js * query('.some-element-that-may-not-be-there', [ * animate(...), @@ -1746,13 +1185,9 @@ export function useAnimation( * * ### Usage Example * - * ### 使用范例 - * * The following example queries for inner elements and animates them * individually using `animate()`. * - * 下面的例子查询内部元素,并用 `animateChild()` 来独立控制它们的动画。 - * * ```typescript * @Component({ * selector: 'inner', @@ -1786,6 +1221,7 @@ export function useAnimation( * } * } * ``` + * * @publicApi */ export function query( @@ -1798,37 +1234,20 @@ export function query( * Use within an animation `query()` call to issue a timing gap after * each queried item is animated. * - * 在调用 `query()` 中使用可以在每个查询到的条目开始播放动画之后插入一个时间间隔。 - * * @param timings A delay value. - * - * 延迟值。 - * * @param animation One ore more animation steps. - * - * 一个或多个动画步骤。 - * * @returns An object that encapsulates the stagger data. * - * 一个封装了交错数据的对象。 - * * @usageNotes - * * In the following example, a container element wraps a list of items stamped out * by an `ngFor`. The container element contains an animation trigger that will later be set * to query for each of the inner items. * - * 在下面的例子中,容器元素包含一个由 `ngFor` 标记的列表。 - * 该容器包含一个动画触发器,用于稍后查询每个内部条目。 - * * Each time items are added, the opacity fade-in animation runs, * and each removed item is faded out. * When either of these animations occur, the stagger effect is * applied after each item's animation is started. * - * 每当新增条目后,就会执行一个透明度淡入动画,移除时则淡出。 - * 无论发生了哪个动画,都会在每个条目的动画开始之后,执行交错器的效果。 - * * ```html * * @@ -1842,8 +1261,6 @@ export function query( * * Here is the component code: * - * 下面是组件代码: - * * ```typescript * import {trigger, transition, style, animate, query, stagger} from '@angular/animations'; * @Component({ @@ -1873,8 +1290,6 @@ export function query( * * Here is the animation trigger code: * - * 下面是动画交错器代码: - * * ```typescript * trigger('listAnimation', [ * transition('* => *', [ // each time the binding value changes diff --git a/packages/animations/src/players/animation_group_player.ts b/packages/animations/src/players/animation_group_player.ts index a5fa06ced5..692f0b971c 100644 --- a/packages/animations/src/players/animation_group_player.ts +++ b/packages/animations/src/players/animation_group_player.ts @@ -148,12 +148,13 @@ export class AnimationGroupPlayer implements AnimationPlayer { } getPosition(): number { - let min = 0; - this.players.forEach(player => { - const p = player.getPosition(); - min = Math.min(p, min); - }); - return min; + const longestPlayer = + this.players.reduce((longestSoFar: AnimationPlayer|null, player: AnimationPlayer) => { + const newPlayerIsLongest = + longestSoFar === null || player.totalTime > longestSoFar.totalTime; + return newPlayerIsLongest ? player : longestSoFar; + }, null); + return longestPlayer != null ? longestPlayer.getPosition() : 0; } beforeDestroy(): void { diff --git a/packages/animations/src/players/animation_player.ts b/packages/animations/src/players/animation_player.ts index fb910f6488..607a996b6a 100644 --- a/packages/animations/src/players/animation_player.ts +++ b/packages/animations/src/players/animation_player.ts @@ -12,8 +12,6 @@ import {scheduleMicroTask} from '../util'; * built using the `build()` method of `AnimationBuilder`. The `build()` method * returns a factory, whose `create()` method instantiates and initializes this interface. * - * 提供对可复用动画序列的编程控制,该动画序列是使用 `AnimationBuilder` 的 `build()` 方法构建的。 `build()` 方法返回一个工厂,其 `create()` 方法将实例化并初始化此接口。 - * * @see `AnimationBuilder` * @see `AnimationFactory` * @see `animate()` @@ -23,143 +21,77 @@ import {scheduleMicroTask} from '../util'; export interface AnimationPlayer { /** * Provides a callback to invoke when the animation finishes. - * - * 提供当动画结束时要调用的回调。 - * * @param fn The callback function. - * - * 回调函数。 - * * @see `finish()` */ onDone(fn: () => void): void; /** * Provides a callback to invoke when the animation starts. - * - * 提供当动画启动时要调用的回调。 - * * @param fn The callback function. - * - * 回调函数。 - * * @see `run()` */ onStart(fn: () => void): void; /** * Provides a callback to invoke after the animation is destroyed. - * - * 提供当动画销毁后要调用的回调。 - * * @param fn The callback function. - * - * 回调函数。 - * * @see `destroy()` * @see `beforeDestroy()` */ onDestroy(fn: () => void): void; /** * Initializes the animation. - * - * 初始化本动画。 - * */ init(): void; /** * Reports whether the animation has started. - * - * 报告动画是否已开始。 - * * @returns True if the animation has started, false otherwise. - * - * 如果动画已开始,则为 true,否则为 false。 - * */ hasStarted(): boolean; /** * Runs the animation, invoking the `onStart()` callback. - * - * 运行动画,并调用 `onStart()` 回调。 - * */ play(): void; /** * Pauses the animation. - * - * 暂停动画。 - * */ pause(): void; /** * Restarts the paused animation. - * - * 重新开始已暂停的动画。 - * */ restart(): void; /** * Ends the animation, invoking the `onDone()` callback. - * - * 结束动画,并调用 `onDone()` 回调。 - * */ finish(): void; /** * Destroys the animation, after invoking the `beforeDestroy()` callback. * Calls the `onDestroy()` callback when destruction is completed. - * - * 在调用 `beforeDestroy()` 回调后销毁动画。销毁完成时调用 `onDestroy()`。 - * */ destroy(): void; /** * Resets the animation to its initial state. - * - * 将动画重置为其初始状态。 - * */ reset(): void; /** * Sets the position of the animation. - * - * 设置动画的位置。 - * * @param position A 0-based offset into the duration, in milliseconds. - * - * 持续时间中从 0 开始的偏移量,以毫秒为单位。 - * */ setPosition(position: any /** TODO #9100 */): void; /** * Reports the current position of the animation. - * - * 报告动画的当前位置。 - * * @returns A 0-based offset into the duration, in milliseconds. - * - * 持续时间中从 0 开始的偏移量,以毫秒为单位。 - * */ getPosition(): number; /** * The parent of this player, if any. - * - * 此播放器的父项(如果有)。 - * */ parentPlayer: AnimationPlayer|null; /** * The total run time of the animation, in milliseconds. - * - * 动画的总运行时间(以毫秒为单位)。 - * */ readonly totalTime: number; /** * Provides a callback to invoke before the animation is destroyed. - * - * 提供在动画销毁之前要调用的回调。 - * */ beforeDestroy?: () => any; /** @@ -179,8 +111,6 @@ export interface AnimationPlayer { * Used internally when animations are disabled, to avoid * checking for the null case when an animation player is expected. * - * 用于可复用动画的空白程序控制器。当禁用动画时在内部使用,以免在要用动画播放器时检查其是否为 null。 - * * @see `animate()` * @see `AnimationPlayer` * @see `GroupPlayer` @@ -194,6 +124,7 @@ export class NoopAnimationPlayer implements AnimationPlayer { private _started = false; private _destroyed = false; private _finished = false; + private _position = 0; public parentPlayer: AnimationPlayer|null = null; public readonly totalTime: number; constructor(duration: number = 0, delay: number = 0) { @@ -254,9 +185,11 @@ export class NoopAnimationPlayer implements AnimationPlayer { } } reset(): void {} - setPosition(position: number): void {} + setPosition(position: number): void { + this._position = this.totalTime ? position * this.totalTime : 1; + } getPosition(): number { - return 0; + return this.totalTime ? this._position / this.totalTime : 1; } /** @internal */ diff --git a/packages/bazel/BUILD.bazel b/packages/bazel/BUILD.bazel index 8ac0f195f6..f5b10a87c9 100644 --- a/packages/bazel/BUILD.bazel +++ b/packages/bazel/BUILD.bazel @@ -17,7 +17,7 @@ pkg_npm( "//packages/bazel/docs", ], substitutions = { - "(#|\/\/)\\s+BEGIN-DEV-ONLY[\\w\W]+?(#|\/\/)\\s+END-DEV-ONLY": "", + "(#|//)\\s+BEGIN-DEV-ONLY[\\w\\W]+?(#|//)\\s+END-DEV-ONLY": "", "//packages/bazel/": "//@angular/bazel/", "@npm//@bazel/typescript/internal:": "//@bazel/typescript/internal:", }, diff --git a/packages/bazel/index.bzl b/packages/bazel/index.bzl index 8c85c121ad..3c43d9f1cc 100644 --- a/packages/bazel/index.bzl +++ b/packages/bazel/index.bzl @@ -7,10 +7,11 @@ Users should not load files under "/src" """ -load("//packages/bazel/src/ng_package:ng_package.bzl", _ng_package = "ng_package") +load("//packages/bazel/src/ng_package:ng_package.bzl", _ng_package = "ng_package_macro") load("//packages/bazel/src:ng_module.bzl", _ng_module = "ng_module_macro") ng_module = _ng_module ng_package = _ng_package + # DO NOT ADD PUBLIC API without including in the documentation generation # Run `yarn bazel build //packages/bazel/docs` to verify diff --git a/packages/bazel/package.bzl b/packages/bazel/package.bzl index 4ebf61a888..a2d475788d 100644 --- a/packages/bazel/package.bzl +++ b/packages/bazel/package.bzl @@ -29,11 +29,11 @@ def rules_angular_dev_dependencies(): _maybe( http_archive, name = "bazel_toolchains", - sha256 = "4fb3ceea08101ec41208e3df9e56ec72b69f3d11c56629d6477c0ff88d711cf7", - strip_prefix = "bazel-toolchains-3.6.0", + sha256 = "1adf5db506a7e3c465a26988514cfc3971af6d5b3c2218925cd6e71ee443fc3f", + strip_prefix = "bazel-toolchains-4.0.0", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/releases/download/3.6.0/bazel-toolchains-3.6.0.tar.gz", - "https://github.com/bazelbuild/bazel-toolchains/releases/download/3.6.0/bazel-toolchains-3.6.0.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/releases/download/4.0.0/bazel-toolchains-4.0.0.tar.gz", + "https://github.com/bazelbuild/bazel-toolchains/releases/download/4.0.0/bazel-toolchains-4.0.0.tar.gz", ], ) @@ -43,11 +43,11 @@ def rules_angular_dev_dependencies(): _maybe( http_archive, name = "io_bazel_rules_sass", - sha256 = "77e241148f26d5dbb98f96fe0029d8f221c6cb75edbb83e781e08ac7f5322c5f", - strip_prefix = "rules_sass-1.24.0", + sha256 = "596ab3616d370135e0ecc710e103422e0aa3719f1c970303a0886b70c81ee819", + strip_prefix = "rules_sass-1.32.2", urls = [ - "https://github.com/bazelbuild/rules_sass/archive/1.24.0.zip", - "https://mirror.bazel.build/github.com/bazelbuild/rules_sass/archive/1.24.0.zip", + "https://github.com/bazelbuild/rules_sass/archive/1.32.2.zip", + "https://mirror.bazel.build/github.com/bazelbuild/rules_sass/archive/1.32.2.zip", ], ) diff --git a/packages/bazel/package.json b/packages/bazel/package.json index aa65c45fad..833c67abb2 100644 --- a/packages/bazel/package.json +++ b/packages/bazel/package.json @@ -20,7 +20,7 @@ } }, "dependencies": { - "@microsoft/api-extractor": "^7.7.13", + "@microsoft/api-extractor": "7.7.11", "shelljs": "0.8.2", "tsickle": "^0.38.0" }, @@ -28,7 +28,7 @@ "@angular/compiler-cli": "0.0.0-PLACEHOLDER", "@bazel/typescript": ">=1.0.0", "terser": "^4.3.1", - "typescript": ">=4.0 <4.2", + "typescript": ">=4.2.3 <4.3", "rollup": ">=1.20.0", "rollup-plugin-commonjs": ">=9.0.0", "rollup-plugin-node-resolve": ">=4.2.0", diff --git a/packages/bazel/src/BUILD.bazel b/packages/bazel/src/BUILD.bazel index d83cc00e8e..4b5587150a 100644 --- a/packages/bazel/src/BUILD.bazel +++ b/packages/bazel/src/BUILD.bazel @@ -11,16 +11,12 @@ exports_files(glob(["*.bzl"])) load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") -filegroup( - name = "empty_node_modules", - srcs = [], -) - nodejs_binary( name = "modify_tsconfig", - data = ["modify_tsconfig.js"], + data = [ + "modify_tsconfig.js", + ], entry_point = ":modify_tsconfig.js", - node_modules = ":empty_node_modules", visibility = ["//visibility:public"], ) # END-DEV-ONLY diff --git a/packages/bazel/src/api-extractor/BUILD.bazel b/packages/bazel/src/api-extractor/BUILD.bazel index 6490cab609..023246ceb3 100644 --- a/packages/bazel/src/api-extractor/BUILD.bazel +++ b/packages/bazel/src/api-extractor/BUILD.bazel @@ -24,6 +24,9 @@ nodejs_binary( "@npm//@microsoft/api-extractor", ], entry_point = ":index.ts", + # TODO(josephperrott): update dependency usages to no longer need bazel patch module resolver + # See: https://github.com/bazelbuild/rules_nodejs/wiki#--bazel_patch_module_resolver-now-defaults-to-false-2324 + templated_args = ["--bazel_patch_module_resolver"], visibility = ["//visibility:public"], ) diff --git a/packages/bazel/src/ng_module.bzl b/packages/bazel/src/ng_module.bzl index 7c18c555dc..febce716c5 100644 --- a/packages/bazel/src/ng_module.bzl +++ b/packages/bazel/src/ng_module.bzl @@ -26,9 +26,19 @@ load( "tsc_wrapped_tsconfig", ) +# enable_perf_logging controls whether Ivy's performance tracing system will be enabled for any +# compilation which includes this provider. +NgPerfInfo = provider(fields = ["enable_perf_logging"]) + _FLAT_DTS_FILE_SUFFIX = ".bundle.d.ts" _R3_SYMBOLS_DTS_FILE = "src/r3_symbols.d.ts" +def is_perf_requested(ctx): + enable_perf_logging = ctx.attr.perf_flag != None and ctx.attr.perf_flag[NgPerfInfo].enable_perf_logging == True + if enable_perf_logging and not is_ivy_enabled(ctx): + fail("Angular View Engine does not support performance tracing") + return enable_perf_logging + def is_ivy_enabled(ctx): """Determine if the ivy compiler should be used to by the ng_module. @@ -278,6 +288,15 @@ def _expected_outs(ctx): else: i18n_messages_files = [] + dev_perf_files = [] + prod_perf_files = [] + + # In Ivy mode, dev and prod builds both produce a .json output containing performance metrics + # from the compiler for that build. + if is_perf_requested(ctx): + dev_perf_files = [ctx.actions.declare_file(ctx.label.name + "_perf_dev.json")] + prod_perf_files = [ctx.actions.declare_file(ctx.label.name + "_perf_prod.json")] + return struct( closure_js = closure_js_files, devmode_js = devmode_js_files, @@ -288,6 +307,8 @@ def _expected_outs(ctx): dts_bundles = dts_bundles, bundle_index_typings = bundle_index_typings, i18n_messages = i18n_messages_files, + dev_perf_files = dev_perf_files, + prod_perf_files = prod_perf_files, ) # Determines if we need to generate View Engine shims (.ngfactory and .ngsummary files) @@ -318,6 +339,7 @@ def _ngc_tsconfig(ctx, files, srcs, **kwargs): # Summaries are only enabled if Angular outputs are to be produced. "enableSummariesForJit": is_legacy_ngc, "enableIvy": is_ivy_enabled(ctx), + "compilationMode": ctx.attr.compilation_mode, "fullTemplateTypeCheck": ctx.attr.type_check, # In Google3 we still want to use the symbol factory re-exports in order to # not break existing apps inside Google. Unlike Bazel, Google3 does not only @@ -336,6 +358,15 @@ def _ngc_tsconfig(ctx, files, srcs, **kwargs): "_useManifestPathsAsModuleName": (not _is_bazel()), } + if is_perf_requested(ctx): + # In Ivy mode, set the `tracePerformance` Angular compiler option to enable performance + # metric output. + if "devmode_manifest" in kwargs: + perf_path = outs.dev_perf_files[0].path + else: + perf_path = outs.prod_perf_files[0].path + angular_compiler_options["tracePerformance"] = perf_path + if _should_produce_flat_module_outs(ctx): angular_compiler_options["flatModuleId"] = ctx.attr.module_name angular_compiler_options["flatModuleOutFile"] = _flat_module_out_file(ctx) @@ -519,6 +550,7 @@ def _compile_action( outputs, dts_bundles_out, messages_out, + perf_out, tsconfig_file, node_opts, compile_mode): @@ -563,12 +595,12 @@ def _compile_action( def _prodmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts): outs = _expected_outs(ctx) - return _compile_action(ctx, inputs, outputs + outs.closure_js, None, outs.i18n_messages, tsconfig_file, node_opts, "prodmode") + return _compile_action(ctx, inputs, outputs + outs.closure_js + outs.prod_perf_files, None, outs.i18n_messages, outs.prod_perf_files, tsconfig_file, node_opts, "prodmode") def _devmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts): outs = _expected_outs(ctx) - compile_action_outputs = outputs + outs.devmode_js + outs.declarations + outs.summaries + outs.metadata - _compile_action(ctx, inputs, compile_action_outputs, outs.dts_bundles, None, tsconfig_file, node_opts, "devmode") + compile_action_outputs = outputs + outs.devmode_js + outs.declarations + outs.summaries + outs.metadata + outs.dev_perf_files + _compile_action(ctx, inputs, compile_action_outputs, outs.dts_bundles, None, outs.dev_perf_files, tsconfig_file, node_opts, "devmode") def _ts_expected_outs(ctx, label, srcs_files = []): # rules_typescript expects a function with two or more arguments, but our @@ -694,6 +726,14 @@ NG_MODULE_ATTRIBUTES = { "filter_summaries": attr.bool(default = False), "type_check": attr.bool(default = True), "inline_resources": attr.bool(default = True), + "compilation_mode": attr.string( + doc = """Set the compilation mode for the Angular compiler. + + This attribute is a noop if Ivy is not enabled. + """, + values = ["partial", "full", ""], + default = "", + ), "no_i18n": attr.bool(default = False), "compiler": attr.label( doc = """Sets a different ngc compiler binary to use for this library. @@ -711,6 +751,16 @@ NG_MODULE_ATTRIBUTES = { executable = True, cfg = "host", ), + # In the angular/angular monorepo, //tools:defaults.bzl wraps the ng_module rule in a macro + # which sets this attribute to the //packages/compiler-cli:ng_perf flag. + # This is done to avoid exposing the flag to user projects, which would require: + # * defining the flag within @angular/bazel and referencing it correctly here, and + # * committing to the flag and its semantics (including the format of perf JSON files) + # as something users can depend upon. + "perf_flag": attr.label( + providers = [NgPerfInfo], + doc = "Private API to control production of performance metric JSON files", + ), "_supports_workers": attr.bool(default = True), } diff --git a/packages/bazel/src/ng_package/BUILD.bazel b/packages/bazel/src/ng_package/BUILD.bazel index fe8b8f1703..53d57140fd 100644 --- a/packages/bazel/src/ng_package/BUILD.bazel +++ b/packages/bazel/src/ng_package/BUILD.bazel @@ -32,6 +32,9 @@ nodejs_binary( # END-DEV-ONLY "//:node_modules/rollup/dist/bin/rollup" ), + # TODO(josephperrott): update dependency usages to no longer need bazel patch module resolver + # See: https://github.com/bazelbuild/rules_nodejs/wiki#--bazel_patch_module_resolver-now-defaults-to-false-2324 + templated_args = ["--bazel_patch_module_resolver"], ) exports_files([ @@ -46,7 +49,6 @@ load("@npm//@bazel/typescript:index.bzl", "ts_library") ts_library( name = "lib", srcs = glob(["*.ts"]), - node_modules = "@npm//typescript:typescript__typings", tsconfig = ":tsconfig.json", deps = [ "@npm//@types/node", @@ -70,5 +72,8 @@ nodejs_binary( "@npm//shelljs", ], entry_point = ":packager.ts", + # TODO(josephperrott): update dependency usages to no longer need bazel patch module resolver + # See: https://github.com/bazelbuild/rules_nodejs/wiki#--bazel_patch_module_resolver-now-defaults-to-false-2324 + templated_args = ["--bazel_patch_module_resolver"], ) # END-DEV-ONLY diff --git a/packages/bazel/src/ng_package/ng_package.bzl b/packages/bazel/src/ng_package/ng_package.bzl index c7823003fe..a73c066640 100644 --- a/packages/bazel/src/ng_package/ng_package.bzl +++ b/packages/bazel/src/ng_package/ng_package.bzl @@ -105,6 +105,7 @@ WELL_KNOWN_GLOBALS = {p: _global_name(p) for p in [ "@angular/forms", "@angular/core/testing", "@angular/core", + "@angular/platform-server/init", "@angular/platform-server/testing", "@angular/platform-server", "@angular/common/testing", @@ -331,7 +332,7 @@ def _filter_out_generated_files(files, extension, package_path = None): files_list = files.to_list() if type(files) == _DEPSET_TYPE else files for file in files_list: # If the "package_path" parameter has been specified, filter out files - # that do not start with the the specified package path. + # that do not start with the specified package path. if package_path and not file.short_path.startswith(package_path): continue @@ -811,3 +812,23 @@ ng_package = rule( """ ng_package produces an npm-ready package from an Angular library. """ + +def ng_package_macro(name, **kwargs): + ng_package( + name = name, + **kwargs + ) + native.alias( + name = name + ".pack", + actual = select({ + "@bazel_tools//src/conditions:host_windows": name + ".pack.bat", + "//conditions:default": name + ".pack.sh", + }), + ) + native.alias( + name = name + ".publish", + actual = select({ + "@bazel_tools//src/conditions:host_windows": name + ".publish.bat", + "//conditions:default": name + ".publish.sh", + }), + ) diff --git a/packages/bazel/src/ng_package/packager.ts b/packages/bazel/src/ng_package/packager.ts index 7f08ca943b..246a264a87 100644 --- a/packages/bazel/src/ng_package/packager.ts +++ b/packages/bazel/src/ng_package/packager.ts @@ -91,7 +91,7 @@ function main(args: string[]): number { const typeDefinitions = typeDefinitionsArg.split(',').filter(s => !!s); const srcs = srcsArg.split(',').filter(s => !!s); const dataFiles: string[] = dataArg.split(',').filter(s => !!s); - const modulesManifest = JSON.parse(modulesManifestArg); + const modulesManifest = JSON.parse(modulesManifestArg) as Record; const dtsBundles: string[] = dtsBundleArg.split(',').filter(s => !!s); /** @@ -226,7 +226,7 @@ function main(args: string[]): number { let content = fs.readFileSync(src, 'utf-8'); // Modify package.json files as necessary for publishing if (path.basename(src) === 'package.json') { - const packageJson = JSON.parse(content); + const packageJson = JSON.parse(content) as {[key: string]: string}; content = amendPackageJson(src, packageJson, false); const packageName = packageJson['name']; @@ -439,7 +439,7 @@ export * from '${srcDirRelative(inputPath, typingsFile.replace(/\.d\.tsx?$/, '') * @param typingsPath the typings bundle entrypoint */ function rewireMetadata(metadataPath: string, typingsPath: string): string { - const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf-8')); + const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf-8')) as {[key: string]: any}; let typingsRelativePath = normalizeSeparators(path.relative(path.dirname(metadataPath), typingsPath)); diff --git a/packages/bazel/src/ng_package/rollup.config.js b/packages/bazel/src/ng_package/rollup.config.js index bab13a4162..efcafcb02f 100644 --- a/packages/bazel/src/ng_package/rollup.config.js +++ b/packages/bazel/src/ng_package/rollup.config.js @@ -49,15 +49,16 @@ function fileExists(filePath) { // This resolver mimics the TypeScript Path Mapping feature, which lets us resolve // modules based on a mapping of short names to paths. -function resolveBazel( - importee, importer, baseDir = process.cwd(), resolve = require.resolve, root = rootDir) { +function resolveBazel(importee, importer) { log_verbose(`resolving '${importee}' from ${importer}`); + const baseDir = process.cwd(); + function resolveInRootDir(importee) { - var candidate = path.join(baseDir, root, importee); + var candidate = path.join(baseDir, rootDir, importee); log_verbose(`try to resolve '${importee}' at '${candidate}'`); try { - var result = resolve(candidate); + var result = require.resolve(candidate); return result; } catch (e) { return undefined; @@ -80,7 +81,7 @@ function resolveBazel( // relative import if (importer) { let importerRootRelative = path.dirname(importer); - const relative = path.relative(path.join(baseDir, root), importerRootRelative); + const relative = path.relative(path.join(baseDir, rootDir), importerRootRelative); if (!relative.startsWith('.')) { importerRootRelative = relative; } diff --git a/packages/bazel/src/ngc-wrapped/BUILD.bazel b/packages/bazel/src/ngc-wrapped/BUILD.bazel index 45421ab417..198caa6604 100644 --- a/packages/bazel/src/ngc-wrapped/BUILD.bazel +++ b/packages/bazel/src/ngc-wrapped/BUILD.bazel @@ -9,7 +9,6 @@ ts_library( "index.ts", ], module_name = "@angular/bazel", - node_modules = "@npm//typescript:typescript__typings", tsconfig = ":tsconfig.json", visibility = [ "//packages/bazel:__pkg__", @@ -17,10 +16,12 @@ ts_library( ], deps = [ "//packages/compiler-cli", + "//packages/compiler-cli/src/ngtsc/perf", "@npm//@bazel/typescript", "@npm//@types/node", "@npm//tsickle", "@npm//typescript", + "@npm//typescript:typescript__typings", ], ) @@ -34,6 +35,9 @@ nodejs_binary( "@npm//tslib", ], entry_point = ":index.ts", + # TODO(josephperrott): update dependency usages to no longer need bazel patch module resolver + # See: https://github.com/bazelbuild/rules_nodejs/wiki#--bazel_patch_module_resolver-now-defaults-to-false-2324 + templated_args = ["--bazel_patch_module_resolver"], visibility = ["//visibility:public"], ) @@ -44,6 +48,9 @@ nodejs_binary( "@npm//source-map-support", ], entry_point = ":extract_i18n.ts", + # TODO(josephperrott): update dependency usages to no longer need bazel patch module resolver + # See: https://github.com/bazelbuild/rules_nodejs/wiki#--bazel_patch_module_resolver-now-defaults-to-false-2324 + templated_args = ["--bazel_patch_module_resolver"], visibility = ["//visibility:public"], ) diff --git a/packages/bazel/src/ngc-wrapped/index.ts b/packages/bazel/src/ngc-wrapped/index.ts index c6127b4d91..477a1a38d2 100644 --- a/packages/bazel/src/ngc-wrapped/index.ts +++ b/packages/bazel/src/ngc-wrapped/index.ts @@ -7,6 +7,7 @@ */ import * as ng from '@angular/compiler-cli'; +import {PerfPhase} from '@angular/compiler-cli/src/ngtsc/perf'; 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'; @@ -44,81 +45,54 @@ export function runOneBuild(args: string[], inputs?: {[path: string]: string}): const project = args[0].replace(/^@+/, ''); const [parsedOptions, errors] = parseTsconfig(project); - if (errors && errors.length) { + if (errors?.length) { console.error(ng.formatDiagnostics(errors)); return false; } - const {options: tsOptions, bazelOpts, files, config} = parsedOptions; - const angularCompilerOptions: {[k: string]: unknown} = config['angularCompilerOptions'] || {}; - // Allow Bazel users to control some of the bazel options. - // Since TypeScript's "extends" mechanism applies only to "compilerOptions" - // we have to repeat some of their logic to get the user's "angularCompilerOptions". - if (config['extends']) { - // Load the user's config file - // Note: this doesn't handle recursive extends so only a user's top level - // `angularCompilerOptions` will be considered. As this code is going to be - // removed with Ivy, the added complication of handling recursive extends - // is likely not needed. - let userConfigFile = resolveNormalizedPath(path.dirname(project), config['extends']); - if (!userConfigFile.endsWith('.json')) userConfigFile += '.json'; - const {config: userConfig, error} = ts.readConfigFile(userConfigFile, ts.sys.readFile); - if (error) { - console.error(ng.formatDiagnostics([error])); - return false; - } + const {bazelOpts, options: tsOptions, files, config} = parsedOptions; + const {errors: userErrors, options: userOptions} = ng.readConfiguration(project); - // All user angularCompilerOptions values that a user has control - // over should be collected here - if (userConfig.angularCompilerOptions) { - angularCompilerOptions['diagnostics'] = - angularCompilerOptions['diagnostics'] || userConfig.angularCompilerOptions.diagnostics; - angularCompilerOptions['trace'] = - angularCompilerOptions['trace'] || userConfig.angularCompilerOptions.trace; - - angularCompilerOptions['disableExpressionLowering'] = - angularCompilerOptions['disableExpressionLowering'] || - userConfig.angularCompilerOptions.disableExpressionLowering; - angularCompilerOptions['disableTypeScriptVersionCheck'] = - angularCompilerOptions['disableTypeScriptVersionCheck'] || - userConfig.angularCompilerOptions.disableTypeScriptVersionCheck; - - angularCompilerOptions['i18nOutLocale'] = angularCompilerOptions['i18nOutLocale'] || - userConfig.angularCompilerOptions.i18nOutLocale; - angularCompilerOptions['i18nOutFormat'] = angularCompilerOptions['i18nOutFormat'] || - userConfig.angularCompilerOptions.i18nOutFormat; - angularCompilerOptions['i18nOutFile'] = - angularCompilerOptions['i18nOutFile'] || userConfig.angularCompilerOptions.i18nOutFile; - - angularCompilerOptions['i18nInFormat'] = - angularCompilerOptions['i18nInFormat'] || userConfig.angularCompilerOptions.i18nInFormat; - angularCompilerOptions['i18nInLocale'] = - angularCompilerOptions['i18nInLocale'] || userConfig.angularCompilerOptions.i18nInLocale; - angularCompilerOptions['i18nInFile'] = - angularCompilerOptions['i18nInFile'] || userConfig.angularCompilerOptions.i18nInFile; - - angularCompilerOptions['i18nInMissingTranslations'] = - angularCompilerOptions['i18nInMissingTranslations'] || - userConfig.angularCompilerOptions.i18nInMissingTranslations; - angularCompilerOptions['i18nUseExternalIds'] = angularCompilerOptions['i18nUseExternalIds'] || - userConfig.angularCompilerOptions.i18nUseExternalIds; - - angularCompilerOptions['preserveWhitespaces'] = - angularCompilerOptions['preserveWhitespaces'] || - userConfig.angularCompilerOptions.preserveWhitespaces; - - angularCompilerOptions.createExternalSymbolFactoryReexports = - angularCompilerOptions.createExternalSymbolFactoryReexports || - userConfig.angularCompilerOptions.createExternalSymbolFactoryReexports; - } + if (userErrors?.length) { + console.error(ng.formatDiagnostics(userErrors)); + return false; } + const allowedNgCompilerOptionsOverrides = new Set([ + 'diagnostics', + 'trace', + 'disableExpressionLowering', + 'disableTypeScriptVersionCheck', + 'i18nOutLocale', + 'i18nOutFormat', + 'i18nOutFile', + 'i18nInLocale', + 'i18nInFile', + 'i18nInFormat', + 'i18nUseExternalIds', + 'i18nInMissingTranslations', + 'preserveWhitespaces', + 'createExternalSymbolFactoryReexports', + ]); + + const userOverrides = Object.entries(userOptions) + .filter(([key]) => allowedNgCompilerOptionsOverrides.has(key)) + .reduce((obj, [key, value]) => { + obj[key] = value; + + return obj; + }, {}); + + const compilerOpts: ng.AngularCompilerOptions = { + ...userOverrides, + ...config['angularCompilerOptions'], + ...tsOptions, + }; + // These are options passed through from the `ng_module` rule which aren't supported // by the `@angular/compiler-cli` and are only intended for `ngc-wrapped`. const {expectedOut, _useManifestPathsAsModuleName} = config['angularCompilerOptions']; - const {basePath} = ng.calcProjectFileAndBasePath(project); - const compilerOpts = ng.createNgCompilerOptions(basePath, config, tsOptions); const tsHost = ts.createCompilerHost(compilerOpts, true); const {diagnostics} = compile({ allDepsCompiledWithBazel: ALL_DEPS_COMPILED_WITH_BAZEL, @@ -377,8 +351,9 @@ export function compile({ if (importedFilePath.indexOf('node_modules') >= 0) { const maybeMetadataFile = importedFilePath.replace(EXT, '') + '.metadata.json'; if (fs.existsSync(maybeMetadataFile)) { - const moduleName = - JSON.parse(fs.readFileSync(maybeMetadataFile, {encoding: 'utf-8'})).importAs; + const moduleName = (JSON.parse(fs.readFileSync(maybeMetadataFile, {encoding: 'utf-8'})) as { + importAs: string + }).importAs; if (moduleName) { return moduleName; } @@ -541,6 +516,12 @@ function gatherDiagnosticsForInputsOnly( options: ng.CompilerOptions, bazelOpts: BazelOptions, ngProgram: ng.Program): (ng.Diagnostic|ts.Diagnostic)[] { const tsProgram = ngProgram.getTsProgram(); + + // For the Ivy compiler, track the amount of time spent fetching TypeScript diagnostics. + let previousPhase = PerfPhase.Unaccounted; + if (ngProgram instanceof ng.NgtscProgram) { + previousPhase = ngProgram.compiler.perfRecorder.phase(PerfPhase.TypeScriptDiagnostics); + } 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 @@ -555,6 +536,11 @@ function gatherDiagnosticsForInputsOnly( diagnostics.push(...tsProgram.getSyntacticDiagnostics(sf)); diagnostics.push(...tsProgram.getSemanticDiagnostics(sf)); } + + if (ngProgram instanceof ng.NgtscProgram) { + ngProgram.compiler.perfRecorder.phase(previousPhase); + } + if (!diagnostics.length) { // only gather the angular diagnostics if we have no diagnostics // in any other files. diff --git a/packages/bazel/test/ng_package/common_package.spec.ts b/packages/bazel/test/ng_package/common_package.spec.ts index c0aaafbcf2..50e881b37b 100644 --- a/packages/bazel/test/ng_package/common_package.spec.ts +++ b/packages/bazel/test/ng_package/common_package.spec.ts @@ -11,10 +11,13 @@ import * as fs from 'fs'; import * as path from 'path'; import * as shx from 'shelljs'; +/** Runfiles helper from bazel to resolve file name paths. */ +const runfiles = require(process.env['BAZEL_NODE_RUNFILES_HELPER']!); + // Resolve the "npm_package" directory by using the runfile resolution. Note that we need to // resolve the "package.json" of the package since otherwise NodeJS would resolve the "main" // file, which is not necessarily at the root of the "npm_package". -shx.cd(path.dirname(require.resolve('angular/packages/common/npm_package/package.json'))); +shx.cd(path.dirname(runfiles.resolve('angular/packages/common/npm_package/package.json'))); describe('@angular/common ng_package', () => { describe('should have the locales files', () => { @@ -104,14 +107,22 @@ describe('@angular/common ng_package', () => { describe('should have module resolution properties in the package.json file for', () => { + interface PackageJson { + main: string; + es2015: string; + module: string; + typings: string; + } // https://github.com/angular/common-builds/blob/master/package.json it('/', () => { - const actual = JSON.parse(fs.readFileSync('package.json', {encoding: 'utf-8'})); + const actual = + JSON.parse(fs.readFileSync('package.json', {encoding: 'utf-8'})) as PackageJson; expect(actual['main']).toEqual('./bundles/common.umd.js'); }); // https://github.com/angular/common-builds/blob/master/http/package.json it('/http', () => { - const actual = JSON.parse(fs.readFileSync('http/package.json', {encoding: 'utf-8'})); + const actual = + JSON.parse(fs.readFileSync('http/package.json', {encoding: 'utf-8'})) as PackageJson; expect(actual['main']).toEqual('../bundles/common-http.umd.js'); expect(actual['es2015']).toEqual('../fesm2015/http.js'); expect(actual['module']).toEqual('../fesm2015/http.js'); @@ -119,12 +130,15 @@ describe('@angular/common ng_package', () => { }); // https://github.com/angular/common-builds/blob/master/testing/package.json it('/testing', () => { - const actual = JSON.parse(fs.readFileSync('testing/package.json', {encoding: 'utf-8'})); + const actual = + JSON.parse(fs.readFileSync('testing/package.json', {encoding: 'utf-8'})) as PackageJson; expect(actual['main']).toEqual('../bundles/common-testing.umd.js'); }); // https://github.com/angular/common-builds/blob/master/http/testing/package.json it('/http/testing', () => { - const actual = JSON.parse(fs.readFileSync('http/testing/package.json', {encoding: 'utf-8'})); + const actual = + JSON.parse(fs.readFileSync('http/testing/package.json', {encoding: 'utf-8'})) as + PackageJson; expect(actual['main']).toEqual('../../bundles/common-http-testing.umd.js'); expect(actual['es2015']).toEqual('../../fesm2015/http/testing.js'); expect(actual['module']).toEqual('../../fesm2015/http/testing.js'); @@ -132,7 +146,8 @@ describe('@angular/common ng_package', () => { }); // https://github.com/angular/common-builds/blob/master/upgrade/package.json it('/upgrade', () => { - const actual = JSON.parse(fs.readFileSync('upgrade/package.json', {encoding: 'utf-8'})); + const actual = + JSON.parse(fs.readFileSync('upgrade/package.json', {encoding: 'utf-8'})) as PackageJson; expect(actual['main']).toEqual('../bundles/common-upgrade.umd.js'); expect(actual['es2015']).toEqual('../fesm2015/upgrade.js'); expect(actual['module']).toEqual('../fesm2015/upgrade.js'); diff --git a/packages/bazel/test/ng_package/core_package.spec.ts b/packages/bazel/test/ng_package/core_package.spec.ts index cf54cabde6..7c5876c2ec 100644 --- a/packages/bazel/test/ng_package/core_package.spec.ts +++ b/packages/bazel/test/ng_package/core_package.spec.ts @@ -10,10 +10,13 @@ import {ivyEnabled, obsoleteInIvy} from '@angular/private/testing'; import * as path from 'path'; import * as shx from 'shelljs'; +/** Runfiles helper from bazel to resolve file name paths. */ +const runfiles = require(process.env['BAZEL_NODE_RUNFILES_HELPER']!); + // Resolve the "npm_package" directory by using the runfile resolution. Note that we need to // resolve the "package.json" of the package since otherwise NodeJS would resolve the "main" // file, which is not necessarily at the root of the "npm_package". -shx.cd(path.dirname(require.resolve('angular/packages/core/npm_package/package.json'))); +shx.cd(path.dirname(runfiles.resolve('angular/packages/core/npm_package/package.json'))); /** * Utility functions that allows me to create fs paths @@ -58,8 +61,11 @@ describe('@angular/core ng_package', () => { }); it('should contain metadata for ng update', () => { + interface PackageJson { + 'ng-update': {packageGroup: string[];}; + } expect(shx.cat(packageJson)).not.toContain('NG_UPDATE_PACKAGE_GROUP'); - expect(JSON.parse(shx.cat(packageJson))['ng-update']['packageGroup']) + expect((JSON.parse(shx.cat(packageJson)) as PackageJson)['ng-update'].packageGroup) .toContain('@angular/core'); }); }); diff --git a/packages/bazel/test/ng_package/example_package.golden b/packages/bazel/test/ng_package/example_package.golden index f8bfa58b79..38876cb8e7 100644 --- a/packages/bazel/test/ng_package/example_package.golden +++ b/packages/bazel/test/ng_package/example_package.golden @@ -89,7 +89,7 @@ License: MIT /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -121,7 +121,7 @@ export { } /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -160,7 +160,7 @@ Hello /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -210,7 +210,7 @@ Hello /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ !function(e,o){"object"==typeof exports&&"undefined"!=typeof module?o(exports,require("@angular/core")):"function"==typeof define&&define.amd?define("example/a11y",["exports","@angular/core"],o):o(((e=e||self).example=e.example||{},e.example.a11y={}),e.ng.core)}(this,(function(e,o){"use strict"; @@ -234,7 +234,7 @@ e.A11yModule=t,Object.defineProperty(e,"__esModule",{value:!0})})); /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -307,7 +307,7 @@ e.A11yModule=t,Object.defineProperty(e,"__esModule",{value:!0})})); /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@angular/core")):"function"==typeof define&&define.amd?define("example/imports",["exports","@angular/core"],t):t(((e=e||self).example=e.example||{},e.example.imports={}),e.ng.core)}(this,(function(e,t){"use strict"; @@ -331,7 +331,7 @@ e.MyService=r,e.ɵangular_packages_bazel_test_ng_package_example_imports_imports /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -383,7 +383,7 @@ e.MyService=r,e.ɵangular_packages_bazel_test_ng_package_example_imports_imports /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ !function(e,o){"object"==typeof exports&&"undefined"!=typeof module?o(exports,require("@angular/core")):"function"==typeof define&&define.amd?define("example/secondary",["exports","@angular/core"],o):o(((e=e||self).example=e.example||{},e.example.secondary={}),e.ng.core)}(this,(function(e,o){"use strict"; @@ -407,7 +407,7 @@ e.SecondaryModule=n,e.a=1,Object.defineProperty(e,"__esModule",{value:!0})})); /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -457,7 +457,7 @@ e.SecondaryModule=n,e.a=1,Object.defineProperty(e,"__esModule",{value:!0})})); /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ !function(e,o){"object"==typeof exports&&"undefined"!=typeof module?o(exports,require("@angular/core")):"function"==typeof define&&define.amd?define("example",["exports","@angular/core"],o):o((e=e||self).example={},e.ng.core)}(this,(function(e,o){"use strict"; @@ -700,7 +700,7 @@ export const a = 1; /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -727,7 +727,7 @@ export { } /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -766,7 +766,7 @@ export { A11yModule }; /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -826,7 +826,7 @@ export { MyService, MySecondService as ɵangular_packages_bazel_test_ng_package_ /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -866,7 +866,7 @@ export { SecondaryModule, a }; /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -905,7 +905,7 @@ export { MyModule }; /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -942,7 +942,7 @@ export { } /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -987,7 +987,7 @@ export * from './imports/imports'; /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -1009,7 +1009,7 @@ export { } /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ diff --git a/packages/bazel/test/ng_package/example_package.spec.ts b/packages/bazel/test/ng_package/example_package.spec.ts index e321f30928..50f3eb8fdc 100644 --- a/packages/bazel/test/ng_package/example_package.spec.ts +++ b/packages/bazel/test/ng_package/example_package.spec.ts @@ -11,6 +11,9 @@ import {createPatch} from 'diff'; import * as fs from 'fs'; import * as path from 'path'; +/** Runfiles helper from bazel to resolve file name paths. */ +const runfiles = require(process.env['BAZEL_NODE_RUNFILES_HELPER']!); + type TestPackage = { displayName: string; packagePath: string; goldenFilePath: string; }; @@ -21,18 +24,18 @@ const packagesToTest: TestPackage[] = [ // Resolve the "npm_package" directory by using the runfile resolution. Note that we need to // resolve the "package.json" of the package since otherwise NodeJS would resolve the "main" // file, which is not necessarily at the root of the "npm_package". - packagePath: path.dirname( - require.resolve('angular/packages/bazel/test/ng_package/example/npm_package/package.json')), - goldenFilePath: require.resolve('./example_package.golden') + packagePath: path.dirname(runfiles.resolve( + 'angular/packages/bazel/test/ng_package/example/npm_package/package.json')), + goldenFilePath: runfiles.resolvePackageRelative('./example_package.golden') }, { displayName: 'Example with ts_library NPM package', // Resolve the "npm_package" directory by using the runfile resolution. Note that we need to // resolve the "package.json" of the package since otherwise NodeJS would resolve the "main" // file, which is not necessarily at the root of the "npm_package". - packagePath: path.dirname(require.resolve( + packagePath: path.dirname(runfiles.resolve( 'angular/packages/bazel/test/ng_package/example-with-ts-library/npm_package/package.json')), - goldenFilePath: require.resolve('./example_with_ts_library_package.golden') + goldenFilePath: runfiles.resolvePackageRelative('./example_with_ts_library_package.golden') }, ]; 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 1b908f3d86..e51a783e69 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 @@ -62,7 +62,7 @@ License: MIT /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -114,7 +114,7 @@ License: MIT /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ !function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@angular/core")):"function"==typeof define&&define.amd?define("example-with-ts-library/portal",["exports","@angular/core"],r):r(((e=e||self).exampleWithTsLibrary=e.exampleWithTsLibrary||{},e.exampleWithTsLibrary.portal={}),e.ng.core)}(this,(function(e,r){"use strict"; @@ -138,7 +138,7 @@ e.PortalModule=t,e.a=1,Object.defineProperty(e,"__esModule",{value:!0})})); /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -179,7 +179,7 @@ e.PortalModule=t,e.a=1,Object.defineProperty(e,"__esModule",{value:!0})})); /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define("example-with-ts-library/utils",["exports"],t):t(((e=e||self).exampleWithTsLibrary=e.exampleWithTsLibrary||{},e.exampleWithTsLibrary.utils={}))}(this,(function(e){"use strict"; @@ -203,7 +203,7 @@ e.dispatchFakeEvent=function t(e,i){e.dispatchEvent(i)},Object.defineProperty(e, /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -234,7 +234,7 @@ e.dispatchFakeEvent=function t(e,i){e.dispatchEvent(i)},Object.defineProperty(e, /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define("example-with-ts-library",["exports"],t):t((e=e||self).exampleWithTsLibrary={})}(this,(function(e){"use strict"; @@ -344,7 +344,7 @@ export function dispatchFakeEvent(el, ev) { /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -365,7 +365,7 @@ export { VERSION }; /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -405,7 +405,7 @@ export { PortalModule, a }; /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -512,7 +512,7 @@ export * from './index'; /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ @@ -564,7 +564,7 @@ export declare function dispatchFakeEvent(el: HTMLElement, ev: Event): void; /** * @license Angular v0.0.0 - * (c) 2010-2020 Google LLC. https://angular.io/ + * (c) 2010-2021 Google LLC. https://angular.io/ * License: MIT */ diff --git a/packages/bazel/test/ngc-wrapped/flat_module_test.ts b/packages/bazel/test/ngc-wrapped/flat_module_test.ts index f1e1dedbd0..492ca59d48 100644 --- a/packages/bazel/test/ngc-wrapped/flat_module_test.ts +++ b/packages/bazel/test/ngc-wrapped/flat_module_test.ts @@ -10,13 +10,16 @@ import {obsoleteInIvy, onlyInIvy} from '@angular/private/testing'; import {existsSync, readFileSync} from 'fs'; import {dirname, join} from 'path'; +/** Runfiles helper from bazel to resolve file name paths. */ +const runfiles = require(process.env['BAZEL_NODE_RUNFILES_HELPER']!); + describe('flat_module ng_module', () => { let packageOutput: string; let flatModuleOutFile: string; beforeAll(() => { packageOutput = - dirname(require.resolve('angular/packages/bazel/test/ngc-wrapped/flat_module/index.js')); + dirname(runfiles.resolve('angular/packages/bazel/test/ngc-wrapped/flat_module/index.js')); flatModuleOutFile = join(packageOutput, 'flat_module.js'); }); diff --git a/packages/benchpress/src/webdriver/chrome_driver_extension.ts b/packages/benchpress/src/webdriver/chrome_driver_extension.ts index 2297d8b5a6..9fecaa33a3 100644 --- a/packages/benchpress/src/webdriver/chrome_driver_extension.ts +++ b/packages/benchpress/src/webdriver/chrome_driver_extension.ts @@ -86,7 +86,9 @@ export class ChromeDriverExtension extends WebDriverExtension { .then((entries) => { const events: PerfLogEvent[] = []; entries.forEach((entry: any) => { - const message = JSON.parse(entry['message'])['message']; + const message = + (JSON.parse(entry['message']) as + {message: {method: string, params: PerfLogEvent}})['message']; if (message['method'] === 'Tracing.dataCollected') { events.push(message['params']); } diff --git a/packages/benchpress/src/webdriver/ios_driver_extension.ts b/packages/benchpress/src/webdriver/ios_driver_extension.ts index f2bad64b33..5da139a7e8 100644 --- a/packages/benchpress/src/webdriver/ios_driver_extension.ts +++ b/packages/benchpress/src/webdriver/ios_driver_extension.ts @@ -44,7 +44,9 @@ export class IOsDriverExtension extends WebDriverExtension { .then((entries) => { const records: any[] = []; entries.forEach((entry: any) => { - const message = JSON.parse(entry['message'])['message']; + const message = + (JSON.parse(entry['message']) as + {message: {method: string, params: PerfLogEvent}})['message']; if (message['method'] === 'Timeline.eventRecorded') { records.push(message['params']['record']); } diff --git a/packages/benchpress/test/reporter/json_file_reporter_spec.ts b/packages/benchpress/test/reporter/json_file_reporter_spec.ts index d8621e7374..6fc2657ba7 100644 --- a/packages/benchpress/test/reporter/json_file_reporter_spec.ts +++ b/packages/benchpress/test/reporter/json_file_reporter_spec.ts @@ -50,7 +50,7 @@ import {Injector, JsonFileReporter, MeasureValues, Options, SampleDescription} f [mv(0, 0, {'a': 3, 'b': 6}), mv(1, 1, {'a': 5, 'b': 9})]); const regExp = /somePath\/someId_\d+\.json/; expect(loggedFile['filename'].match(regExp) != null).toBe(true); - const parsedContent = JSON.parse(loggedFile['content']); + const parsedContent = JSON.parse(loggedFile['content']) as {[key: string]: any}; expect(parsedContent).toEqual({ 'description': { 'id': 'someId', diff --git a/packages/common/http/public_api.ts b/packages/common/http/public_api.ts index 8a434169d7..c08bef4d0c 100644 --- a/packages/common/http/public_api.ts +++ b/packages/common/http/public_api.ts @@ -6,14 +6,36 @@ * found in the LICENSE file at https://angular.io/license */ +import {XhrFactory as XhrFactory_fromAngularCommon} from '@angular/common'; + +/** + * A wrapper around the `XMLHttpRequest` constructor. + * + * @publicApi + * @see `XhrFactory` + * @deprecated + * `XhrFactory` has moved, please import `XhrFactory` from `@angular/common` instead. + */ +export type XhrFactory = XhrFactory_fromAngularCommon; +/** + * A wrapper around the `XMLHttpRequest` constructor. + * + * @publicApi + * @see `XhrFactory` + * @deprecated + * `XhrFactory` has moved, please import `XhrFactory` from `@angular/common` instead. + */ +export const XhrFactory = XhrFactory_fromAngularCommon; + export {HttpBackend, HttpHandler} from './src/backend'; export {HttpClient} from './src/client'; +export {HttpContext, HttpContextToken} from './src/context'; export {HttpHeaders} from './src/headers'; export {HTTP_INTERCEPTORS, HttpInterceptor} from './src/interceptor'; export {JsonpClientBackend, JsonpInterceptor} from './src/jsonp'; export {HttpClientJsonpModule, HttpClientModule, HttpClientXsrfModule, HttpInterceptingHandler as ɵHttpInterceptingHandler} from './src/module'; export {HttpParameterCodec, HttpParams, HttpParamsOptions, HttpUrlEncodingCodec} from './src/params'; export {HttpRequest} from './src/request'; -export {HttpDownloadProgressEvent, HttpErrorResponse, HttpEvent, HttpEventType, HttpHeaderResponse, HttpProgressEvent, HttpResponse, HttpResponseBase, HttpSentEvent, HttpUploadProgressEvent, HttpUserEvent} from './src/response'; -export {HttpXhrBackend, XhrFactory} from './src/xhr'; +export {HttpDownloadProgressEvent, HttpErrorResponse, HttpEvent, HttpEventType, HttpHeaderResponse, HttpProgressEvent, HttpResponse, HttpResponseBase, HttpSentEvent, HttpStatusCode, HttpUploadProgressEvent, HttpUserEvent} from './src/response'; +export {HttpXhrBackend} from './src/xhr'; export {HttpXsrfTokenExtractor} from './src/xsrf'; diff --git a/packages/common/http/src/backend.ts b/packages/common/http/src/backend.ts index 2ab6af1642..531bb1735e 100644 --- a/packages/common/http/src/backend.ts +++ b/packages/common/http/src/backend.ts @@ -14,19 +14,12 @@ import {HttpEvent} from './response'; * Transforms an `HttpRequest` into a stream of `HttpEvent`s, one of which will likely be a * `HttpResponse`. * - * 把一个 `HttpRequest` 转换成 `HttpEvent` 组成的流,`HttpResponse` 就是其中之一。 - * * `HttpHandler` is injectable. When injected, the handler instance dispatches requests to the * first interceptor in the chain, which dispatches to the second, etc, eventually reaching the * `HttpBackend`. * - * `HttpHandler` 是可注入的。当被注入时,该处理器的实例会把请求派发给拦截器链中的第一个拦截器,第一个拦截器会再派发给第二个拦截器,以此类推。 - * 最终抵达 `HttpBackend`。 - * * In an `HttpInterceptor`, the `HttpHandler` parameter is the next interceptor in the chain. * - * 在 `HttpInterceptor` 中,`HttpHandler` 参数就表示链中的下一个拦截器。 - * * @publicApi */ export abstract class HttpHandler { @@ -36,17 +29,11 @@ export abstract class HttpHandler { /** * A final `HttpHandler` which will dispatch the request via browser HTTP APIs to a backend. * - * 最后一个 `HttpHandler`,它将会把该请求通过浏览器的 HTTP API 发到后端。 - * * Interceptors sit between the `HttpClient` interface and the `HttpBackend`. * - * 拦截器位于 `HttpClient` 接口和 `HttpBackend` 之间。 - * * When injected, `HttpBackend` dispatches requests directly to the backend, without going * through the interceptor chain. * - * 当它被注入时,`HttpBackend` 会把请求直接发给后端,而不会经过拦截器链。 - * * @publicApi */ export abstract class HttpBackend implements HttpHandler { diff --git a/packages/common/http/src/client.ts b/packages/common/http/src/client.ts index ed4d059d2b..627610a272 100644 --- a/packages/common/http/src/client.ts +++ b/packages/common/http/src/client.ts @@ -11,6 +11,7 @@ import {Observable, of} from 'rxjs'; import {concatMap, filter, map} from 'rxjs/operators'; import {HttpHandler} from './backend'; +import {HttpContext} from './context'; import {HttpHeaders} from './headers'; import {HttpParams, HttpParamsOptions} from './params'; import {HttpRequest} from './request'; @@ -30,8 +31,10 @@ import {HttpEvent, HttpResponse} from './response'; function addBody( options: { headers?: HttpHeaders|{[header: string]: string | string[]}, - observe?: HttpObserve, - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + observe?: 'body'|'events'|'response', + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'arraybuffer'|'blob'|'json'|'text', withCredentials?: boolean, @@ -40,6 +43,7 @@ function addBody( return { body, headers: options.headers, + context: options.context, observe: options.observe, params: options.params, reportProgress: options.reportProgress, @@ -48,33 +52,23 @@ function addBody( }; } -export type HttpObserve = 'body'|'events'|'response'; - /** * Performs HTTP requests. * This service is available as an injectable class, with methods to perform HTTP requests. * Each request method has multiple signatures, and the return type varies based on * the signature that is called (mainly the values of `observe` and `responseType`). * - * 执行 HTTP 请求。该服务作为可注入类提供,带有执行 HTTP 请求的方法。每个请求方法都有多个签名,并且返回类型会根据所调用的签名(主要的值是 `observe` 和 `responseType`)而有所不同。 - * * Note that the `responseType` *options* value is a String that identifies the * single data type of the response. * A single overload version of the method handles each response type. * The value of `responseType` cannot be a union, as the combined signature could imply. - * - * 请注意, `responseType` *选项*的值是一个字符串,用于标识此响应的单一数据类型。该方法的各个重载版本处理每种响应类型。正如组合签名所暗示的那样 `responseType` 的值不能为联合类型。 + * * @usageNotes - * * Sample HTTP requests for the [Tour of Heroes](/tutorial/toh-pt0) application. * - * [“英雄之旅”](/tutorial/toh-pt0)应用程序的示例 HTTP 请求。 - * * ### HTTP Request Example * - * ### HTTP 请求示例 - * * ``` * // GET heroes whose name contains search term * searchHeroes(term: string): observable{ @@ -84,10 +78,14 @@ export type HttpObserve = 'body'|'events'|'response'; * } * ``` * + * Alternatively, the parameter string can be used without invoking HttpParams + * by directly joining to the URL. + * ``` + * this.httpClient.request('GET', this.heroesUrl + '?' + 'name=term', {responseType:'json'}); + * ``` + * + * * ### JSONP Example - * - * ### JSONP 示例 - * * ``` * requestJsonp(url, callback = 'callback') { * return this.httpClient.jsonp(this.heroesURL, callback); @@ -95,9 +93,6 @@ export type HttpObserve = 'body'|'events'|'response'; * ``` * * ### PATCH Example - * - * ### PATCH 示例 - * * ``` * // PATCH one of the heroes' name * patchHero (id: number, heroName: string): Observable<{}> { @@ -108,8 +103,7 @@ export type HttpObserve = 'body'|'events'|'response'; * ``` * * @see [HTTP Guide](guide/http) - * - * [HTTP 指南](guide/http) + * @see [HTTP Request](api/common/http/HttpRequest) * * @publicApi */ @@ -118,14 +112,9 @@ export class HttpClient { constructor(private handler: HttpHandler) {} /** - * Sends an `HTTPRequest` and returns a stream of `HTTPEvents`. - * - * 发送 `HTTPRequest` 并返回 `HTTPEvents` 流。 - * - * @return An `Observable` of the response, with the response body as a stream of `HTTPEvents`. - * - * 响应对象的 `Observable` ,其响应体为 `HTTPEvents` 流。 + * Sends an `HttpRequest` and returns a stream of `HttpEvent`s. * + * @return An `Observable` of the response, with the response body as a stream of `HttpEvent`s. */ request(req: HttpRequest): Observable>; @@ -133,30 +122,20 @@ export class HttpClient { * Constructs a request that interprets the body as an `ArrayBuffer` and returns the response in * an `ArrayBuffer`. * - * 构造一个请求,它将请求体解释为 `ArrayBuffer`,并且返回 `ArrayBuffer` 格式的响应体。 - * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 * * @return An `Observable` of the response, with the response body as an `ArrayBuffer`. - * - * 响应对象的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ request(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable; @@ -165,30 +144,19 @@ export class HttpClient { * Constructs a request that interprets the body as a blob and returns * the response as a blob. * - * 构造一个请求,将请求体解释为 `Blob` 类型,其响应体为 `Blob` 类型。 - * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response, with the response body of type `Blob`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `Blob` 类型。 - * */ request(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable; @@ -197,30 +165,19 @@ export class HttpClient { * Constructs a request that interprets the body as a text string and * returns a string value. * - * 构造一个请求,它将请求体解释为字符串,并且返回一个字符串。 - * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response, with the response body of type string. - * - * 此请求的响应对象的 `Observable`,其响应体为 string 类型。 - * */ request(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable; @@ -229,31 +186,20 @@ export class HttpClient { * Constructs a request that interprets the body as an `ArrayBuffer` and returns the * the full event stream. * - * 构造一个请求,它将请求体解释为 `ArrayBuffer`,并返回完整的事件流。 - * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the response, with the response body as an array of `HTTPEvents` for - * the - * request. - * - * 一个响应对象的 `Observable`,其响应主体为此请求的 `HTTPEvents` - * + * @return An `Observable` of the response, with the response body as an array of `HttpEvent`s 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', + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, + observe: 'events', reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; @@ -262,30 +208,19 @@ export class HttpClient { * Constructs a request that interprets the body as a `Blob` and returns * the full event stream. * - * 构造一个请求,它将请求体解释为 `Blob`,并返回完整的事件流。 - * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of of all `HttpEvents` for the request, + * @return An `Observable` of all `HttpEvent`s for the request, * with the response body of type `Blob`. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为 `Blob` 类型。 - * */ request(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; @@ -294,30 +229,19 @@ export class HttpClient { * Constructs a request which interprets the body as a text string and returns the full event * stream. * - * 构造一个请求,它将请求体解释为文本字符串,并且返回完整的事件流。 - * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of all `HttpEvents` for the reques, + * @return An `Observable` of all `HttpEvent`s for the request, * with the response body of type string. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为 string 类型。 - * */ request(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; @@ -326,31 +250,20 @@ export class HttpClient { * Constructs a request which interprets the body as a JSON object and returns the full event * stream. * - * 构造一个请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 要和此请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of all `HttpEvents` for the request, - * with the response body of type `Object`. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为 `Object` 类型。 - * + * @return An `Observable` of all `HttpEvent`s for the request, + * with the response body of type `Object`. */ request(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, reportProgress?: boolean, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, responseType?: 'json', withCredentials?: boolean, }): Observable>; @@ -359,220 +272,143 @@ export class HttpClient { * Constructs a request which interprets the body as a JSON object and returns the full event * stream. * - * 构造一个请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of all `HttpEvents` for the request, + * @return An `Observable` of all `HttpEvent`s for the request, * with the response body of type `R`. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为 `R` 类型。 - * */ request(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, reportProgress?: boolean, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, responseType?: 'json', withCredentials?: boolean, }): Observable>; /** * Constructs a request which interprets the body as an `ArrayBuffer` - * and returns the full `HTTPResponse`. - * - * 构造一个请求,它将请求体解释为 `ArrayBuffer`,并且返回完整的 `HttpResponse`。 + * and returns the full `HttpResponse`. * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse`, with the response body as an `ArrayBuffer`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * + * @return An `Observable` of the `HttpResponse`, with the response body as an `ArrayBuffer`. */ request(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; /** - * Constructs a request which interprets the body as a `Blob` and returns the full `HTTPResponse`. - * - * 构造一个请求,它将请求体解释为 `Blob`,并且返回完整的 `HttpResponse`。 + * Constructs a request which interprets the body as a `Blob` and returns the full `HttpResponse`. * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse`, with the response body of type `Blob`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `Blob` 类型。 - * + * @return An `Observable` of the `HttpResponse`, with the response body of type `Blob`. */ request(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; /** * Constructs a request which interprets the body as a text stream and returns the full - * `HTTPResponse`. - * - * 构造一个请求,它将请求体解释为文本流,并且返回完整的 `HttpResponse`。 + * `HttpResponse`. * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the HTTP response, with the response body of type string. - * - * 此请求的 `HTTP response` 的 `Observable`,其响应体为 string 类型。 - * */ request(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; /** * Constructs a request which interprets the body as a JSON object and returns the full - * `HTTPResponse`. - * - * 构造一个请求,它将请求体解释为 JSON 对象,并返回完整 `HTTPResponse`。 + * `HttpResponse`. * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the full `HTTPResponse`, + * @return An `Observable` of the full `HttpResponse`, * with the response body of type `Object`. - * - * 此请求的完整 `HTTPResponse` 的 `Observable`,其响应体为 `Object` 类型。 - * */ request(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, reportProgress?: boolean, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, responseType?: 'json', withCredentials?: boolean, }): Observable>; /** * Constructs a request which interprets the body as a JSON object and returns - * the full `HTTPResponse` with the response body in the requested type. - * - * 构造一个请求,它将请求体解释为 JSON 对象,并返回带有请求主体类型 `HTTPResponse` + * the full `HttpResponse` with the response body in the requested type. * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the full `HTTPResponse`, with the response body of type `R`. - * - * 此请求的完整 `HTTPResponse` 的 `Observable`,其响应体为 `R` 类型。 - * + * @return An `Observable` of the full `HttpResponse`, with the response body of type `R`. */ request(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, reportProgress?: boolean, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, responseType?: 'json', withCredentials?: boolean, }): Observable>; /** * Constructs a request which interprets the body as a JSON object and returns the full - * `HTTPResponse` as a JSON object. - * - * 构造一个请求,它将请求体解释为 JSON 对象,并返回完整的 `HTTPResponse`。 + * `HttpResponse` as a JSON object. * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse`, with the response body of type `Object`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `Object` 类型。 - * + * @return An `Observable` of the `HttpResponse`, with the response body of type `Object`. */ request(method: string, url: string, options?: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, responseType?: 'json', reportProgress?: boolean, withCredentials?: boolean, @@ -582,30 +418,19 @@ export class HttpClient { * Constructs a request which interprets the body as a JSON object * with the response body of the requested type. * - * 构造一个请求,它将请求体解释为 JSON 对象,并返回所请求类型的响应体。 - * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse`, with the response body of type `R`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `R` 类型。 - * + * @return An `Observable` of the `HttpResponse`, with the response body of type `R`. */ request(method: string, url: string, options?: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, responseType?: 'json', reportProgress?: boolean, withCredentials?: boolean, @@ -614,30 +439,19 @@ export class HttpClient { /** * Constructs a request where response type and requested observable are not known statically. * - * 构造一个请求,其中的响应类型和所请求的可观察对象都不是静态已知的。 - * * @param method The HTTP method. - * - * HTTP 方法。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the reuested response, wuth body of type `any`. - * - * 所请求的响应对象的 `Observable`,其响应体是 `any` 类型。 - * */ request(method: string, url: string, options?: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, - params?: HttpParams|{[param: string]: string | string[]}, - observe?: HttpObserve, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, + observe?: 'body'|'events'|'response', reportProgress?: boolean, responseType?: 'arraybuffer'|'blob'|'json'|'text', withCredentials?: boolean, @@ -648,54 +462,34 @@ export class HttpClient { * fires the request through the chain of registered interceptors and on to the * server. * - * 为任意 HTTP 请求构造一个可观察的对象,该请求在被订阅时将通过已注册的拦截器链触发该请求,然后发送到服务器。 - * * You can pass an `HttpRequest` directly as the only parameter. In this case, * the call returns an observable of the raw `HttpEvent` stream. * - * 你可以直接把 `HttpRequest` 作为唯一参数。在这种情况下,该调用将返回原始 `HttpEvent` 流的可观察值。 - * * Alternatively you can pass an HTTP method as the first parameter, * a URL string as the second, and an options hash containing the request body as the third. * See `addBody()`. In this case, the specified `responseType` and `observe` options determine the * type of returned observable. - * - * 或者,你可以将 HTTP 方法作为第一个参数,将 URL 字符串作为第二个参数,将包含请求正文的选项哈希作为第三个参数。参见 `addBody()`。在这种情况下,指定的 `responseType` 和 `observe` 选项会决定要返回的 observable 类型。 - * * * The `responseType` value determines how a successful response body is parsed. - * - * 此 `responseType` 值确定如何解析成功的响应体。 - * * * If `responseType` is the default `json`, you can pass a type interface for the resulting * object as a type parameter to the call. * - * 如果 `responseType` 是默认的 `json` ,则可以将结果对象的类型接口作为类型参数传递给调用。 - * * The `observe` value determines the return type, according to what you are interested in * observing. - * - * `observe` 值根据你感兴趣的观察值确定其返回类型。 - * * * An `observe` value of events returns an observable of the raw `HttpEvent` stream, including * progress events by default. - * - * 事件的 `observe` 值返回原始 `HttpEvent` 流的可观察值,默认情况下包括进度事件。 - * * * An `observe` value of response returns an observable of `HttpResponse`, * where the `T` parameter depends on the `responseType` and any optionally provided type * parameter. - * - * 响应对象的 `observe` 值返回 `HttpResponse` 的可观察对象,其中 `T` 参数取决于 `responseType` 和所提供的可选类型参数。 - * * * An `observe` value of body returns an observable of `` with the same `T` body type. * - * `observe` 的 body 值会返回与 `T` 的响应体具有相同类型的 `` 型可观察对象。 */ request(first: string|HttpRequest, url?: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, - observe?: HttpObserve, - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + observe?: 'body'|'events'|'response', + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'arraybuffer'|'blob'|'json'|'text', withCredentials?: boolean, @@ -732,6 +526,7 @@ export class HttpClient { // Construct the request. req = new HttpRequest(first, url!, (options.body !== undefined ? options.body : null), { headers, + context: options.context, params, reportProgress: options.reportProgress, // By default, JSON is assumed to be returned for all calls. @@ -811,25 +606,17 @@ export class HttpClient { * Constructs a `DELETE` request that interprets the body as an `ArrayBuffer` * and returns the response as an `ArrayBuffer`. * - * 构造一个 `DELETE` 请求,它将请求体解释为 `ArrayBuffer`,并且返回 `ArrayBuffer` 格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response body as an `ArrayBuffer`. - * - * 类型为 `ArrayBuffer` 的响应体的 `Observable`。 - * */ delete(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable; @@ -839,25 +626,17 @@ export class HttpClient { * Constructs a `DELETE` request that interprets the body as a `Blob` and returns * the response as a `Blob`. * - * 构造一个 `DELETE` 请求,它将请求体解释为 `Blob`,并且返回 `Blob` 格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response body as a `Blob`. - * - * 类型为 `Blob` 的响应体的 `Observable`。 - * */ delete(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable; @@ -866,25 +645,17 @@ export class HttpClient { * Constructs a `DELETE` request that interprets the body as a text string and returns * a string. * - * 构造一个 `DELETE` 请求,它将请求体解释为字符串,并且返回一个字符串。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response, with the response body of type string. - * - * 此请求的响应对象的 `Observable`,其响应体为 string 类型。 - * */ delete(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable; @@ -893,25 +664,17 @@ export class HttpClient { * Constructs a `DELETE` request that interprets the body as an `ArrayBuffer` * and returns the full event stream. * - * 构造一个 `DELETE` 请求,它将请求体解释为 `ArrayBuffer`,并返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of all `HTTPEvents` for the request, + * @return An `Observable` of all `HttpEvent`s for the request, * with response body as an `ArrayBuffer`. - * - * 此请求的所有 `HTTPEvents` 的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ delete(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; @@ -920,25 +683,17 @@ export class HttpClient { * Constructs a `DELETE` request that interprets the body as a `Blob` * and returns the full event stream. * - * 构造一个 `DELETE` 请求,它将请求体解释为 `Blob`,并返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of all the `HTTPEvents` for the request, with the response body as a + * @return An `Observable` of all the `HttpEvent`s for the request, with the response body as a * `Blob`. - * - * 此请求的所有 `HTTPEvents` 的 `Observable`,其响应体为 `Blob` 类型。 - * */ delete(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; @@ -947,25 +702,17 @@ export class HttpClient { * Constructs a `DELETE` request that interprets the body as a text string * and returns the full event stream. * - * 构造一个 `DELETE` 请求,它将请求体解释为文本字符串,并且返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of all `HTTPEvents` for the request, with the response - * body of type string. - * - * 表示啥此请求的 `HTTPEvents` 的 `Observable`,响应体为 string 类型。 - * + * @return An `Observable` of all `HttpEvent`s for the request, with the response + * body of type string. */ delete(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; @@ -974,25 +721,17 @@ export class HttpClient { * Constructs a `DELETE` request that interprets the body as a JSON object * and returns the full event stream. * - * 构造一个 `DELETE` 请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of all `HTTPEvents` for the request, with response body of + * @return An `Observable` of all `HttpEvent`s for the request, with response body of * type `Object`. - * - * 表示啥此请求的 `HTTPEvents` 的 `Observable`,响应体为 `Object` 类型。 - * */ delete(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -1002,25 +741,17 @@ export class HttpClient { * Constructs a `DELETE`request that interprets the body as a JSON object * and returns the full event stream. * - * 构造一个 `DELETE` 请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of all the `HTTPEvents` for the request, with a response + * @return An `Observable` of all the `HttpEvent`s for the request, with a response * body in the requested type. - * - * 表示此请求的 `HTTPEvents` 的 `Observable`,响应体为所请求的类型。 - * */ delete(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | (string | number | boolean)[]}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -1028,104 +759,73 @@ export class HttpClient { /** * Constructs a `DELETE` request that interprets the body as an `ArrayBuffer` and returns - * the full `HTTPResponse`. - * - * 构造一个 `DELETE` 请求,它将请求体解释为 `ArrayBuffer`,并且返回完整的 `HttpResponse`。 + * the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the full `HTTPResponse`, with the response body as an `ArrayBuffer`. - * - * 此请求的完整 `HTTPResponse` 的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * + * @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', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; /** * Constructs a `DELETE` request that interprets the body as a `Blob` and returns the full - * `HTTPResponse`. - * - * 构造一个 `DELETE` 请求,它将请求体解释为 `Blob`,并且返回完整的 `HttpResponse`。 + * `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse`, with the response body of type `Blob`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `Blob` 类型。 - * + * @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', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; /** * Constructs a `DELETE` request that interprets the body as a text stream and - * returns the full `HTTPResponse`. - * - * 构造一个 `DELETE` 请求,它将请求体解释为文本流,并且返回完整的 `HttpResponse`。 + * returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the full `HTTPResponse`, with the response body of type string. - * - * 此请求的完整 `HTTPResponse` 的 `Observable`,其响应体为 string 类型。 - * + * @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', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; /** * Constructs a `DELETE` request the interprets the body as a JSON object and returns - * the full `HTTPResponse`. - * - * 构造一个 `DELETE` 请求,它将请求体解释为 JSON 对象,并且返回完整的 `HttpResponse`。 + * the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse`, with the response body of type `Object`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `Object` 类型。 + * @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', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -1133,26 +833,18 @@ export class HttpClient { /** * Constructs a `DELETE` request that interprets the body as a JSON object - * and returns the full `HTTPResponse`. - * - * 构造一个 `DELETE` 请求,它将请求体解释为 JSON 对象,并且返回完整的 `HttpResponse`。 + * and returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse`, with the response body of the requested type. - * - * 此请求的响应对象的 `Observable`,其响应体为所请求的类型。 - * + * @return An `Observable` of the `HttpResponse`, with the response body of the requested type. */ delete(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -1162,25 +854,17 @@ export class HttpClient { * Constructs a `DELETE` request that interprets the body as a JSON object and * returns the response body as a JSON object. * - * 构造一个 `DELETE` 请求,它将请求体解释为 JSON 对象,并且返回 JSON 对象格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response, with the response body of type `Object`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `Object` 类型。 - * */ delete(url: string, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -1190,25 +874,17 @@ export class HttpClient { * Constructs a DELETE request that interprets the body as a JSON object and returns * the response in a given type. * - * 构造一个 `DELETE` 请求,它将请求体解释为 JSON 对象,并且返回给定类型的响应。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse`, with response body in the requested type. - * - * 此请求的响应对象的 `Observable`,其响应体为所请求的类型。 - * + * @return An `Observable` of the `HttpResponse`, with response body in the requested type. */ delete(url: string, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -1219,21 +895,16 @@ export class HttpClient { * `DELETE` request to execute on the server. See the individual overloads for * details on the return type. * - * 构造一个 Observable,当它被订阅时,会要求服务器执行配置好的 `DELETE` 请求。参见它的各个独立重载形式,以了解其返回值类型。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * */ delete(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, - observe?: HttpObserve, - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + observe?: 'body'|'events'|'response', + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'arraybuffer'|'blob'|'json'|'text', withCredentials?: boolean, @@ -1246,25 +917,17 @@ export class HttpClient { * Constructs a `GET` request that interprets the body as an `ArrayBuffer` and returns the * response in an `ArrayBuffer`. * - * 构造一个 `GET` 请求,它将请求体解释为 `ArrayBuffer`,并且返回 `ArrayBuffer` 格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response, with the response body as an `ArrayBuffer`. - * - * 响应对象的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ get(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable; @@ -1273,25 +936,17 @@ export class HttpClient { * Constructs a `GET` request that interprets the body as a `Blob` * and returns the response as a `Blob`. * - * 构造一个 `GET` 请求,它将请求体解释为 `Blob`,并且返回 `Blob` 格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response, with the response body as a `Blob`. - * - * 响应对象的 `Observable` ,其响应体为 `Blob` 类型。 - * */ get(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable; @@ -1300,25 +955,17 @@ export class HttpClient { * Constructs a `GET` request that interprets the body as a text string * and returns the response as a string value. * - * 构造一个 `GET` 请求,它将请求体解释为字符串,并且返回字符串格式的响应。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response, with the response body of type string. - * - * 此请求的响应对象的 `Observable`,其响应体为 string 类型。 - * */ get(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable; @@ -1327,25 +974,17 @@ export class HttpClient { * Constructs a `GET` request that interprets the body as an `ArrayBuffer` and returns * the full event stream. * - * 构造一个 `GET` 请求,它将请求体解释为 `ArrayBuffer`,并返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of all `HttpEvents` for the request, with the response + * @return An `Observable` of all `HttpEvent`s for the request, with the response * body as an `ArrayBuffer`. - * - * 表示此请求的 `HttpEvents` 的 `Observable`,响应体为 n `ArrayBuffer` 类型。 - * */ get(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; @@ -1354,24 +993,16 @@ export class HttpClient { * Constructs a `GET` request that interprets the body as a `Blob` and * returns the full event stream. * - * 构造一个 `GET` 请求,它将请求体解释为 `Blob`,并返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response, with the response body as a `Blob`. - * - * 响应对象的 `Observable` ,其响应体为 `Blob` 类型。 - * */ get(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; @@ -1380,24 +1011,16 @@ export class HttpClient { * Constructs a `GET` request that interprets the body as a text string and returns * the full event stream. * - * 构造一个 `GET` 请求,它将请求体解释为文本字符串,并且返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response, with the response body of type string. - * - * 此请求的响应对象的 `Observable`,其响应体为 string 类型。 - * */ get(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; @@ -1406,24 +1029,16 @@ export class HttpClient { * Constructs a `GET` request that interprets the body as a JSON object * and returns the full event stream. * - * 构造一个 `GET` 请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response, with the response body of type `Object`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `Object` 类型。 - * */ get(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -1433,24 +1048,16 @@ export class HttpClient { * Constructs a `GET` request that interprets the body as a JSON object and returns the full event * stream. * - * 构造一个 `GET` 请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response, with a response body in the requested type. - * - * 响应对象的 `Observable` ,其响应体为所请求的类型。 - * */ get(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -1458,108 +1065,76 @@ export class HttpClient { /** * Constructs a `GET` request that interprets the body as an `ArrayBuffer` and - * returns the full `HTTPResponse`. - * - * 构造一个 `GET` 请求,它将请求体解释为 `ArrayBuffer`,并且返回完整的 `HttpResponse`。 + * returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse` for the request, + * @return An `Observable` of the `HttpResponse` for the request, * with the response body as an `ArrayBuffer`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ get(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; /** * Constructs a `GET` request that interprets the body as a `Blob` and - * returns the full `HTTPResponse`. - * - * 构造一个 `GET` 请求,它将请求体解释为 `Blob`,并且返回完整的 `HttpResponse`。 + * returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse` for the request, - * with the response body as a `Blob`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `Blob` 类型。 - * + * @return An `Observable` of the `HttpResponse` for the request, + * with the response body as a `Blob`. */ get(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; /** * Constructs a `GET` request that interprets the body as a text stream and - * returns the full `HTTPResponse`. - * - * 构造一个 `GET` 请求,它将请求体解释为文本流,并且返回完整的 `HttpResponse`。 + * returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse` for the request, + * @return An `Observable` of the `HttpResponse` for the request, * with the response body of type string. - * - * 此请求的响应对象的 `Observable`,其响应体为 string 类型。 - * */ get(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; /** * Constructs a `GET` request that interprets the body as a JSON object and - * returns the full `HTTPResponse`. - * - * 构造一个 `GET` 请求,它将请求体解释为 JSON 对象,并且返回完整的 `HttpResponse`。 + * returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the full `HttpResponse`, * with the response body of type `Object`. - * - * 此请求的完整 `HttpResponse` 的 `Observable`,其响应体为 `Object` 类型。 - * */ get(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -1567,27 +1142,19 @@ export class HttpClient { /** * Constructs a `GET` request that interprets the body as a JSON object and - * returns the full `HTTPResponse`. - * - * 构造一个 `GET` 请求,它将请求体解释为 JSON 对象,并且返回完整的 `HttpResponse`。 + * returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the full `HTTPResponse` for the request, + * @return An `Observable` of the full `HttpResponse` for the request, * with a response body in the requested type. - * - * 此请求的完整 `HTTPResponse` 的 `Observable`,其响应体为所请求的类型。 - * */ get(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -1597,25 +1164,18 @@ export class HttpClient { * Constructs a `GET` request that interprets the body as a JSON object and * returns the response body as a JSON object. * - * 构造一个 `GET` 请求,它将请求体解释为 JSON 对象,并且返回 JSON 对象格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 * * @return An `Observable` of the response body as a JSON object. - * - * 响应体的 `Observable` 作为 JSON 对象。 - * */ get(url: string, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -1625,25 +1185,17 @@ export class HttpClient { * Constructs a `GET` request that interprets the body as a JSON object and returns * the response body in a given type. * - * 构造一个 `GET` 请求,它将请求体解释为 JSON 对象,并且返回给定类型的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse`, with a response body in the requested type. - * - * 此请求的响应对象的 `Observable`,其响应体为所请求的类型。 - * + * @return An `Observable` of the `HttpResponse`, with a response body in the requested type. */ get(url: string, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -1653,14 +1205,13 @@ export class HttpClient { * Constructs an observable that, when subscribed, causes the configured * `GET` request to execute on the server. See the individual overloads for * details on the return type. - * - * 构造一个 Observable,当它被订阅时,会要求服务器执行配置好的 `GET` 请求。参见它的各个独立重载形式,以了解其返回值类型。 - * */ get(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, - observe?: HttpObserve, - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + observe?: 'body'|'events'|'response', + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'arraybuffer'|'blob'|'json'|'text', withCredentials?: boolean, @@ -1673,25 +1224,17 @@ export class HttpClient { * Constructs a `HEAD` request that interprets the body as an `ArrayBuffer` and * returns the response as an `ArrayBuffer`. * - * 构造一个 `HEAD` 请求,它将请求体解释为 `ArrayBuffer`,并且返回 `ArrayBuffer` 格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response, with the response body as an `ArrayBuffer`. - * - * 响应对象的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ head(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable; @@ -1700,26 +1243,18 @@ export class HttpClient { * Constructs a `HEAD` request that interprets the body as a `Blob` and returns * the response as a `Blob`. * - * 构造一个 `HEAD` 请求,它将请求体解释为 `Blob`,并且返回 `Blob` 格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response, with the response body as a `Blob`. - * - * 响应对象的 `Observable` ,其响应体为 `Blob` 类型。 - * */ head(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable; @@ -1728,25 +1263,17 @@ export class HttpClient { * Constructs a `HEAD` request that interprets the body as a text string and returns the response * as a string value. * - * 构造一个 `HEAD` 请求,它将请求体解释为字符串,并且返回字符串格式的响应。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response, with the response body of type string. - * - * 此请求的响应对象的 `Observable`,其响应体为 string 类型。 - * */ head(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable; @@ -1755,25 +1282,17 @@ export class HttpClient { * Constructs a `HEAD` request that interprets the body as an `ArrayBuffer` * and returns the full event stream. * - * 构造一个 `HEAD` 请求,它把请求体解释为 `ArrayBuffer`,并返回完整事件的流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of tall `HttpEvents` for the request, + * @return An `Observable` of all `HttpEvent`s for the request, * with the response body as an `ArrayBuffer`. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ head(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; @@ -1782,25 +1301,17 @@ export class HttpClient { * Constructs a `HEAD` request that interprets the body as a `Blob` and * returns the full event stream. * - * 构造一个 `HEAD` 请求,它将请求体解释为 `Blob`,并返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of all `HttpEvents` for the request, + * @return An `Observable` of all `HttpEvent`s for the request, * with the response body as a `Blob`. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为 `Blob` 类型。 - * */ head(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; @@ -1809,25 +1320,17 @@ export class HttpClient { * Constructs a `HEAD` request that interprets the body as a text string * and returns the full event stream. * - * 构造一个 `HEAD` 请求,它将请求体解释为文本字符串,并且返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of all HttpEvents for the request, with the response body of type - * string. - * - * 表示啥此请求的 `HttpEvents for the request` 的 `Observable`,响应体为 string 类型。 - * + * @return An `Observable` of all `HttpEvent`s for the request, with the response body of type + * string. */ head(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; @@ -1836,25 +1339,17 @@ export class HttpClient { * Constructs a `HEAD` request that interprets the body as a JSON object * and returns the full HTTP event stream. * - * 构造一个 `HEAD` 请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of all `HTTPEvents` for the request, with a response body of + * @return An `Observable` of all `HttpEvent`s for the request, with a response body of * type `Object`. - * - * 表示啥此请求的 `HTTPEvents` 的 `Observable`,响应体为 `Object` 类型。 - * */ head(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -1864,25 +1359,17 @@ export class HttpClient { * Constructs a `HEAD` request that interprets the body as a JSON object and * returns the full event stream. * - * 构造一个 `HEAD` 请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * - * @return An `Observable` of all the `HTTPEvents` for the request - * , with a response body in the requested type. - * - * 表示此请求的 `HTTPEvents` 的 `Observable`,响应体为所请求的类型。 + * @return An `Observable` of all the `HttpEvent`s for the request, + * with a response body in the requested type. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. - * - * 与请求一起发送的 HTTP 选项。 - * */ head(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -1892,106 +1379,74 @@ export class HttpClient { * Constructs a `HEAD` request that interprets the body as an `ArrayBuffer` * and returns the full HTTP response. * - * 构造一个 `HEAD` 请求,它将请求体解释为 `ArrayBuffer`,并且返回完整的 `HttpResponse`。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse` for the request, + * @return An `Observable` of the `HttpResponse` for the request, * with the response body as an `ArrayBuffer`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ head(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; /** * Constructs a `HEAD` request that interprets the body as a `Blob` and returns - * the full `HTTPResponse`. - * - * 构造一个 `HEAD` 请求,它将请求体解释为 `Blob`,并且返回完整的 `HttpResponse`。 + * the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse` for the request, + * @return An `Observable` of the `HttpResponse` for the request, * with the response body as a blob. - * - * 此请求的响应对象的 `Observable`,其响应体为 blob 类型。 - * */ head(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; /** * Constructs a `HEAD` request that interprets the body as text stream - * and returns the full `HTTPResponse`. - * - * 构造一个 `HEAD` 请求,它将请求体解释为文本流,并且返回完整的 `HttpResponse`。 + * and returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse` for the request, + * @return An `Observable` of the `HttpResponse` for the request, * with the response body of type string. - * - * 此请求的响应对象的 `Observable`,其响应体为 string 类型。 - * */ head(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; /** * Constructs a `HEAD` request that interprets the body as a JSON object and - * returns the full `HTTPResponse`. - * - * 构造一个 `HEAD` 请求,它将请求体解释为 JSON 对象,并且返回完整的 `HttpResponse`。 + * returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse` for the request, + * @return An `Observable` of the `HttpResponse` for the request, * with the response body of type `Object`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `Object` 类型。 - * */ head(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -1999,27 +1454,19 @@ export class HttpClient { /** * Constructs a `HEAD` request that interprets the body as a JSON object - * and returns the full `HTTPResponse`. - * - * 构造一个 `HEAD` 请求,它将请求体解释为 JSON 对象,并且返回完整的 `HttpResponse`。 + * and returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse` for the request, + * @return An `Observable` of the `HttpResponse` for the request, * with a responmse body of the requested type. - * - * 此请求的响应对象的 `Observable`,其响应体为所请求的类型。 - * */ head(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -2029,25 +1476,17 @@ export class HttpClient { * Constructs a `HEAD` request that interprets the body as a JSON object and * returns the response body as a JSON object. * - * 构造一个 `HEAD` 请求,它将请求体解释为 JSON 对象,并且返回 JSON 对象格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * * @return An `Observable` of the response, with the response body as a JSON object. - * - * 响应对象的 `Observable` ,其响应体为 JSON 对象。 - * */ head(url: string, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -2057,26 +1496,18 @@ export class HttpClient { * Constructs a `HEAD` request that interprets the body as a JSON object and returns * the response in a given type. * - * 构造一个 `HEAD` 请求,它将请求体解释为 JSON 对象,并且返回给定类型的响应。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options The HTTP options to send with the request. * - * 与请求一起发送的 HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse` for the request, + * @return An `Observable` of the `HttpResponse` for the request, * with a response body of the given type. - * - * 此请求的响应对象的 `Observable`,其响应体为给定的类型。 - * */ head(url: string, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -2088,14 +1519,13 @@ export class HttpClient { * meta information about the resource without transferring the * resource itself. See the individual overloads for * details on the return type. - * - * 构造一个 Observable,当它被订阅时,会要求服务器执行配置好的 `HEAD` 请求。`HEAD` 方法会返回该资源的元数据,而不会传输资源本身。参见它的各个独立重载形式,以了解其返回类型。 - * */ head(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, - observe?: HttpObserve, - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + observe?: 'body'|'events'|'response', + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'arraybuffer'|'blob'|'json'|'text', withCredentials?: boolean, @@ -2106,46 +1536,24 @@ export class HttpClient { /** * Constructs a `JSONP` request for the given URL and name of the callback parameter. * - * 为给定的 URL 和回调参数的名称构造一个 `JSONP` - * * @param url The resource URL. - * - * 资源 URL。 - * * @param callbackParam The callback function name. * - * 回调函数名称。 - * * @return An `Observable` of the response object, with response body as an object. - * - * 响应对象的 `Observable` ,其响应体为对象类型。 - * */ jsonp(url: string, callbackParam: string): Observable; /** * Constructs a `JSONP` request for the given URL and name of the callback parameter. * - * 为给定的 URL 和回调参数的名称构造一个 `JSONP` - * * @param url The resource URL. - * - * 资源 URL。 - * * @param callbackParam The callback function name. * - * 回调函数名称。 - * * You must install a suitable interceptor, such as one provided by `HttpClientJsonpModule`. * If no such interceptor is reached, * then the `JSONP` request can be rejected by the configured backend. * - * 你必须安装合适的拦截器,例如 `HttpClientJsonpModule` 提供的拦截器。如果未经过此类拦截器,则所配置的后端可以拒绝 `JSONP` - * * @return An `Observable` of the response object, with response body in the requested type. - * - * 响应对象的 `Observable` ,其响应体为所请求的类型。 - * */ jsonp(url: string, callbackParam: string): Observable; @@ -2163,16 +1571,9 @@ export class HttpClient { * You can pass the callback function name as one of the query parameters. * Note that JSONP requests can only be used with `GET` requests. * - * 构造一个 `Observable` ,当订阅该 Observable 时,将通过拦截器管道分派特殊的 `JSONP` 方法。[JSONP 模式](https://en.wikipedia.org/wiki/JSONP) 可绕过某些 API 端点的局限性,这些端点不支持新的方式。更推荐使用 [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) 协议。JSONP 将端点 API 视为 JavaScript 文件,并欺骗浏览器处理请求,即使 API 端点与发出请求的客户端应用不在同一个域(源)上。端点 API 必须支持 JSONP 回调,JSONP 请求才能正常工作。此资源 API 会返回包装在回调函数中的 JSON 响应。你可以将回调函数名称作为查询参数之一传递。请注意,JSONP 请求只能与 `GET` 请求一起使用。 - * * @param url The resource URL. - * - * 资源 URL。 - * * @param callbackParam The callback function name. * - * 回调函数名称。 - * */ jsonp(url: string, callbackParam: string): Observable { return this.request('JSONP', url, { @@ -2186,25 +1587,17 @@ export class HttpClient { * Constructs an `OPTIONS` request that interprets the body as an * `ArrayBuffer` and returns the response as an `ArrayBuffer`. * - * 构造一个 `OPTIONS` 请求,它将请求体解释为 `ArrayBuffer`,并且返回 `ArrayBuffer` 格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the response, with the response body as an `ArrayBuffer`. - * - * 响应对象的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ options(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable; @@ -2213,25 +1606,17 @@ export class HttpClient { * Constructs an `OPTIONS` request that interprets the body as a `Blob` and returns * the response as a `Blob`. * - * 构造一个 `OPTIONS` 请求,它将请求体解释为 `Blob`,并且返回 `Blob` 格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the response, with the response body as a `Blob`. - * - * 响应对象的 `Observable` ,其响应体为 `Blob` 类型。 - * */ options(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable; @@ -2240,25 +1625,17 @@ export class HttpClient { * Constructs an `OPTIONS` request that interprets the body as a text string and * returns a string value. * - * 构造一个 `OPTIONS` 请求,它将请求体解释为字符串,并且返回一个字符串。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the response, with the response body of type string. - * - * 此请求的响应对象的 `Observable`,其响应体为 string 类型。 - * */ options(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable; @@ -2267,25 +1644,17 @@ export class HttpClient { * Constructs an `OPTIONS` request that interprets the body as an `ArrayBuffer` * and returns the full event stream. * - * 构造一个 `OPTIONS` 请求,它将请求体解释为 `ArrayBuffer`,并返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options HTTP options. * - * HTTP 选项。 - * - * @return An `Observable` of all `HttpEvents` for the request, + * @return An `Observable` of all `HttpEvent`s for the request, * with the response body as an `ArrayBuffer`. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ options(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; @@ -2294,25 +1663,17 @@ export class HttpClient { * Constructs an `OPTIONS` request that interprets the body as a `Blob` and * returns the full event stream. * - * 构造一个 `OPTIONS` 请求,它将请求体解释为 `Blob`,并返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options HTTP options. * - * HTTP 选项。 - * - * @return An `Observable` of all `HttpEvents` for the request, + * @return An `Observable` of all `HttpEvent`s for the request, * with the response body as a `Blob`. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为 `Blob` 类型。 - * */ options(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; @@ -2321,25 +1682,17 @@ export class HttpClient { * Constructs an `OPTIONS` request that interprets the body as a text string * and returns the full event stream. * - * 构造一个 `OPTIONS` 请求,它将请求体解释为文本字符串,并且返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options HTTP options. * - * HTTP 选项。 - * - * @return An `Observable` of all the `HTTPEvents` for the request, + * @return An `Observable` of all the `HttpEvent`s for the request, * with the response body of type string. - * - * 此请求的 `HTTPEvents` 的 `Observable`,其响应体为 string 类型。 - * */ options(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; @@ -2348,25 +1701,17 @@ export class HttpClient { * Constructs an `OPTIONS` request that interprets the body as a JSON object * and returns the full event stream. * - * 构造一个 `OPTIONS` 请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options HTTP options. * - * HTTP 选项。 - * - * @return An `Observable` of all the `HttpEvents` for the request with the response + * @return An `Observable` of all the `HttpEvent`s for the request with the response * body of type `Object`. - * - * 表示啥此请求的 `HttpEvents` 的 `Observable`,响应体为 `Object` 类型。 - * */ options(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -2376,25 +1721,17 @@ export class HttpClient { * Constructs an `OPTIONS` request that interprets the body as a JSON object and * returns the full event stream. * - * 构造一个 `OPTIONS` 请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options HTTP options. * - * HTTP 选项。 - * - * @return An `Observable` of all the `HttpEvents` for the request, + * @return An `Observable` of all the `HttpEvent`s for the request, * with a response body in the requested type. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为所请求的类型。 - * */ options(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -2404,106 +1741,74 @@ export class HttpClient { * Constructs an `OPTIONS` request that interprets the body as an `ArrayBuffer` * and returns the full HTTP response. * - * 构造一个 `OPTIONS` 请求,它将请求体解释为 `ArrayBuffer`,并且返回完整的 `HttpResponse`。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the `HttpResponse` for the request, * with the response body as an `ArrayBuffer`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ options(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; /** * Constructs an `OPTIONS` request that interprets the body as a `Blob` - * and returns the full `HTTPResponse`. - * - * 构造一个 `OPTIONS` 请求,它将请求体解释为 `Blob`,并且返回完整的 `HttpResponse`。 + * and returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the `HttpResponse` for the request, - * with the response body as a `Blob`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `Blob` 类型。 - * + * with the response body as a `Blob`. */ options(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; /** * Constructs an `OPTIONS` request that interprets the body as text stream - * and returns the full `HTTPResponse`. - * - * 构造一个 `OPTIONS` 请求,它将请求体解释为文本流,并且返回完整的 `HttpResponse`。 + * and returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the `HttpResponse` for the request, - * with the response body of type string. - * - * 此请求的响应对象的 `Observable`,其响应体为 string 类型。 - * + * with the response body of type string. */ options(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; /** * Constructs an `OPTIONS` request that interprets the body as a JSON object - * and returns the full `HTTPResponse`. - * - * 构造一个 `OPTIONS` 请求,它将请求体解释为 JSON 对象,并且返回完整的 `HttpResponse`。 + * and returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the `HttpResponse` for the request, * with the response body of type `Object`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `Object` 类型。 - * */ options(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -2511,27 +1816,19 @@ export class HttpClient { /** * Constructs an `OPTIONS` request that interprets the body as a JSON object and - * returns the full `HTTPResponse`. - * - * 构造一个 `OPTIONS` 请求,它将请求体解释为 JSON 对象,并且返回完整的 `HttpResponse`。 + * returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the `HttpResponse` for the request, * with a response body in the requested type. - * - * 此请求的响应对象的 `Observable`,其响应体为所请求的类型。 - * */ options(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -2541,25 +1838,17 @@ export class HttpClient { * Constructs an `OPTIONS` request that interprets the body as a JSON object and returns the * response body as a JSON object. * - * 构造一个 `OPTIONS` 请求,它将请求体解释为 JSON 对象,并且返回 JSON 对象格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the response, with the response body as a JSON object. - * - * 响应对象的 `Observable` ,其响应体为 JSON 对象。 - * */ options(url: string, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -2569,25 +1858,17 @@ export class HttpClient { * Constructs an `OPTIONS` request that interprets the body as a JSON object and returns the * response in a given type. * - * 构造一个 `OPTIONS` 请求,它将请求体解释为 JSON 对象,并且返回给定类型的响应。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param options HTTP options. * - * HTTP 选项。 - * - * @return An `Observable` of the `HTTPResponse`, with a response body of the given type. - * - * 此请求的响应对象的 `Observable`,其响应体为给定的类型。 - * + * @return An `Observable` of the `HttpResponse`, with a response body of the given type. */ options(url: string, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -2599,14 +1880,13 @@ export class HttpClient { * to determine the supported HTTP methods and other capabilites of an endpoint, * without implying a resource action. See the individual overloads for * details on the return type. - * - * 构造一个 `Observable`,当订阅该 Observable 时,它会让已配置的 `OPTIONS` 请求在服务器上执行。此方法允许客户端确定所支持的 HTTP 方法和端点的其他功能,而无需进行隐式资源操作。有关返回类型的详细信息,请参见各个重载。 - * */ options(url: string, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, - observe?: HttpObserve, - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + observe?: 'body'|'events'|'response', + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'arraybuffer'|'blob'|'json'|'text', withCredentials?: boolean, @@ -2618,29 +1898,18 @@ export class HttpClient { * Constructs a `PATCH` request that interprets the body as an `ArrayBuffer` and returns * the response as an `ArrayBuffer`. * - * 构造一个 `PATCH` 请求,它将请求体解释为 `ArrayBuffer`,并且返回 `ArrayBuffer` 格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to edit. - * - * 要编辑的资源。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the response, with the response body as an `ArrayBuffer`. - * - * 响应对象的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ patch(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable; @@ -2649,29 +1918,18 @@ export class HttpClient { * Constructs a `PATCH` request that interprets the body as a `Blob` and returns the response * as a `Blob`. * - * 构造一个 `PATCH` 请求,它将请求体解释为 `Blob`,并且返回 `Blob` 格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to edit. - * - * 要编辑的资源。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the response, with the response body as a `Blob`. - * - * 响应对象的 `Observable` ,其响应体为 `Blob` 类型。 - * */ patch(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable; @@ -2680,61 +1938,39 @@ export class HttpClient { * Constructs a `PATCH` request that interprets the body as a text string and * returns the response as a string value. * - * 构造一个 `PATCH` 请求,它将请求体解释为字符串,并且返回字符串格式的响应。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to edit. - * - * 要编辑的资源。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the response, with a response body of type string. - * - * 此请求的响应对象的 `Observable`,其响应体为 string 类型。 - * */ patch(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable; /** * Constructs a `PATCH` request that interprets the body as an `ArrayBuffer` and - * returns the the full event stream. - * - * 构造一个 `PATCH` 请求,它将请求体解释为 `ArrayBuffer`,并返回完整的事件流。 + * returns the full event stream. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to edit. - * - * 要编辑的资源。 - * * @param options HTTP options. * - * HTTP 选项。 - * - * @return An `Observable` of all the `HTTPevents` for the request, + * @return An `Observable` of all the `HttpEvent`s for the request, * with the response body as an `ArrayBuffer`. - * - * 此请求的 `HTTPevents` 的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ patch(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; @@ -2743,29 +1979,18 @@ export class HttpClient { * Constructs a `PATCH` request that interprets the body as a `Blob` * and returns the full event stream. * - * 构造一个 `PATCH` 请求,它将请求体解释为 `Blob`,并返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to edit. - * - * 要编辑的资源。 - * * @param options HTTP options. * - * HTTP 选项。 - * - * @return An `Observable` of all the `HTTPevents` for the request, with the + * @return An `Observable` of all the `HttpEvent`s for the request, with the * response body as `Blob`. - * - * 表示此请求的 `HTTPevents` 的 `Observable`,响应体为 `Blob` 类型。 - * */ patch(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; @@ -2774,29 +1999,18 @@ export class HttpClient { * Constructs a `PATCH` request that interprets the body as a text string and * returns the full event stream. * - * 构造一个 `PATCH` 请求,它将请求体解释为文本字符串,并且返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to edit. - * - * 要编辑的资源。 - * * @param options HTTP options. * - * HTTP 选项。 - * - * @return An `Observable` of all the `HTTPevents`for the request, with a + * @return An `Observable` of all the `HttpEvent`s for the request, with a * response body of type string. - * - * 表示啥此请求的 `HTTPevents` 的 `Observable`,响应体为 string 类型。 - * */ patch(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; @@ -2805,29 +2019,18 @@ export class HttpClient { * Constructs a `PATCH` request that interprets the body as a JSON object * and returns the full event stream. * - * 构造一个 `PATCH` 请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to edit. - * - * 要编辑的资源。 - * * @param options HTTP options. * - * HTTP 选项。 - * - * @return An `Observable` of all the `HTTPevents` for the request, + * @return An `Observable` of all the `HttpEvent`s for the request, * with a response body of type `Object`. - * - * 此请求的 `HTTPevents` 的 `Observable`,其响应体为 `Object` 类型。 - * */ patch(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -2837,29 +2040,18 @@ export class HttpClient { * Constructs a `PATCH` request that interprets the body as a JSON object * and returns the full event stream. * - * 构造一个 `PATCH` 请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to edit. - * - * 要编辑的资源。 - * * @param options HTTP options. * - * HTTP 选项。 - * - * @return An `Observable` of all the `HTTPevents` for the request, - * with a response body in the requested type. - * - * 此请求的 `HTTPevents` 的 `Observable`,其响应体为所请求的类型。 - * + * @return An `Observable` of all the `HttpEvent`s for the request, + * with a response body in the requested type. */ patch(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -2867,124 +2059,80 @@ export class HttpClient { /** * Constructs a `PATCH` request that interprets the body as an `ArrayBuffer` - * and returns the full `HTTPResponse`. - * - * 构造一个 `PATCH` 请求,它将请求体解释为 `ArrayBuffer`,并且返回完整的 `HttpResponse`。 + * and returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to edit. - * - * 要编辑的资源。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the `HttpResponse` for the request, - * with the response body as an `ArrayBuffer`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * + * with the response body as an `ArrayBuffer`. */ patch(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; /** * Constructs a `PATCH` request that interprets the body as a `Blob` and returns the full - * `HTTPResponse`. - * - * 构造一个 `PATCH` 请求,它将请求体解释为 `Blob`,并且返回完整的 `HttpResponse`。 + * `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to edit. - * - * 要编辑的资源。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the `HttpResponse` for the request, * with the response body as a `Blob`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `Blob` 类型。 - * */ patch(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; /** * Constructs a `PATCH` request that interprets the body as a text stream and returns the - * full `HTTPResponse`. - * - * 构造一个 `PATCH` 请求,它将请求体解释为文本流,并且返回完整的 `HttpResponse`。 + * full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to edit. - * - * 要编辑的资源。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the `HttpResponse` for the request, * with a response body of type string. - * - * 此请求的响应对象的 `Observable`,其响应体为 string 类型。 - * */ patch(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; /** * Constructs a `PATCH` request that interprets the body as a JSON object - * and returns the full `HTTPResponse`. - * - * 构造一个 `PATCH` 请求,它将请求体解释为 JSON 对象,并且返回完整的 `HttpResponse`。 + * and returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to edit. - * - * 要编辑的资源。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the `HttpResponse` for the request, * with a response body in the requested type. - * - * 此请求的响应对象的 `Observable`,其响应体为所请求的类型。 - * */ patch(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -2992,31 +2140,20 @@ export class HttpClient { /** * Constructs a `PATCH` request that interprets the body as a JSON object - * and returns the full `HTTPResponse`. - * - * 构造一个 `PATCH` 请求,它将请求体解释为 JSON 对象,并且返回完整的 `HttpResponse`。 + * and returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to edit. - * - * 要编辑的资源。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the `HttpResponse` for the request, * with a response body in the given type. - * - * 此请求的响应对象的 `Observable`,其响应体为给定的类型。 - * */ patch(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -3026,29 +2163,18 @@ export class HttpClient { * Constructs a `PATCH` request that interprets the body as a JSON object and * returns the response body as a JSON object. * - * 构造一个 `PATCH` 请求,它将请求体解释为 JSON 对象,并且返回 JSON 对象格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to edit. - * - * 要编辑的资源。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the response, with the response body as a JSON object. - * - * 响应对象的 `Observable` ,其响应体为 JSON 对象。 - * */ patch(url: string, body: any|null, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -3058,30 +2184,19 @@ export class HttpClient { * Constructs a `PATCH` request that interprets the body as a JSON object * and returns the response in a given type. * - * 构造一个 `PATCH` 请求,它将请求体解释为 JSON 对象,并且返回给定类型的响应。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to edit. - * - * 要编辑的资源。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the `HttpResponse` for the request, * with a response body in the given type. - * - * 此请求的响应对象的 `Observable`,其响应体为给定的类型。 - * */ patch(url: string, body: any|null, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -3091,14 +2206,13 @@ export class HttpClient { * Constructs an observable that, when subscribed, causes the configured * `PATCH` request to execute on the server. See the individual overloads for * details on the return type. - * - * 构造一个 Observable,当它被订阅时,会要求服务器执行配置好的 `PATCH` 请求。参见它的各个独立重载形式,以了解其返回值类型。 - * */ patch(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, - observe?: HttpObserve, - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + observe?: 'body'|'events'|'response', + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'arraybuffer'|'blob'|'json'|'text', withCredentials?: boolean, @@ -3110,29 +2224,18 @@ export class HttpClient { * Constructs a `POST` request that interprets the body as an `ArrayBuffer` and returns * an `ArrayBuffer`. * - * 构造一个 `POST` 请求,它将请求体解释为 `ArrayBuffer`,并且返回 `ArrayBuffer`。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The content to replace with. - * - * 要替换的内容。 - * * @param options HTTP options. * - * HTTP 选项。 - * * @return An `Observable` of the response, with the response body as an `ArrayBuffer`. - * - * 响应对象的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ post(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable; @@ -3141,29 +2244,18 @@ export class HttpClient { * Constructs a `POST` request that interprets the body as a `Blob` and returns the * response as a `Blob`. * - * 构造一个 `POST` 请求,它将请求体解释为 `Blob`,并且返回 `Blob` 格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The content to replace with. - * - * 要替换的内容。 - * * @param options HTTP options * - * HTTP 选项 - * * @return An `Observable` of the response, with the response body as a `Blob`. - * - * 响应对象的 `Observable` ,其响应体为 `Blob` 类型。 - * */ post(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable; @@ -3172,29 +2264,18 @@ export class HttpClient { * Constructs a `POST` request that interprets the body as a text string and * returns the response as a string value. * - * 构造一个 `POST` 请求,它将请求体解释为字符串,并且返回字符串格式的响应。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The content to replace with. - * - * 要替换的内容。 - * * @param options HTTP options * - * HTTP 选项 - * * @return An `Observable` of the response, with a response body of type string. - * - * 此请求的响应对象的 `Observable`,其响应体为 string 类型。 - * */ post(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable; @@ -3203,29 +2284,18 @@ export class HttpClient { * Constructs a `POST` request that interprets the body as an `ArrayBuffer` and * returns the full event stream. * - * 构造一个 `POST` 请求,它将请求体解释为 `ArrayBuffer`,并返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The content to replace with. - * - * 要替换的内容。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of all `HttpEvents` for the request, + * @return An `Observable` of all `HttpEvent`s for the request, * with the response body as an `ArrayBuffer`. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ post(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; @@ -3234,28 +2304,17 @@ export class HttpClient { * Constructs a `POST` request that interprets the body as a `Blob` * and returns the response in an observable of the full event stream. * - * 构造一个 `POST` 请求,它把请求体解释为 `Blob`,并返回完整事件流的 `Observable`。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The content to replace with. - * - * 要替换的内容。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of all `HttpEvents` for the request, with the response body as `Blob`. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为 `Blob` 类型。 - * + * @return An `Observable` of all `HttpEvent`s for the request, with the response body as `Blob`. */ post(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; @@ -3264,29 +2323,18 @@ export class HttpClient { * Constructs a `POST` request that interprets the body as a text string and returns the full * event stream. * - * 构造一个 `POST` 请求,它将请求体解释为文本字符串,并且返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The content to replace with. - * - * 要替换的内容。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of all `HttpEvents` for the request, + * @return An `Observable` of all `HttpEvent`s for the request, * with a response body of type string. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为 string 类型。 - * */ post(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; @@ -3295,29 +2343,18 @@ export class HttpClient { * Constructs a POST request that interprets the body as a JSON object and returns the full event * stream. * - * 构造一个 `POST` 请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The content to replace with. - * - * 要替换的内容。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of all `HttpEvents` for the request, + * @return An `Observable` of all `HttpEvent`s for the request, * with a response body of type `Object`. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为 `Object` 类型。 - * */ post(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -3327,29 +2364,18 @@ export class HttpClient { * Constructs a POST request that interprets the body as a JSON object and returns the full event * stream. * - * 构造一个 `POST` 请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The content to replace with. - * - * 要替换的内容。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of all `HttpEvents` for the request, + * @return An `Observable` of all `HttpEvent`s for the request, * with a response body in the requested type. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为所请求的类型。 - * */ post(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -3357,124 +2383,80 @@ export class HttpClient { /** * Constructs a POST request that interprets the body as an `ArrayBuffer` - * and returns the full `HTTPresponse`. - * - * 构造一个 POST 请求,它将请求体解释为 `ArrayBuffer` 类型,并返回完整的 `HTTPresponse` 。 + * and returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The content to replace with. - * - * 要替换的内容。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of the `HTTPResponse` for the request, with the response body as an - * `ArrayBuffer`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `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', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; /** * Constructs a `POST` request that interprets the body as a `Blob` and returns the full - * `HTTPResponse`. - * - * 构造一个 `POST` 请求,它将请求体解释为 `Blob`,并且返回完整的 `HttpResponse`。 + * `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The content to replace with. - * - * 要替换的内容。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of the `HTTPResponse` for the request, + * @return An `Observable` of the `HttpResponse` for the request, * with the response body as a `Blob`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `Blob` 类型。 - * */ post(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; /** * Constructs a `POST` request that interprets the body as a text stream and returns - * the full `HTTPResponse`. - * - * 构造一个 `POST` 请求,它将请求体解释为文本流,并且返回完整的 `HttpResponse`。 + * the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The content to replace with. - * - * 要替换的内容。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of the `HTTPResponse` for the request, + * @return An `Observable` of the `HttpResponse` for the request, * with a response body of type string. - * - * 此请求的响应对象的 `Observable`,其响应体为 string 类型。 - * */ post(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; /** * Constructs a `POST` request that interprets the body as a JSON object - * and returns the full `HTTPResponse`. - * - * 构造一个 `POST` 请求,它将请求体解释为 JSON 对象,并且返回完整的 `HttpResponse`。 + * and returns the full `HttpResponse`. * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The content to replace with. - * - * 要替换的内容。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of the `HTTPResponse` for the request, with a response body of type + * @return An `Observable` of the `HttpResponse` for the request, with a response body of type * `Object`. - * - * 表示啥此请求的 `HTTPResponse` 的 `Observable`,响应体为 `Object` 类型。 - * */ post(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -3482,31 +2464,21 @@ export class HttpClient { /** * Constructs a `POST` request that interprets the body as a JSON object and returns the full - * `HTTPResponse`. + * `HttpResponse`. * - * 构造一个 `POST` 请求,它将请求体解释为 JSON 对象,并且返回完整的 `HttpResponse`。 * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The content to replace with. - * - * 要替换的内容。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of the `HTTPResponse` for the request, with a response body in the - * requested type. - * - * 表示此请求的 `HTTPResponse` 的 `Observable`,响应体为所请求的类型。 - * + * @return An `Observable` of the `HttpResponse` for the request, with a response body in the + * requested type. */ post(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -3516,29 +2488,18 @@ export class HttpClient { * Constructs a `POST` request that interprets the body as a * JSON object and returns the response body as a JSON object. * - * 构造一个 `POST` 请求,它将请求体解释为 JSON 对象,并且返回 JSON 对象格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The content to replace with. - * - * 要替换的内容。 - * * @param options HTTP options * - * HTTP 选项 - * * @return An `Observable` of the response, with the response body as a JSON object. - * - * 响应对象的 `Observable` ,其响应体为 JSON 对象。 - * */ post(url: string, body: any|null, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -3548,30 +2509,19 @@ export class HttpClient { * Constructs a `POST` request that interprets the body as a JSON object * and returns an observable of the response. * - * 构造一个 `POST` 请求,它将请求体解释为 JSON 对象,并且返回响应对象的 `Observable`。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The content to replace with. - * - * 要替换的内容。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of the `HTTPResponse` for the request, with a response body in the - * requested type. - * - * 表示此请求的 `HTTPResponse` 的 `Observable`,响应体为所请求的类型。 - * + * @return An `Observable` of the `HttpResponse` for the request, with a response body in the + * requested type. */ post(url: string, body: any|null, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -3582,14 +2532,13 @@ export class HttpClient { * `POST` request to execute on the server. The server responds with the location of * the replaced resource. See the individual overloads for * details on the return type. - * - * 构造一个 `Observable`,当订阅该 Observable 时,它会让已配置的 `POST` 请求在服务器上执行。。服务器以替换后资源的位置进行响应。有关返回类型的详细信息,请参见各个重载。 - * */ post(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, - observe?: HttpObserve, - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + observe?: 'body'|'events'|'response', + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'arraybuffer'|'blob'|'json'|'text', withCredentials?: boolean, @@ -3601,29 +2550,18 @@ export class HttpClient { * Constructs a `PUT` request that interprets the body as an `ArrayBuffer` and returns the * response as an `ArrayBuffer`. * - * 构造一个 `PUT` 请求,它将请求体解释为 `ArrayBuffer`,并且返回 `ArrayBuffer` 格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to add/update. - * - * 要添加/更新的资源。 - * * @param options HTTP options * - * HTTP 选项 - * * @return An `Observable` of the response, with the response body as an `ArrayBuffer`. - * - * 响应对象的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ put(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable; @@ -3632,29 +2570,18 @@ export class HttpClient { * Constructs a `PUT` request that interprets the body as a `Blob` and returns * the response as a `Blob`. * - * 构造一个 `PUT` 请求,它将请求体解释为 `Blob`,并且返回 `Blob` 格式的响应体。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to add/update. - * - * 要添加/更新的资源。 - * * @param options HTTP options * - * HTTP 选项 - * * @return An `Observable` of the response, with the response body as a `Blob`. - * - * 响应对象的 `Observable` ,其响应体为 `Blob` 类型。 - * */ put(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable; @@ -3663,29 +2590,18 @@ export class HttpClient { * Constructs a `PUT` request that interprets the body as a text string and * returns the response as a string value. * - * 构造一个 `PUT` 请求,它将请求体解释为字符串,并且返回字符串格式的响应。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to add/update. - * - * 要添加/更新的资源。 - * * @param options HTTP options * - * HTTP 选项 - * * @return An `Observable` of the response, with a response body of type string. - * - * 此请求的响应对象的 `Observable`,其响应体为 string 类型。 - * */ put(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable; @@ -3694,29 +2610,18 @@ export class HttpClient { * Constructs a `PUT` request that interprets the body as an `ArrayBuffer` and * returns the full event stream. * - * 构造一个 `PUT` 请求,它将请求体解释为 `ArrayBuffer`,并返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to add/update. - * - * 要添加/更新的资源。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of all `HttpEvents` for the request, + * @return An `Observable` of all `HttpEvent`s for the request, * with the response body as an `ArrayBuffer`. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为 `ArrayBuffer` 类型。 - * */ put(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; @@ -3725,29 +2630,18 @@ export class HttpClient { * Constructs a `PUT` request that interprets the body as a `Blob` and returns the full event * stream. * - * 构造一个 `PUT` 请求,它将请求体解释为 `Blob`,并返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to add/update. - * - * 要添加/更新的资源。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of all `HttpEvents` for the request, + * @return An `Observable` of all `HttpEvent`s for the request, * with the response body as a `Blob`. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为 `Blob` 类型。 - * */ put(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; @@ -3756,29 +2650,18 @@ export class HttpClient { * Constructs a `PUT` request that interprets the body as a text string and returns the full event * stream. * - * 构造一个 `PUT` 请求,它将请求体解释为文本字符串,并且返回完整的事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to add/update. - * - * 要添加/更新的资源。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of all HttpEvents for the request, with a response body + * @return An `Observable` of all `HttpEvent`s for the request, with a response body * of type string. - * - * 表示啥此请求的 `HttpEvents for the request` 的 `Observable`,响应体为 string 类型。 - * */ put(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; @@ -3787,29 +2670,18 @@ export class HttpClient { * Constructs a `PUT` request that interprets the body as a JSON object and returns the full event * stream. * - * 构造一个 `PUT` 请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to add/update. - * - * 要添加/更新的资源。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of all `HttpEvents` for the request, with a response body of + * @return An `Observable` of all `HttpEvent`s for the request, with a response body of * type `Object`. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为 `Object` 类型。 - * */ put(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -3819,29 +2691,18 @@ export class HttpClient { * Constructs a `PUT` request that interprets the body as a JSON object and returns the * full event stream. * - * 构造一个 `PUT` 请求,它将请求体解释为 JSON 对象,并且返回完整的 HTTP 事件流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to add/update. - * - * 要添加/更新的资源。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of all `HttpEvents` for the request, + * @return An `Observable` of all `HttpEvent`s for the request, * with a response body in the requested type. - * - * 此请求的 `HttpEvents` 的 `Observable`,其响应体为所请求的类型。 - * */ put(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -3851,29 +2712,18 @@ export class HttpClient { * Constructs a `PUT` request that interprets the body as an * `ArrayBuffer` and returns an observable of the full HTTP response. * - * 构造一个 `PUT` 请求,它将请求体解释为 `ArrayBuffer`,并返回完整的 `HttpResponse`。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to add/update. - * - * 要添加/更新的资源。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of the `HTTPResponse` for the request, with the response body as an - * `ArrayBuffer`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `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', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'arraybuffer', withCredentials?: boolean, }): Observable>; @@ -3882,29 +2732,18 @@ export class HttpClient { * Constructs a `PUT` request that interprets the body as a `Blob` and returns the * full HTTP response. * - * 构造一个 `PUT` 请求,它将请求体解释为 `Blob`,并且返回完整的 `HttpResponse`。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to add/update. - * - * 要添加/更新的资源。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of the `HTTPResponse` for the request, + * @return An `Observable` of the `HttpResponse` for the request, * with the response body as a `Blob`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `Blob` 类型。 - * */ put(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'blob', withCredentials?: boolean, }): Observable>; @@ -3913,29 +2752,18 @@ export class HttpClient { * Constructs a `PUT` request that interprets the body as a text stream and returns the * full HTTP response. * - * 构造一个 `PUT` 请求,它将请求体解释为文本流,并返回完整的 `HttpResponse`。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to add/update. - * - * 要添加/更新的资源。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of the `HTTPResponse` for the request, with a response body of type - * string. - * - * 表示啥此请求的 `HTTPResponse` 的 `Observable`,响应体为 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', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType: 'text', withCredentials?: boolean, }): Observable>; @@ -3944,27 +2772,18 @@ export class HttpClient { * Constructs a `PUT` request that interprets the body as a JSON object and returns the full HTTP * response. * - * 构造一个 `PUT` 请求,它将请求体解释为 JSON 对象,并返回完整的 `HttpResponse`。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to add/update. - * - * 要添加/更新的资源。 - * * @param options HTTP options * - * HTTP 选项 - * @return An `Observable` of the `HTTPResponse` for the request, with a response body + * @return An `Observable` of the `HttpResponse` for the request, with a response body * of type 'Object`. - * - * 此请求的响应对象的 `Observable`,其响应体为 `Object` 类型。 */ put(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -3974,29 +2793,18 @@ export class HttpClient { * Constructs a `PUT` request that interprets the body as an instance of the requested type and * returns the full HTTP response. * - * 构造一个 `PUT` 请求,它将请求体解释为所请求类型的实例,并返回完整的 HTTP 响应对象。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to add/update. - * - * 要添加/更新的资源。 - * * @param options HTTP options * - * HTTP 选项 - * - * @return An `Observable` of the `HTTPResponse` for the request, + * @return An `Observable` of the `HttpResponse` for the request, * with a response body in the requested type. - * - * 此请求的响应对象的 `Observable`,其响应体为所请求的类型。 - * */ put(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -4006,29 +2814,18 @@ export class HttpClient { * Constructs a `PUT` request that interprets the body as a JSON object * and returns an observable of JSON object. * - * 构造一个 `PUT` 请求,它将请求体解释为 JSON 对象,并返回 JSON 对象的 `Observable`。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to add/update. - * - * 要添加/更新的资源。 - * * @param options HTTP options * - * HTTP 选项 - * * @return An `Observable` of the response as a JSON object. - * - * JSON 格式的响应对象的 `Observable` - * */ put(url: string, body: any|null, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -4038,29 +2835,18 @@ export class HttpClient { * Constructs a `PUT` request that interprets the body as an instance of the requested type * and returns an observable of the requested type. * - * 构造一个 `PUT` 请求,它将请求体解释为所请求类型的实例,并返回所请求类型的响应流。 - * * @param url The endpoint URL. - * - * 端点 URL。 - * * @param body The resources to add/update. - * - * 要添加/更新的资源。 - * * @param options HTTP options * - * HTTP 选项 - * * @return An `Observable` of the requested type. - * - * 所请求类型的 `Observable` - * */ put(url: string, body: any|null, options?: { headers?: HttpHeaders|{[header: string]: string | string[]}, + context?: HttpContext, observe?: 'body', - params?: HttpParams|{[param: string]: string | string[]}, + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'json', withCredentials?: boolean, @@ -4071,14 +2857,13 @@ export class HttpClient { * `PUT` request to execute on the server. The `PUT` method replaces an existing resource * with a new set of values. * See the individual overloads for details on the return type. - * - * 构造一个 `Observable`,当订阅该 Observable 时,它会让已配置的 `PUT` 请求在服务器上执行。。 `PUT` 方法用一组新值替换现有资源。有关返回类型的详细信息,请参见各个重载。 - * */ put(url: string, body: any|null, options: { headers?: HttpHeaders|{[header: string]: string | string[]}, - observe?: HttpObserve, - params?: HttpParams|{[param: string]: string | string[]}, + context?: HttpContext, + observe?: 'body'|'events'|'response', + params?: HttpParams| + {[param: string]: string | number | boolean | ReadonlyArray}, reportProgress?: boolean, responseType?: 'arraybuffer'|'blob'|'json'|'text', withCredentials?: boolean, diff --git a/packages/common/http/src/headers.ts b/packages/common/http/src/headers.ts index 324c27d57c..8c60904003 100755 --- a/packages/common/http/src/headers.ts +++ b/packages/common/http/src/headers.ts @@ -17,15 +17,11 @@ interface Update { * Instances are immutable. Modifying methods return a cloned * instance with the change. The original object is never changed. * - * Http 头的不可变集合,惰性解析。 - * * @publicApi */ export class HttpHeaders { /** * Internal map of lowercase header names to values. - * - * 一个内部映射表,key 是头名称的小写形式,value 是其值。 */ // TODO(issue/24571): remove '!'. private headers!: Map; @@ -34,31 +30,20 @@ export class HttpHeaders { /** * Internal map of lowercased header names to the normalized * form of the name (the form seen first). - * - * 一个内部映射表,key 是头名称的小写形式,值是该名称的标准化形式(即首先看到的形式)。 */ private normalizedNames: Map = new Map(); /** * Complete the lazy initialization of this object (needed before reading). - * - * 完成该对象的惰性初始化(在读取之前需要)。 */ private lazyInit!: HttpHeaders|Function|null; /** * Queued updates to be materialized the next initialization. - * - * 一个队列,用于存储下次初始化时要执行的更新。 */ private lazyUpdate: Update[]|null = null; - /** - * Constructs a new HTTP header object with the given values. - * - * 使用给定的值构造一个新的 HTTP 标头对象。 - * - */ + /** Constructs a new HTTP header object with the given values.*/ constructor(headers?: string|{[name: string]: string | string[]}) { if (!headers) { @@ -102,16 +87,9 @@ export class HttpHeaders { /** * Checks for existence of a given header. * - * 检查是否存在指定名称的头。 - * * @param name The header name to check for existence. * - * 要检查是否存在的头名称 - * * @returns True if the header exists, false otherwise. - * - * 这个头是否存在。 - * */ has(name: string): boolean { this.init(); @@ -122,16 +100,9 @@ export class HttpHeaders { /** * Retrieves the first value of a given header. * - * 返回匹配指定名称的第一个头的值。 - * * @param name The header name. * - * 要取的头名称 - * * @returns The value string if the header exists, null otherwise - * - * 如果头存在则返回一个字符串,否则返回 null - * */ get(name: string): string|null { this.init(); @@ -143,11 +114,7 @@ export class HttpHeaders { /** * Retrieves the names of the headers. * - * 返回所有的头名称。 - * * @returns A list of header names. - * - * 一个头名称列表。 */ keys(): string[] { this.init(); @@ -158,16 +125,9 @@ export class HttpHeaders { /** * Retrieves a list of values for a given header. * - * 返回头中具有指定名称的值的列表。 - * * @param name The header name from which to retrieve values. * - * 准备获取值的头名称 - * * @returns A string of values if the header exists, null otherwise. - * - * 如果标头存在则返回一个字符串数组,否则返回 null。 - * */ getAll(name: string): string[]|null { this.init(); @@ -179,20 +139,10 @@ export class HttpHeaders { * Appends a new value to the existing set of values for a header * and returns them in a clone of the original instance. * - * 将新值附加到标头的现有值集中,并在原始实例的克隆中返回它们。 - * * @param name The header name for which to append the values. - * - * 要附加值的标头名称。 - * * @param value The value to append. * - * 要附加的值。 - * * @returns A clone of the HTTP headers object with the value appended to the given header. - * - * HTTP 标头对象的克隆,带有由给定标头追加的值。 - * */ append(name: string, value: string|string[]): HttpHeaders { @@ -203,20 +153,10 @@ export class HttpHeaders { * If the header already exists, its value is replaced with the given value * in the returned object. * - * 设置或修改原始实例的克隆中给定标头的值。如果标题已经存在,则其值将在返回对象中被给定值替换。 - * * @param name The header name. - * - * 标头名称。 - * * @param value The value or values to set or overide for the given header. * - * 要设置或覆盖给定标头的一个或多个值。 - * * @returns A clone of the HTTP headers object with the newly set header value. - * - * HTTP 标头对象的克隆,其中包含新设置的标头值。 - * */ set(name: string, value: string|string[]): HttpHeaders { return this.clone({name, value, op: 's'}); @@ -224,20 +164,10 @@ export class HttpHeaders { /** * Deletes values for a given header in a clone of the original instance. * - * 在原始实例的克隆中删除给定标头的值。 - * * @param name The header name. - * - * 标头名称。 - * * @param value The value or values to delete for the given header. * - * 要删除的给定标头的一个或多个值。 - * * @returns A clone of the HTTP headers object with the given value deleted. - * - * HTTP 标头对象的克隆,其中删除了给定值。 - * */ delete(name: string, value?: string|string[]): HttpHeaders { return this.clone({name, value, op: 'd'}); diff --git a/packages/common/http/src/interceptor.ts b/packages/common/http/src/interceptor.ts index e24d8e0a3e..02eaee2527 100644 --- a/packages/common/http/src/interceptor.ts +++ b/packages/common/http/src/interceptor.ts @@ -16,35 +16,23 @@ import {HttpEvent} from './response'; /** * Intercepts and handles an `HttpRequest` or `HttpResponse`. * - * 拦截 `HttpRequest` 并处理它们。 - * * Most interceptors transform the outgoing request before passing it to the * next interceptor in the chain, by calling `next.handle(transformedReq)`. * An interceptor may transform the * response event stream as well, by applying additional RxJS operators on the stream * returned by `next.handle()`. * - * 大多数拦截器都会在外发的请求由 `next.handle(transformedReq)` 发给拦截器链中的下一个拦截器之前,对该请求进行转换。 - * 拦截器还可以通过为 `next.handle()` 返回的流添加额外的 RxJS 操作符,来对响应事件流进行转换。 - * * More rarely, an interceptor may handle the request entirely, * and compose a new event stream instead of invoking `next.handle()`. This is an * acceptable behavior, but keep in mind that further interceptors will be skipped entirely. * - * 极少数情况下,拦截器也可以自己完全处理一个请求,并且组合出新的事件流来而不必调用 `next.handle()`。 - * 这也是允许的,不过要时刻记住,这将会完全跳过所有后继拦截器。 - * * It is also rare but valid for an interceptor to return multiple responses on the * event stream for a single request. * - * 另一种同样罕见但是有用的拦截器,会为单个请求在事件流上给出多个响应对象。 - * * @publicApi * * @see [HTTP Guide](guide/http#intercepting-requests-and-responses) * - * [HTTP 一章](guide/http#intercepting-requests-and-responses) - * * @usageNotes * * To use the same instance of `HttpInterceptors` for the entire app, import the `HttpClientModule` @@ -53,28 +41,14 @@ import {HttpEvent} from './response'; * loading modules), each import creates a new copy of the `HttpClientModule`, which overwrites the * interceptors provided in the root module. * - * 要想在整个应用中使用 `HttpInterceptors` 的同一个实例,就只能在 `AppModule` 模块中导入 `HttpClientModule`,并且把拦截器都添加到应用的根注入器中。 - * 如果你在不同的模块中多次导入 `HttpClientModule`,则每次导入都会创建 `HttpClientModule` 的一个新复本,它将会覆盖根模块上提供的那些拦截器。 - * */ export interface HttpInterceptor { /** * Identifies and handles a given HTTP request. - * - * 标识并处理给定的 HTTP 请求。 - * * @param req The outgoing request object to handle. - * - * 要处理的传出请求对象。 - * * @param next The next interceptor in the chain, or the backend * if no interceptors remain in the chain. - * - * 链中的下一个拦截器,如果链中没有拦截器,则为其后端接口。 - * * @returns An observable of the event stream. - * - * 事件流的可观察值。 */ intercept(req: HttpRequest, next: HttpHandler): Observable>; } @@ -82,7 +56,7 @@ export interface HttpInterceptor { /** * `HttpHandler` which applies an `HttpInterceptor` to an `HttpRequest`. * - * `HttpHandler` 会把 `HttpInterceptor` 应用到 `HttpRequest` 上。 + * */ export class HttpInterceptorHandler implements HttpHandler { constructor(private next: HttpHandler, private interceptor: HttpInterceptor) {} @@ -96,9 +70,6 @@ export class HttpInterceptorHandler implements HttpHandler { * A multi-provider token that represents the array of registered * `HttpInterceptor` objects. * - * - * 一个多重提供者(multi-provider)令牌,它代表所有已注册的 `HttpInterceptor` 构成的数组。 - * * @publicApi */ export const HTTP_INTERCEPTORS = new InjectionToken('HTTP_INTERCEPTORS'); diff --git a/packages/common/http/src/jsonp.ts b/packages/common/http/src/jsonp.ts index 4499ad8b1c..e5be963497 100644 --- a/packages/common/http/src/jsonp.ts +++ b/packages/common/http/src/jsonp.ts @@ -12,7 +12,8 @@ import {Observable, Observer} from 'rxjs'; import {HttpBackend, HttpHandler} from './backend'; import {HttpRequest} from './request'; -import {HttpErrorResponse, HttpEvent, HttpEventType, HttpResponse} from './response'; +import {HttpErrorResponse, HttpEvent, HttpEventType, HttpResponse, HttpStatusCode} from './response'; + // Every request made through JSONP needs a callback name that's unique across the // whole page. Each request is assigned an id and the callback name is constructed @@ -43,9 +44,6 @@ export abstract class JsonpCallbackContext { /** * Processes an `HttpRequest` with the JSONP method, * by performing JSONP style requests. - * - * 通过执行 JSONP 风格的请求,使用 JSONP 方法处理 `HttpRequest` - * * @see `HttpHandler` * @see `HttpXhrBackend` * @@ -55,9 +53,6 @@ export abstract class JsonpCallbackContext { export class JsonpClientBackend implements HttpBackend { /** * A resolved promise that can be used to schedule microtasks in the event handlers. - * - * 已解决的 Promise 可用于安排事件处理器中的微任务。 - * */ private readonly resolvedPromise = Promise.resolve(); @@ -65,9 +60,6 @@ export class JsonpClientBackend implements HttpBackend { /** * Get the name of the next callback method, by incrementing the global `nextRequestId`. - * - * 通过递增 `nextRequestId`,来获取下一个回调方法的名称。 - * */ private nextCallback(): string { return `ng_jsonp_callback_${nextRequestId++}`; @@ -75,17 +67,9 @@ export class JsonpClientBackend implements HttpBackend { /** * Processes a JSONP request and returns an event stream of the results. - * - * 处理 JSONP 请求并返回结果的事件流。 - * * @param req The request object. - * - * 请求对象。 - * * @returns An observable of the response events. * - * 响应事件的可观察对象。 - * */ handle(req: HttpRequest): Observable> { // Firstly, check both the method and response type. If either doesn't match @@ -186,7 +170,7 @@ export class JsonpClientBackend implements HttpBackend { // returned. observer.next(new HttpResponse({ body, - status: 200, + status: HttpStatusCode.Ok, statusText: 'OK', url, })); @@ -244,8 +228,6 @@ export class JsonpClientBackend implements HttpBackend { * Identifies requests with the method JSONP and * shifts them to the `JsonpClientBackend`. * - * 使用 JSONP 方法标识这些请求,并将其转移到 `JsonpClientBackend` 。 - * * @see `HttpInterceptor` * * @publicApi @@ -256,21 +238,10 @@ export class JsonpInterceptor { /** * Identifies and handles a given JSONP request. - * - * 识别并处理给定的 JSONP 请求。 - * * @param req The outgoing request object to handle. - * - * 要处理的传出请求对象。 - * * @param next The next interceptor in the chain, or the backend * if no interceptors remain in the chain. - * - * 链中的下一个拦截器,如果链中没有拦截器,则为其后端接口。 - * * @returns An observable of the event stream. - * - * 事件流的可观察对象。 */ intercept(req: HttpRequest, next: HttpHandler): Observable> { if (req.method === 'JSONP') { diff --git a/packages/common/http/src/module.ts b/packages/common/http/src/module.ts index 78a0a97be4..3fae5c1056 100644 --- a/packages/common/http/src/module.ts +++ b/packages/common/http/src/module.ts @@ -15,22 +15,16 @@ import {HTTP_INTERCEPTORS, HttpInterceptor, HttpInterceptorHandler, NoopIntercep import {JsonpCallbackContext, JsonpClientBackend, JsonpInterceptor} from './jsonp'; import {HttpRequest} from './request'; import {HttpEvent} from './response'; -import {BrowserXhr, HttpXhrBackend, XhrFactory} from './xhr'; +import {HttpXhrBackend} from './xhr'; import {HttpXsrfCookieExtractor, HttpXsrfInterceptor, HttpXsrfTokenExtractor, XSRF_COOKIE_NAME, XSRF_HEADER_NAME} from './xsrf'; /** * An injectable `HttpHandler` that applies multiple interceptors * to a request before passing it to the given `HttpBackend`. * - * 一个可注入的 `HttpHandler`,它可以在把请求传给指定的 `HttpBackend` 之前,使用多个拦截器对该请求进行处理。 - * * The interceptors are loaded lazily from the injector, to allow * interceptors to themselves inject classes depending indirectly * on `HttpInterceptingHandler` itself. - * - * 这些拦截器是由注入器惰性加载起来的,以便让这些拦截器可以把其它类作为依赖注入进来, - * 还可以让它们间接注入 `HttpInterceptingHandler` 自己。 - * * @see `HttpInterceptor` */ @Injectable() @@ -53,11 +47,8 @@ export class HttpInterceptingHandler implements HttpHandler { * Constructs an `HttpHandler` that applies interceptors * to a request before passing it to the given `HttpBackend`. * - * 构造一个 `HttpHandler`,它会在把请求传给指定的 `HttpBackend` 之前,先对该请求应用各个拦截器。 - * * Use as a factory function within `HttpClientModule`. * - * 在 `HttpClientModule` 中用作工厂函数。 * */ export function interceptingHandler( @@ -72,12 +63,9 @@ export function interceptingHandler( /** * Factory function that determines where to store JSONP callbacks. * - * 一个工厂函数,用来决定在哪里保存 JSONP 回调。 - * * Ordinarily JSONP callbacks are stored on the `window` object, but this may not exist * in test environments. In that case, callbacks are stored on an anonymous object instead. * - * 原始的 JSONP 回调保存在 `window` 对象上,不过测试环境下可能不存在 `window` 对象。这时,回调就会转而保存在一个匿名对象上。 * */ export function jsonpCallbackContext(): Object { @@ -90,19 +78,13 @@ export function jsonpCallbackContext(): Object { /** * Configures XSRF protection support for outgoing requests. * - * 配置 XSRF 保护,以支持外发请求。 - * * For a server that supports a cookie-based XSRF protection system, * use directly to configure XSRF protection with the correct * cookie and header names. * - * 对于支持基于 Cookie 的 XSRF 保护系统的服务器来说,只要配置上正确的 Cookie 名和请求头的名字,就可以自动获得 XSRF 保护。 - * * If no names are supplied, the default cookie name is `XSRF-TOKEN` * and the default header name is `X-XSRF-TOKEN`. * - * 如果没有提供名字,则默认的 Cookie 名是 `XSRF-TOKEN`,默认的请求头名字是 `X-XSRF-TOKEN`。 - * * @publicApi */ @NgModule({ @@ -117,8 +99,6 @@ export function jsonpCallbackContext(): Object { export class HttpClientXsrfModule { /** * Disable the default XSRF protection. - * - * 禁用默认的 XSRF 保护。 */ static disable(): ModuleWithProviders { return { @@ -131,22 +111,11 @@ export class HttpClientXsrfModule { /** * Configure XSRF protection. - * - * 配置 XSRF 保护。 - * * @param options An object that can specify either or both * cookie name or header name. - * - * 一个对象,可以指定 Cookie 名和/或请求头的名字。 - * * - Cookie name default is `XSRF-TOKEN`. - * - * Cookie 名默认值是 `XSRF-TOKEN`。 - * * - Header name default is `X-XSRF-TOKEN`. * - * 请求头的名字默认是 `X-XSRF-TOKEN`。 - * */ static withOptions(options: { cookieName?: string, @@ -166,21 +135,14 @@ export class HttpClientXsrfModule { * Configures the [dependency injector](guide/glossary#injector) for `HttpClient` * with supporting services for XSRF. Automatically imported by `HttpClientModule`. * - * 为支持 XSRF 的 `HttpClient` 配置[依赖注入器](guide/glossary#injector)。它会被 `HttpClientModule` 自动导入。 - * * You can add interceptors to the chain behind `HttpClient` by binding them to the * multiprovider for built-in [DI token](guide/glossary#di-token) `HTTP_INTERCEPTORS`. * - * - * 通过把拦截器提供为内置的 [DI 令牌](guide/glossary#di-token) `HTTP_INTERCEPTORS`(允许有多个),你可以把它们添加到 `HttpClient` 调用链的后面。 - * * @publicApi */ @NgModule({ /** * Optional configuration for XSRF protection. - * - * 可选的 XSRF 保护的配置项。 */ imports: [ HttpClientXsrfModule.withOptions({ @@ -191,16 +153,12 @@ export class HttpClientXsrfModule { /** * Configures the [dependency injector](guide/glossary#injector) where it is imported * with supporting services for HTTP communications. - * - * 配置[依赖注入器](guide/glossary#injector),其中导入了用于支持 HTTP通讯的服务。 */ providers: [ HttpClient, {provide: HttpHandler, useClass: HttpInterceptingHandler}, HttpXhrBackend, {provide: HttpBackend, useExisting: HttpXhrBackend}, - BrowserXhr, - {provide: XhrFactory, useExisting: BrowserXhr}, ], }) export class HttpClientModule { @@ -212,14 +170,9 @@ export class HttpClientModule { * Without this module, Jsonp requests reach the backend * with method JSONP, where they are rejected. * - * 为支持 JSONP 的 `HttpClient` 配置[依赖注入器](guide/glossary#injector)。 - * 如果没有该模块,Jsonp 请求就会被发送到后端,然后被后端拒绝。 - * * You can add interceptors to the chain behind `HttpClient` by binding them to the * multiprovider for built-in [DI token](guide/glossary#di-token) `HTTP_INTERCEPTORS`. * - * 通过把拦截器提供为内置的 [DI 令牌](guide/glossary#di-token) `HTTP_INTERCEPTORS`(允许有多个),你可以把它们添加到 `HttpClient` 调用链的后面。 - * * @publicApi */ @NgModule({ diff --git a/packages/common/http/src/params.ts b/packages/common/http/src/params.ts index fab5fcb8df..a9f161eb85 100755 --- a/packages/common/http/src/params.ts +++ b/packages/common/http/src/params.ts @@ -9,12 +9,8 @@ /** * A codec for encoding and decoding parameters in URLs. * - * 一个用来在 URL 中编码和解码参数的编解码器。 - * * Used by `HttpParams`. * - * 由 `HttpParams` 使用。 - * * @publicApi **/ export interface HttpParameterCodec { @@ -28,32 +24,18 @@ export interface HttpParameterCodec { /** * Provides encoding and decoding of URL parameter and query-string values. * - * 提供 URL 参数和查询字符串值的编码和解码。 - * * Serializes and parses URL parameter keys and values to encode and decode them. * If you pass URL query parameters without encoding, * the query parameters can be misinterpreted at the receiving end. * * - * 一个 `HttpParameterCodec`,它使用 `encodeURIComponent` 和 `decodeURIComponent` 来序列化和解析 URL 参数的 key 和 value。 - * 如果你传入未编码的查询参数,那么接收端可能会对这些参数进行错误解析。请使用 `HttpParameterCodec` 类对查询字符串的值进行编码和解码。 - * * @publicApi */ export class HttpUrlEncodingCodec implements HttpParameterCodec { /** * Encodes a key name for a URL parameter or query-string. - * - * 编码 URL 参数或查询字符串的键名。 - * * @param key The key name. - * - * 键名。 - * * @returns The encoded key name. - * - * 编码过的键名。 - * */ encodeKey(key: string): string { return standardEncoding(key); @@ -61,17 +43,8 @@ export class HttpUrlEncodingCodec implements HttpParameterCodec { /** * Encodes the value of a URL parameter or query-string. - * - * 对 URL 参数或查询字符串的值进行编码。 - * * @param value The value. - * - * 值。 - * * @returns The encoded value. - * - * 编码过的值。 - * */ encodeValue(value: string): string { return standardEncoding(value); @@ -79,17 +52,8 @@ export class HttpUrlEncodingCodec implements HttpParameterCodec { /** * Decodes an encoded URL parameter or query-string key. - * - * 解码编码的 URL 参数或查询字符串键。 - * * @param key The encoded key name. - * - * 编码过的键名。 - * * @returns The decoded key name. - * - * 解码过的键名。 - * */ decodeKey(key: string): string { return decodeURIComponent(key); @@ -97,17 +61,8 @@ export class HttpUrlEncodingCodec implements HttpParameterCodec { /** * Decodes an encoded URL parameter or query-string value. - * - * 解码编码的 URL 参数或查询字符串值。 - * * @param value The encoded value. - * - * 编码过的值。 - * * @returns The decoded value. - * - * 解码过的值。 - * */ decodeValue(value: string) { return decodeURIComponent(value); @@ -118,7 +73,10 @@ export class HttpUrlEncodingCodec implements HttpParameterCodec { function paramParser(rawParams: string, codec: HttpParameterCodec): Map { const map = new Map(); if (rawParams.length > 0) { - const params: string[] = rawParams.split('&'); + // The `window.location.search` can be used while creating an instance of the `HttpParams` class + // (e.g. `new HttpParams({ fromString: window.location.search })`). The `window.location.search` + // may start with the `?` char, so we strip it if it's present. + const params: string[] = rawParams.replace(/^\?/, '').split('&'); params.forEach((param: string) => { const eqIdx = param.indexOf('='); const [key, val]: string[] = eqIdx == -1 ? @@ -143,39 +101,32 @@ function standardEncoding(v: string): string { .replace(/%3F/gi, '?') .replace(/%2F/gi, '/'); } +function valueToString(value: string|number|boolean): string { + return `${value}`; +} interface Update { param: string; - value?: string; + value?: string|number|boolean; op: 'a'|'d'|'s'; } /** * Options used to construct an `HttpParams` instance. * - * 用于构造 `HttpParams` 实例的选项。 - * * @publicApi */ export interface HttpParamsOptions { /** * String representation of the HTTP parameters in URL-query-string format. * Mutually exclusive with `fromObject`. - * - * HTTP 参数的 URL 查询字符串格式表示法。与 `fromObject` 互斥。 */ fromString?: string; - /** Object map of the HTTP parameters. Mutually exclusive with `fromString`. - * - * HTTP 参数的对象映射表。与 `fromString` 互斥。 - */ - fromObject?: {[param: string]: string|ReadonlyArray}; + /** Object map of the HTTP parameters. Mutually exclusive with `fromString`. */ + fromObject?: {[param: string]: string|number|boolean|ReadonlyArray}; - /** Encoding codec used to parse and serialize the parameters. - * - * 用来解析和序列化参数的编解码器。 - */ + /** Encoding codec used to parse and serialize the parameters. */ encoder?: HttpParameterCodec; } @@ -183,12 +134,8 @@ export interface HttpParamsOptions { * An HTTP request/response body that represents serialized parameters, * per the MIME type `application/x-www-form-urlencoded`. * - * HTTP 请求体/响应体,用来表示序列化参数,它们的 MIME 类型都是 `application/x-www-form-urlencoded`。 - * * This class is immutable; all mutation operations return a new instance. * - * 这个类是不可变的 - 每个修改类的操作都会返回一个新实例。 - * * @publicApi */ export class HttpParams { @@ -217,18 +164,9 @@ export class HttpParams { /** * Reports whether the body includes one or more values for a given parameter. - * - * 报告主体中是否包含给定参数的一个或多个值。 - * * @param param The parameter name. - * - * 参数名称。 - * * @returns True if the parameter has one or more values, * false if it has no value or is not present. - * - * 如果参数具有一个或多个值,则为 true;如果参数没有值或不存在,则为 false。 - * */ has(param: string): boolean { this.init(); @@ -237,17 +175,9 @@ export class HttpParams { /** * Retrieves the first value for a parameter. - * - * 检索参数的第一个值。 - * * @param param The parameter name. - * - * 参数名称。 - * * @returns The first value of the given parameter, * or `null` if the parameter is not present. - * - * 获取给定参数名对应的第一个值,如果没有则返回 `null`。 */ get(param: string): string|null { this.init(); @@ -257,17 +187,9 @@ export class HttpParams { /** * Retrieves all values for a parameter. - * - * 检索某个参数的所有值。 - * * @param param The parameter name. - * - * 参数名称。 - * * @returns All values in a string array, * or `null` if the parameter not present. - * - * 获取给定参数名对应的所有值,如果没有则返回 `null`。 */ getAll(param: string): string[]|null { this.init(); @@ -276,13 +198,7 @@ export class HttpParams { /** * Retrieves all the parameters for this body. - * - * 检索此 `body` 的所有参数。 - * * @returns The parameter names in a string array. - * - * 字符串数组中的参数名称。 - * */ keys(): string[] { this.init(); @@ -291,73 +207,59 @@ export class HttpParams { /** * Appends a new value to existing values for a parameter. - * - * 将新值附加到参数的现有值。 - * * @param param The parameter name. - * - * 参数名称。 - * * @param value The new value to add. - * - * 要添加的新值。 - * * @return A new body with the appended value. - * - * 构造一个新的 `body`,添加一个具有给定参数名的值。 */ - append(param: string, value: string): HttpParams { + append(param: string, value: string|number|boolean): HttpParams { return this.clone({param, value, op: 'a'}); } /** - * Replaces the value for a parameter. - * - * 替换参数的值。 - * - * @param param The parameter name. - * - * 参数名称。 - * - * @param value The new value. - * - * 新值。 - * + * Constructs a new body with appended values for the given parameter name. + * @param params parameters and values * @return A new body with the new value. - * - * 构造一个新的 `body`,具有一个给定参数名的新值。 */ - set(param: string, value: string): HttpParams { + appendAll(params: {[param: string]: string|number|boolean|ReadonlyArray}): + HttpParams { + const updates: Update[] = []; + Object.keys(params).forEach(param => { + const value = params[param]; + if (Array.isArray(value)) { + value.forEach(_value => { + updates.push({param, value: _value, op: 'a'}); + }); + } else { + updates.push({param, value: value as (string | number | boolean), op: 'a'}); + } + }); + return this.clone(updates); + } + + /** + * Replaces the value for a parameter. + * @param param The parameter name. + * @param value The new value. + * @return A new body with the new value. + */ + set(param: string, value: string|number|boolean): HttpParams { return this.clone({param, value, op: 's'}); } /** * Removes a given value or all values from a parameter. - * - * 从参数中删除给定值或所有值。 - * * @param param The parameter name. - * - * 参数名称。 - * * @param value The value to remove, if provided. - * - * 要删除的值(如果提供)。 - * * @return A new body with the given value removed, or with all values * removed if no value is specified. - * - * 构造一个新的 `body`,如果指定了 `value`,则移除具有指定 `value` 和指定 `param` 的条目;如果没有指定 `value`,则移除指定 `param` 对应的所有值。 */ - delete(param: string, value?: string): HttpParams { + delete(param: string, value?: string|number|boolean): HttpParams { return this.clone({param, value, op: 'd'}); } /** * Serializes the body to an encoded string, where key-value pairs (separated by `=`) are * separated by `&`s. - * - * 把该 `body` 序列化为一个编码过的字符串,其中的 key-value 对(用 `=` 分隔)会以 `&` 分隔。 */ toString(): string { this.init(); @@ -376,10 +278,10 @@ export class HttpParams { .join('&'); } - private clone(update: Update): HttpParams { + private clone(update: Update|Update[]): HttpParams { const clone = new HttpParams({encoder: this.encoder} as HttpParamsOptions); clone.cloneFrom = this.cloneFrom || this; - clone.updates = (this.updates || []).concat([update]); + clone.updates = (this.updates || []).concat(update); return clone; } @@ -395,13 +297,13 @@ export class HttpParams { case 'a': case 's': const base = (update.op === 'a' ? this.map!.get(update.param) : undefined) || []; - base.push(update.value!); + base.push(valueToString(update.value!)); this.map!.set(update.param, base); break; case 'd': if (update.value !== undefined) { let base = this.map!.get(update.param) || []; - const idx = base.indexOf(update.value); + const idx = base.indexOf(valueToString(update.value)); if (idx !== -1) { base.splice(idx, 1); } diff --git a/packages/common/http/src/request.ts b/packages/common/http/src/request.ts index 6a2105cbf1..bb49113b0a 100644 --- a/packages/common/http/src/request.ts +++ b/packages/common/http/src/request.ts @@ -6,20 +6,19 @@ * found in the LICENSE file at https://angular.io/license */ +import {HttpContext} from './context'; import {HttpHeaders} from './headers'; import {HttpParams} from './params'; + /** * Construction interface for `HttpRequest`s. * - * `HttpRequest` 的构造接口。 - * * All values are optional and will override default values if provided. - * - * 所有值都是可选的,如果提供了,则会覆盖默认值。 */ interface HttpRequestInit { headers?: HttpHeaders; + context?: HttpContext; reportProgress?: boolean; params?: HttpParams; responseType?: 'arraybuffer'|'blob'|'json'|'text'; @@ -28,8 +27,6 @@ interface HttpRequestInit { /** * Determine whether the given HTTP method may include a body. - * - * 决定 `body` 中将包含哪种 HTTP 方法。 */ function mightHaveBody(method: string): boolean { switch (method) { @@ -47,11 +44,7 @@ function mightHaveBody(method: string): boolean { /** * Safely assert whether the given value is an ArrayBuffer. * - * 安全地断言指定值是否 `ArrayBuffer`。 - * * In some execution environments ArrayBuffer is not defined. - * - * 在某些运行环境中可能没有定义 `ArrayBuffer`。 */ function isArrayBuffer(value: any): value is ArrayBuffer { return typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer; @@ -60,11 +53,7 @@ function isArrayBuffer(value: any): value is ArrayBuffer { /** * Safely assert whether the given value is a Blob. * - * 安全地断言指定值是否 `Blob`。 - * * In some execution environments Blob is not defined. - * - * 在某些运行环境下可能没有定义 `Blob`。 */ function isBlob(value: any): value is Blob { return typeof Blob !== 'undefined' && value instanceof Blob; @@ -73,11 +62,7 @@ function isBlob(value: any): value is Blob { /** * Safely assert whether the given value is a FormData instance. * - * 安全的断言指定的值是否为 `FormData` 实例。 - * * In some execution environments FormData is not defined. - * - * 在某些运行环境下可能没有定义 `FormData`。 */ function isFormData(value: any): value is FormData { return typeof FormData !== 'undefined' && value instanceof FormData; @@ -86,97 +71,81 @@ function isFormData(value: any): value is FormData { /** * An outgoing HTTP request with an optional typed body. * - * 一个外发的 HTTP 请求,带有一个可选的类型化的请求体(`body`)。 - * * `HttpRequest` represents an outgoing request, including URL, method, * headers, body, and other request configuration options. Instances should be * assumed to be immutable. To modify a `HttpRequest`, the `clone` * method should be used. * - * `HttpRequest` 表示一个外发请求,包括 URL、方法、请求头、请求体和其它请求配置项。 - * 它的实例都是不可变的。要修改 `HttpRequest`,应该使用 `clone` 方法。 - * * @publicApi */ export class HttpRequest { /** * The request body, or `null` if one isn't set. * - * 请求体,如果没有则为 `null`。 - * * Bodies are not enforced to be immutable, as they can include a reference to any * user-defined data type. However, interceptors should take care to preserve * idempotence by treating them as such. - * - * 请求体无法确保自己是不可变的,因为它们可以包含指向任何自定义数据类型的引用。 - * 不过,在拦截器中,要小心维护其幂等性 —— 把它们当做不可变对象。 */ readonly body: T|null = null; /** * Outgoing headers for this request. - * - * 本请求的外发请求头。 - * */ // TODO(issue/24571): remove '!'. readonly headers!: HttpHeaders; + /** + * Shared and mutable context that can be used by interceptors + */ + readonly context!: HttpContext; + /** * Whether this request should be made in a way that exposes progress events. * - * 该请求是否应该暴露出进度事件。 - * * Progress events are expensive (change detection runs on each event) and so * they should only be requested if the consumer intends to monitor them. - * - * 进度事件很昂贵(在每个事件中都会执行一次变更检测),所以只有当消费者关心这些事件时才应该请求这些进度事件。 */ readonly reportProgress: boolean = false; /** * Whether this request should be sent with outgoing credentials (cookies). - * - * 此请求是否应该带着凭证(Cookie)一起外发。 */ readonly withCredentials: boolean = false; /** * The expected response type of the server. * - * 所期待的服务器响应类型。 - * * This is used to parse the response appropriately before returning it to * the requestee. - * - * 它用来在把响应对象返回给被请求者之前以恰当的方式解析它。 */ readonly responseType: 'arraybuffer'|'blob'|'json'|'text' = 'json'; /** * The outgoing HTTP request method. - * - * 外发 HTTP 请求的方法。 */ readonly method: string; /** * Outgoing URL parameters. * - * 外发的 URL 参数。 + * To pass a string representation of HTTP parameters in the URL-query-string format, + * the `HttpParamsOptions`' `fromString` may be used. For example: + * + * ``` + * new HttpParams({fromString: 'angular=awesome'}) + * ``` */ // TODO(issue/24571): remove '!'. readonly params!: HttpParams; /** * The outgoing URL with all URL parameters set. - * - * 外发的 URL,及其所有 URL 参数。 */ readonly urlWithParams: string; constructor(method: 'DELETE'|'GET'|'HEAD'|'JSONP'|'OPTIONS', url: string, init?: { headers?: HttpHeaders, + context?: HttpContext, reportProgress?: boolean, params?: HttpParams, responseType?: 'arraybuffer'|'blob'|'json'|'text', @@ -184,6 +153,7 @@ export class HttpRequest { }); constructor(method: 'POST'|'PUT'|'PATCH', url: string, body: T|null, init?: { headers?: HttpHeaders, + context?: HttpContext, reportProgress?: boolean, params?: HttpParams, responseType?: 'arraybuffer'|'blob'|'json'|'text', @@ -191,6 +161,7 @@ export class HttpRequest { }); constructor(method: string, url: string, body: T|null, init?: { headers?: HttpHeaders, + context?: HttpContext, reportProgress?: boolean, params?: HttpParams, responseType?: 'arraybuffer'|'blob'|'json'|'text', @@ -199,6 +170,7 @@ export class HttpRequest { constructor( method: string, readonly url: string, third?: T|{ headers?: HttpHeaders, + context?: HttpContext, reportProgress?: boolean, params?: HttpParams, responseType?: 'arraybuffer'|'blob'|'json'|'text', @@ -206,6 +178,7 @@ export class HttpRequest { }|null, fourth?: { headers?: HttpHeaders, + context?: HttpContext, reportProgress?: boolean, params?: HttpParams, responseType?: 'arraybuffer'|'blob'|'json'|'text', @@ -243,6 +216,10 @@ export class HttpRequest { this.headers = options.headers; } + if (!!options.context) { + this.context = options.context; + } + if (!!options.params) { this.params = options.params; } @@ -253,6 +230,11 @@ export class HttpRequest { this.headers = new HttpHeaders(); } + // If no context have been passed in, construct a new HttpContext instance. + if (!this.context) { + this.context = new HttpContext(); + } + // If no parameters have been passed in, construct a new HttpUrlEncodedParams instance. if (!this.params) { this.params = new HttpParams(); @@ -282,8 +264,6 @@ export class HttpRequest { /** * Transform the free-form body into a serialized format suitable for * transmission to the server. - * - * 把无格式的请求体转换成适合传给服务器的序列化格式。 */ serializeBody(): ArrayBuffer|Blob|FormData|string|null { // If no body is present, no need to serialize it. @@ -313,11 +293,7 @@ export class HttpRequest { * Examine the body and attempt to infer an appropriate MIME type * for it. * - * 检测请求体,并尝试给它推断出一个合适的 MIME 类型。 - * * If no such type can be inferred, this method will return `null`. - * - * 如果没有合适的 MIME 类型,该方法就会返回 `null`。 */ detectContentTypeHeader(): string|null { // An empty body has no content type. @@ -358,6 +334,7 @@ export class HttpRequest { clone(): HttpRequest; clone(update: { headers?: HttpHeaders, + context?: HttpContext, reportProgress?: boolean, params?: HttpParams, responseType?: 'arraybuffer'|'blob'|'json'|'text', @@ -370,6 +347,7 @@ export class HttpRequest { }): HttpRequest; clone(update: { headers?: HttpHeaders, + context?: HttpContext, reportProgress?: boolean, params?: HttpParams, responseType?: 'arraybuffer'|'blob'|'json'|'text', @@ -382,6 +360,7 @@ export class HttpRequest { }): HttpRequest; clone(update: { headers?: HttpHeaders, + context?: HttpContext, reportProgress?: boolean, params?: HttpParams, responseType?: 'arraybuffer'|'blob'|'json'|'text', @@ -416,6 +395,9 @@ export class HttpRequest { let headers = update.headers || this.headers; let params = update.params || this.params; + // Pass on context if needed + const context = update.context ?? this.context; + // Check whether the caller has asked to add headers. if (update.setHeaders !== undefined) { // Set every requested header. @@ -435,6 +417,7 @@ export class HttpRequest { return new HttpRequest(method, url, body, { params, headers, + context, reportProgress, responseType, withCredentials, diff --git a/packages/common/http/src/response.ts b/packages/common/http/src/response.ts index c33f214317..8ce1a92f3a 100644 --- a/packages/common/http/src/response.ts +++ b/packages/common/http/src/response.ts @@ -11,50 +11,36 @@ import {HttpHeaders} from './headers'; /** * Type enumeration for the different kinds of `HttpEvent`. * - * 不同种类的 `HttpEvent` 的枚举类型。 - * * @publicApi */ export enum HttpEventType { /** * The request was sent out over the wire. - * - * 该请求已经在路由上发出了。 */ Sent, /** * An upload progress event was received. - * - * 收到了上传进度事件。 */ UploadProgress, /** * The response status code and headers were received. - * - * 收到了响应状态码和响应头。 */ ResponseHeader, /** * A download progress event was received. - * - * 收到了下载进度事件。 */ DownloadProgress, /** * The full response including the body was received. - * - * 收到了包括响应体在内的完整响应对象。 */ Response, /** * A custom event from an interceptor or a backend. - * - * 来自拦截器或后端的自定义事件。 */ User, } @@ -62,30 +48,22 @@ export enum HttpEventType { /** * Base interface for progress events. * - * 进度事件的基础接口。 - * * @publicApi */ export interface HttpProgressEvent { /** * Progress event type is either upload or download. - * - * 进度事件的类型或者是上传或者是下载。 */ type: HttpEventType.DownloadProgress|HttpEventType.UploadProgress; /** * Number of bytes uploaded or downloaded. - * - * 已经上传或下载的字节数。 */ loaded: number; /** * Total number of bytes to upload or download. Depending on the request or * response, this may not be computable and thus may not be present. - * - * 要上传或下载的总字节数。它可能是无法计算出来的,因此也就可能不存在,取决于是请求还是响应。 */ total?: number; } @@ -93,8 +71,6 @@ export interface HttpProgressEvent { /** * A download progress event. * - * 下载进度事件。 - * * @publicApi */ export interface HttpDownloadProgressEvent extends HttpProgressEvent { @@ -103,11 +79,7 @@ export interface HttpDownloadProgressEvent extends HttpProgressEvent { /** * The partial response body as downloaded so far. * - * 到目前为止已经下载的那部分响应体。 - * * Only present if the responseType was `text`. - * - * 只有当 responseType 是 `text` 时才会出现。 */ partialText?: string; } @@ -115,8 +87,6 @@ export interface HttpDownloadProgressEvent extends HttpProgressEvent { /** * An upload progress event. * - * 上传进度事件。 - * * @publicApi */ export interface HttpUploadProgressEvent extends HttpProgressEvent { @@ -128,9 +98,6 @@ export interface HttpUploadProgressEvent extends HttpProgressEvent { * when a request may be retried multiple times, to distinguish between * retries on the final event stream. * - * 用于表示请求已经发到服务器的事件。 - * 当请求可能被多次接受时很有用,以区分出最终事件流上的重试行为。 - * * @publicApi */ export interface HttpSentEvent { @@ -140,13 +107,9 @@ export interface HttpSentEvent { /** * A user-defined event. * - * 用户定义的事件。 - * * Grouping all custom events under this type ensures they will be handled * and forwarded by all implementations of interceptors. * - * 把所有自定义事件都分组在此类型下,以确保它们会被所有的拦截器所处理和转发。 - * * @publicApi */ export interface HttpUserEvent { @@ -157,11 +120,8 @@ export interface HttpUserEvent { * An error that represents a failed attempt to JSON.parse text coming back * from the server. * - * 一个错误对象,用来表示当视图用 JSON.parse 对从服务器返回的文本进行解析时出错。 - * * It bundles the Error object with the actual response body that failed to parse. * - * 它把 `Error` 对象和解析出错的实际响应体绑在一起。 * */ export interface HttpJsonParseError { @@ -172,12 +132,8 @@ export interface HttpJsonParseError { /** * Union type for all possible events on the response stream. * - * 响应流中所有可能出现的事件的联合类型。 - * * Typed according to the expected type of the response. * - * 其类型取决于所期待的响应类型。 - * * @publicApi */ export type HttpEvent = @@ -186,54 +142,38 @@ export type HttpEvent = /** * Base class for both `HttpResponse` and `HttpHeaderResponse`. * - * `HttpResponse` 和 `HttpHeaderResponse` 的共同基类。 - * * @publicApi */ export abstract class HttpResponseBase { /** * All response headers. - * - * 所有响应头。 */ readonly headers: HttpHeaders; /** * Response status code. - * - * 响应的状态码。 */ readonly status: number; /** * Textual description of response status code, defaults to OK. * - * 响应状态码的文本描述。 - * * Do not depend on this. - * - * 请不要在代码中依赖它。 */ readonly statusText: string; /** * URL of the resource retrieved, or null if not available. - * - * 所接收的资源的 URL,如果不可用则为 `null`。 */ readonly url: string|null; /** * Whether the status code falls in the 2xx range. - * - * 状态码是否位于 2xx 范围内。 */ readonly ok: boolean; /** * Type of the response, narrowed to either the full response or the header. - * - * 响应对象的类型,窄化为完整的响应对象或只有响应头。 */ // TODO(issue/24571): remove '!'. readonly type!: HttpEventType.Response|HttpEventType.ResponseHeader; @@ -241,12 +181,8 @@ export abstract class HttpResponseBase { /** * Super-constructor for all responses. * - * 所有响应体的上级(super)构造器。 - * * The single parameter accepted is an initialization hash. Any properties * of the response passed there will override the default values. - * - * 接受的唯一参数是一个初始化哈希值。所传进来的响应对象的任何属性都会覆盖这些默认值。 */ constructor( init: { @@ -255,7 +191,7 @@ export abstract class HttpResponseBase { statusText?: string, url?: string, }, - defaultStatus: number = 200, defaultStatusText: string = 'OK') { + defaultStatus: number = HttpStatusCode.Ok, defaultStatusText: string = 'OK') { // If the hash has values passed, use them to initialize the response. // Otherwise use the default values. this.headers = init.headers || new HttpHeaders(); @@ -272,20 +208,14 @@ export abstract class HttpResponseBase { * A partial HTTP response which only includes the status and header data, * but no response body. * - * 一个部分 HTTP 请求,它只包括状态和响应头数据,但没有响应体。 - * * `HttpHeaderResponse` is a `HttpEvent` available on the response * event stream, only when progress events are requested. * - * `HttpHeaderResponse` 是一个可用于响应事件流的 `HttpEvent`,只有在要求了进度事件时才有效。 - * * @publicApi */ export class HttpHeaderResponse extends HttpResponseBase { /** * Create a new `HttpHeaderResponse` with the given parameters. - * - * 根据给定的参数创建新的 `HttpHeaderResponse` 对象。 */ constructor(init: { headers?: HttpHeaders, @@ -301,8 +231,6 @@ export class HttpHeaderResponse extends HttpResponseBase { /** * Copy this `HttpHeaderResponse`, overriding its contents with the * given parameter hash. - * - * 复制这个 `HttpHeaderResponse`,使用给定的参数哈希对象覆盖其内容。 */ clone(update: {headers?: HttpHeaders; status?: number; statusText?: string; url?: string;} = {}): HttpHeaderResponse { @@ -321,27 +249,19 @@ export class HttpHeaderResponse extends HttpResponseBase { * A full HTTP response, including a typed response body (which may be `null` * if one was not returned). * - * 一个完整的 HTTP 响应对象,包括一个带类型的响应体(如果没返回内容,则为 `null`)。 - * * `HttpResponse` is a `HttpEvent` available on the response event * stream. * - * - * `HttpResponse` 是一个用于事件响应流的 `HttpEvent`。 * @publicApi */ export class HttpResponse extends HttpResponseBase { /** * The response body, or `null` if one was not returned. - * - * 响应体,如果没有返回内容则为 `null`。 */ readonly body: T|null; /** * Construct a new `HttpResponse`. - * - * 构造一个新的 `HttpResponse`。 */ constructor(init: { body?: T|null, @@ -388,17 +308,12 @@ export class HttpResponse extends HttpResponseBase { * non-successful HTTP status, an error while executing the request, * or some other failure which occurred during the parsing of the response. * - * 一个用于表示错误或失败的响应对象,或者来自执行请求时发生的错误给出的失败的 HTTP 状态码,或者来自在解析响应对象期间发生的其它错误。 - * * Any error returned on the `Observable` response stream will be * wrapped in an `HttpErrorResponse` to provide additional context about * the state of the HTTP layer when the error occurred. The error property * will contain either a wrapped Error object or the error response returned * from the server. * - * 任何从 `Observable` 响应流中返回的错误都会被包装成 `HttpErrorResponse` 对象,以便在发生错误时,提供关于 HTTP 层状态的额外上下文信息。 - * 该错误或者包含一个包装好的错误对象,或者包含一个从服务端返回的错误响应体。 - * * @publicApi */ export class HttpErrorResponse extends HttpResponseBase implements Error { @@ -408,8 +323,6 @@ export class HttpErrorResponse extends HttpResponseBase implements Error { /** * Errors are never okay, even when the status code is in the 2xx success range. - * - * 只要是错误,其 `ok` 就永远为 `false`,就算其 HTTP 状态码是 2xx 也一样。 */ readonly ok = false; @@ -435,3 +348,78 @@ export class HttpErrorResponse extends HttpResponseBase implements Error { this.error = init.error || null; } } + +/** + * Http status codes. + * As per https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml + * @publicApi + */ +export const enum HttpStatusCode { + Continue = 100, + SwitchingProtocols = 101, + Processing = 102, + EarlyHints = 103, + + Ok = 200, + Created = 201, + Accepted = 202, + NonAuthoritativeInformation = 203, + NoContent = 204, + ResetContent = 205, + PartialContent = 206, + MultiStatus = 207, + AlreadyReported = 208, + ImUsed = 226, + + MultipleChoices = 300, + MovedPermanently = 301, + Found = 302, + SeeOther = 303, + NotModified = 304, + UseProxy = 305, + Unused = 306, + TemporaryRedirect = 307, + PermanentRedirect = 308, + + BadRequest = 400, + Unauthorized = 401, + PaymentRequired = 402, + Forbidden = 403, + NotFound = 404, + MethodNotAllowed = 405, + NotAcceptable = 406, + ProxyAuthenticationRequired = 407, + RequestTimeout = 408, + Conflict = 409, + Gone = 410, + LengthRequired = 411, + PreconditionFailed = 412, + PayloadTooLarge = 413, + UriTooLong = 414, + UnsupportedMediaType = 415, + RangeNotSatisfiable = 416, + ExpectationFailed = 417, + ImATeapot = 418, + MisdirectedRequest = 421, + UnprocessableEntity = 422, + Locked = 423, + FailedDependency = 424, + TooEarly = 425, + UpgradeRequired = 426, + PreconditionRequired = 428, + TooManyRequests = 429, + RequestHeaderFieldsTooLarge = 431, + UnavailableForLegalReasons = 451, + + InternalServerError = 500, + NotImplemented = 501, + BadGateway = 502, + ServiceUnavailable = 503, + GatewayTimeout = 504, + HttpVersionNotSupported = 505, + VariantAlsoNegotiates = 506, + InsufficientStorage = 507, + LoopDetected = 508, + NotExtended = 510, + NetworkAuthenticationRequired = 511 +} diff --git a/packages/common/http/src/xhr.ts b/packages/common/http/src/xhr.ts index d11eea04e9..6a679c72e4 100644 --- a/packages/common/http/src/xhr.ts +++ b/packages/common/http/src/xhr.ts @@ -6,13 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ +import {XhrFactory} from '@angular/common'; import {Injectable} from '@angular/core'; import {Observable, Observer} from 'rxjs'; import {HttpBackend} from './backend'; import {HttpHeaders} from './headers'; import {HttpRequest} from './request'; -import {HttpDownloadProgressEvent, HttpErrorResponse, HttpEvent, HttpEventType, HttpHeaderResponse, HttpJsonParseError, HttpResponse, HttpUploadProgressEvent} from './response'; +import {HttpDownloadProgressEvent, HttpErrorResponse, HttpEvent, HttpEventType, HttpHeaderResponse, HttpJsonParseError, HttpResponse, HttpStatusCode, HttpUploadProgressEvent} from './response'; + const XSSI_PREFIX = /^\)\]\}',?\n/; @@ -30,44 +32,8 @@ function getResponseUrl(xhr: any): string|null { return null; } -/** - * A wrapper around the `XMLHttpRequest` constructor. - * - * `XMLHttpRequest` 构造函数的包装器。 - * - * @publicApi - */ -export abstract class XhrFactory { - abstract build(): XMLHttpRequest; -} - -/** - * A factory for `HttpXhrBackend` that uses the `XMLHttpRequest` browser API. - * - */ -@Injectable() -export class BrowserXhr implements XhrFactory { - constructor() {} - build(): any { - return (new XMLHttpRequest()); - } -} - -/** - * Tracks a response from the server that does not yet have a body. - */ -interface PartialResponse { - headers: HttpHeaders; - status: number; - statusText: string; - url: string; -} - /** * Uses `XMLHttpRequest` to send requests to a backend server. - * - * 使用 `XMLHttpRequest` 将请求发送到后端服务器。 - * * @see `HttpHandler` * @see `JsonpClientBackend` * @@ -79,17 +45,8 @@ export class HttpXhrBackend implements HttpBackend { /** * Processes a request and returns a stream of response events. - * - * 处理请求并返回响应事件流。 - * * @param req The request object. - * - * 请求对象。 - * * @returns An observable of the response events. - * - * 响应事件的可观察对象。 - * */ handle(req: HttpRequest): Observable> { // Quick check to give a better error message when a user attempts to use @@ -156,7 +113,7 @@ export class HttpXhrBackend implements HttpBackend { } // Read status and normalize an IE9 bug (https://bugs.jquery.com/ticket/1450). - const status: number = xhr.status === 1223 ? 204 : xhr.status; + const status: number = xhr.status === 1223 ? HttpStatusCode.NoContent : xhr.status; const statusText = xhr.statusText || 'OK'; // Parse headers from XMLHttpRequest - this step is lazy. @@ -182,14 +139,14 @@ export class HttpXhrBackend implements HttpBackend { // The body will be read out if present. let body: any|null = null; - if (status !== 204) { + if (status !== HttpStatusCode.NoContent) { // Use XMLHttpRequest.response if set, responseText otherwise. body = (typeof xhr.response === 'undefined') ? xhr.responseText : xhr.response; } // Normalize another potential bug (this one comes from CORS). if (status === 0) { - status = !!body ? 200 : 0; + status = !!body ? HttpStatusCode.Ok : 0; } // ok determines whether the response will be transmitted on the event or @@ -324,6 +281,8 @@ export class HttpXhrBackend implements HttpBackend { // By default, register for load and error events. xhr.addEventListener('load', onLoad); xhr.addEventListener('error', onError); + xhr.addEventListener('timeout', onError); + xhr.addEventListener('abort', onError); // Progress events are only enabled if requested. if (req.reportProgress) { @@ -345,7 +304,9 @@ export class HttpXhrBackend implements HttpBackend { return () => { // On a cancellation, remove all registered event listeners. xhr.removeEventListener('error', onError); + xhr.removeEventListener('abort', onError); xhr.removeEventListener('load', onLoad); + xhr.removeEventListener('timeout', onError); if (req.reportProgress) { xhr.removeEventListener('progress', onDownProgress); if (reqBody !== null && xhr.upload) { diff --git a/packages/common/http/src/xsrf.ts b/packages/common/http/src/xsrf.ts index 1c45a979e4..ae868547f1 100644 --- a/packages/common/http/src/xsrf.ts +++ b/packages/common/http/src/xsrf.ts @@ -21,20 +21,13 @@ export const XSRF_HEADER_NAME = new InjectionToken('XSRF_HEADER_NAME'); /** * Retrieves the current XSRF token to use with the next outgoing request. * - * 检索当前的 XSRF 令牌以用于下一个传出请求。 - * * @publicApi */ export abstract class HttpXsrfTokenExtractor { /** * Get the XSRF token to use with an outgoing request. * - * 获取 XSRF 令牌以用于传出请求。 - * * Will be called for every request, so the token may change between requests. - * - * 在每个请求中都会被调用,因此该令牌可能会在请求之间更改。 - * */ abstract getToken(): string|null; } diff --git a/packages/common/http/test/BUILD.bazel b/packages/common/http/test/BUILD.bazel index bf662c1c41..73a82e7e29 100644 --- a/packages/common/http/test/BUILD.bazel +++ b/packages/common/http/test/BUILD.bazel @@ -16,6 +16,7 @@ ts_library( # Visible to //:saucelabs_unit_tests_poc target visibility = ["//:__pkg__"], deps = [ + "//packages/common", "//packages/common/http", "//packages/common/http/testing", "//packages/core", diff --git a/packages/common/http/test/client_spec.ts b/packages/common/http/test/client_spec.ts index f76c1a2389..a974b76b45 100644 --- a/packages/common/http/test/client_spec.ts +++ b/packages/common/http/test/client_spec.ts @@ -7,7 +7,7 @@ */ import {HttpClient} from '@angular/common/http/src/client'; -import {HttpErrorResponse, HttpEventType, HttpResponse} from '@angular/common/http/src/response'; +import {HttpErrorResponse, HttpEventType, HttpResponse, HttpStatusCode} from '@angular/common/http/src/response'; import {HttpClientTestingBackend} from '@angular/common/http/testing/src/backend'; import {ddescribe, describe, fit, it} from '@angular/core/testing/src/testing_internal'; import {toArray} from 'rxjs/operators'; @@ -51,10 +51,34 @@ import {toArray} from 'rxjs/operators'; expect(req.request.headers.get('X-Option')).toEqual('true'); req.flush({}); }); - it('with params', done => { + it('with string params', done => { client.get('/test', {params: {'test': 'true'}}).subscribe(() => done()); backend.expectOne('/test?test=true').flush({}); }); + it('with an array of string params', done => { + client.get('/test', {params: {'test': ['a', 'b']}}).subscribe(() => done()); + backend.expectOne('/test?test=a&test=b').flush({}); + }); + it('with number params', done => { + client.get('/test', {params: {'test': 2}}).subscribe(() => done()); + backend.expectOne('/test?test=2').flush({}); + }); + it('with an array of number params', done => { + client.get('/test', {params: {'test': [2, 3]}}).subscribe(() => done()); + backend.expectOne('/test?test=2&test=3').flush({}); + }); + it('with boolean params', done => { + client.get('/test', {params: {'test': true}}).subscribe(() => done()); + backend.expectOne('/test?test=true').flush({}); + }); + it('with an array of boolean params', done => { + client.get('/test', {params: {'test': [true, false]}}).subscribe(() => done()); + backend.expectOne('/test?test=true&test=false').flush({}); + }); + it('with an array of params of different types', done => { + client.get('/test', {params: {'test': [true, 'a', 2] as const}}).subscribe(() => done()); + backend.expectOne('/test?test=true&test=a&test=2').flush({}); + }); it('for an arraybuffer', done => { const body = new ArrayBuffer(4); client.get('/test', {responseType: 'arraybuffer'}).subscribe(res => { @@ -105,7 +129,7 @@ import {toArray} from 'rxjs/operators'; client.post('/test', 'text body', {observe: 'response', responseType: 'text'}) .subscribe(res => { expect(res.ok).toBeTruthy(); - expect(res.status).toBe(200); + expect(res.status).toBe(HttpStatusCode.Ok); done(); }); backend.expectOne('/test').flush('hello world'); @@ -114,7 +138,7 @@ import {toArray} from 'rxjs/operators'; const body = {data: 'json body'}; client.post('/test', body, {observe: 'response', responseType: 'text'}).subscribe(res => { expect(res.ok).toBeTruthy(); - expect(res.status).toBe(200); + expect(res.status).toBe(HttpStatusCode.Ok); done(); }); const testReq = backend.expectOne('/test'); @@ -124,7 +148,7 @@ import {toArray} from 'rxjs/operators'; it('with a json body of false', done => { client.post('/test', false, {observe: 'response', responseType: 'text'}).subscribe(res => { expect(res.ok).toBeTruthy(); - expect(res.status).toBe(200); + expect(res.status).toBe(HttpStatusCode.Ok); done(); }); const testReq = backend.expectOne('/test'); @@ -134,7 +158,7 @@ import {toArray} from 'rxjs/operators'; it('with a json body of 0', done => { client.post('/test', 0, {observe: 'response', responseType: 'text'}).subscribe(res => { expect(res.ok).toBeTruthy(); - expect(res.status).toBe(200); + expect(res.status).toBe(HttpStatusCode.Ok); done(); }); const testReq = backend.expectOne('/test'); @@ -145,7 +169,7 @@ import {toArray} from 'rxjs/operators'; const body = new ArrayBuffer(4); client.post('/test', body, {observe: 'response', responseType: 'text'}).subscribe(res => { expect(res.ok).toBeTruthy(); - expect(res.status).toBe(200); + expect(res.status).toBe(HttpStatusCode.Ok); done(); }); const testReq = backend.expectOne('/test'); @@ -167,7 +191,8 @@ import {toArray} from 'rxjs/operators'; done(); }); backend.expectOne('/test').flush( - {'data': 'hello world'}, {status: 500, statusText: 'Server error'}); + {'data': 'hello world'}, + {status: HttpStatusCode.InternalServerError, statusText: 'Server error'}); }); }); }); diff --git a/packages/common/http/test/module_spec.ts b/packages/common/http/test/module_spec.ts index b242d21da9..ef8a866be9 100644 --- a/packages/common/http/test/module_spec.ts +++ b/packages/common/http/test/module_spec.ts @@ -8,6 +8,7 @@ import {HttpHandler} from '@angular/common/http/src/backend'; import {HttpClient} from '@angular/common/http/src/client'; +import {HttpContext, HttpContextToken} from '@angular/common/http/src/context'; import {HTTP_INTERCEPTORS, HttpInterceptor} from '@angular/common/http/src/interceptor'; import {HttpRequest} from '@angular/common/http/src/request'; import {HttpEvent, HttpResponse} from '@angular/common/http/src/response'; @@ -19,6 +20,8 @@ import {TestBed} from '@angular/core/testing'; import {Observable} from 'rxjs'; import {map} from 'rxjs/operators'; +const IS_INTERCEPTOR_C_ENABLED = new HttpContextToken(() => undefined); + class TestInterceptor implements HttpInterceptor { constructor(private value: string) {} @@ -49,6 +52,19 @@ class InterceptorB extends TestInterceptor { } } +class InterceptorC extends TestInterceptor { + constructor() { + super('C'); + } + + intercept(req: HttpRequest, delegate: HttpHandler): Observable> { + if (req.context.get(IS_INTERCEPTOR_C_ENABLED) === true) { + return super.intercept(req, delegate); + } + return delegate.handle(req); + } +} + @Injectable() class ReentrantInterceptor implements HttpInterceptor { constructor(private client: HttpClient) {} @@ -58,54 +74,66 @@ class ReentrantInterceptor implements HttpInterceptor { } } -{ - describe('HttpClientModule', () => { - let injector: Injector; - beforeEach(() => { - injector = TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [ - {provide: HTTP_INTERCEPTORS, useClass: InterceptorA, multi: true}, - {provide: HTTP_INTERCEPTORS, useClass: InterceptorB, multi: true}, - ], - }); - }); - it('initializes HttpClient properly', done => { - injector.get(HttpClient).get('/test', {responseType: 'text'}).subscribe((value: string) => { - expect(value).toBe('ok!'); - done(); - }); - injector.get(HttpTestingController).expectOne('/test').flush('ok!'); - }); - it('intercepts outbound responses in the order in which interceptors were bound', done => { - injector.get(HttpClient) - .get('/test', {observe: 'response', responseType: 'text'}) - .subscribe(() => done()); - const req = injector.get(HttpTestingController).expectOne('/test') as TestRequest; - expect(req.request.headers.get('Intercepted')).toEqual('A,B'); - req.flush('ok!'); - }); - it('intercepts inbound responses in the right (reverse binding) order', done => { - injector.get(HttpClient) - .get('/test', {observe: 'response', responseType: 'text'}) - .subscribe((value: HttpResponse) => { - expect(value.headers.get('Intercepted')).toEqual('B,A'); - done(); - }); - injector.get(HttpTestingController).expectOne('/test').flush('ok!'); - }); - it('allows interceptors to inject HttpClient', done => { - TestBed.resetTestingModule(); - injector = TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [ - {provide: HTTP_INTERCEPTORS, useClass: ReentrantInterceptor, multi: true}, - ], - }); - injector.get(HttpClient).get('/test').subscribe(() => { - done(); - }); - injector.get(HttpTestingController).expectOne('/test').flush('ok!'); +describe('HttpClientModule', () => { + let injector: Injector; + beforeEach(() => { + injector = TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + {provide: HTTP_INTERCEPTORS, useClass: InterceptorA, multi: true}, + {provide: HTTP_INTERCEPTORS, useClass: InterceptorB, multi: true}, + {provide: HTTP_INTERCEPTORS, useClass: InterceptorC, multi: true}, + ], }); }); -} + it('initializes HttpClient properly', done => { + injector.get(HttpClient).get('/test', {responseType: 'text'}).subscribe((value: string) => { + expect(value).toBe('ok!'); + done(); + }); + injector.get(HttpTestingController).expectOne('/test').flush('ok!'); + }); + it('intercepts outbound responses in the order in which interceptors were bound', done => { + injector.get(HttpClient) + .get('/test', {observe: 'response', responseType: 'text'}) + .subscribe(() => done()); + const req = injector.get(HttpTestingController).expectOne('/test') as TestRequest; + expect(req.request.headers.get('Intercepted')).toEqual('A,B'); + req.flush('ok!'); + }); + it('intercepts outbound responses in the order in which interceptors were bound and include specifically enabled interceptor', + done => { + injector.get(HttpClient) + .get('/test', { + observe: 'response', + responseType: 'text', + context: new HttpContext().set(IS_INTERCEPTOR_C_ENABLED, true) + }) + .subscribe(value => done()); + const req = injector.get(HttpTestingController).expectOne('/test') as TestRequest; + expect(req.request.headers.get('Intercepted')).toEqual('A,B,C'); + req.flush('ok!'); + }); + it('intercepts inbound responses in the right (reverse binding) order', done => { + injector.get(HttpClient) + .get('/test', {observe: 'response', responseType: 'text'}) + .subscribe((value: HttpResponse) => { + expect(value.headers.get('Intercepted')).toEqual('B,A'); + done(); + }); + injector.get(HttpTestingController).expectOne('/test').flush('ok!'); + }); + it('allows interceptors to inject HttpClient', done => { + TestBed.resetTestingModule(); + injector = TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + {provide: HTTP_INTERCEPTORS, useClass: ReentrantInterceptor, multi: true}, + ], + }); + injector.get(HttpClient).get('/test').subscribe(() => { + done(); + }); + injector.get(HttpTestingController).expectOne('/test').flush('ok!'); + }); +}); diff --git a/packages/common/http/test/params_spec.ts b/packages/common/http/test/params_spec.ts index a4a8b9c581..dfd3a208da 100644 --- a/packages/common/http/test/params_spec.ts +++ b/packages/common/http/test/params_spec.ts @@ -21,39 +21,136 @@ import {HttpParams} from '@angular/common/http/src/params'; expect(body.getAll('a')).toEqual(['b']); expect(body.getAll('c')).toEqual(['d', 'e']); }); + + it('should ignore question mark in a url', () => { + const body = new HttpParams({fromString: '?a=b&c=d&c=e'}); + expect(body.getAll('a')).toEqual(['b']); + expect(body.getAll('c')).toEqual(['d', 'e']); + }); + + it('should only remove question mark at the beginning of the params', () => { + const body = new HttpParams({fromString: '?a=b&c=d&?e=f'}); + expect(body.getAll('a')).toEqual(['b']); + expect(body.getAll('c')).toEqual(['d']); + expect(body.getAll('?e')).toEqual(['f']); + }); }); describe('lazy mutation', () => { - it('should allow setting parameters', () => { + it('should allow setting string parameters', () => { const body = new HttpParams({fromString: 'a=b'}); const mutated = body.set('a', 'c'); expect(mutated.toString()).toEqual('a=c'); }); - it('should allow appending parameters', () => { + it('should allow setting number parameters', () => { + const body = new HttpParams({fromString: 'a=b'}); + const mutated = body.set('a', 1); + expect(mutated.toString()).toEqual('a=1'); + }); + + it('should allow setting boolean parameters', () => { + const body = new HttpParams({fromString: 'a=b'}); + const mutated = body.set('a', true); + expect(mutated.toString()).toEqual('a=true'); + }); + + it('should allow appending string parameters', () => { const body = new HttpParams({fromString: 'a=b'}); const mutated = body.append('a', 'c'); expect(mutated.toString()).toEqual('a=b&a=c'); }); + it('should allow appending number parameters', () => { + const body = new HttpParams({fromString: 'a=b'}); + const mutated = body.append('a', 1); + expect(mutated.toString()).toEqual('a=b&a=1'); + }); + + it('should allow appending boolean parameters', () => { + const body = new HttpParams({fromString: 'a=b'}); + const mutated = body.append('a', true); + expect(mutated.toString()).toEqual('a=b&a=true'); + }); + + it('should allow appending all string parameters', () => { + const body = new HttpParams({fromString: 'a=a1&b=b1'}); + const mutated = body.appendAll({a: ['a2', 'a3'], b: 'b2'}); + expect(mutated.toString()).toEqual('a=a1&a=a2&a=a3&b=b1&b=b2'); + }); + + it('should allow appending all number parameters', () => { + const body = new HttpParams({fromString: 'a=1&b=b1'}); + const mutated = body.appendAll({a: [2, 3], b: 'b2'}); + expect(mutated.toString()).toEqual('a=1&a=2&a=3&b=b1&b=b2'); + }); + + it('should allow appending all boolean parameters', () => { + const body = new HttpParams({fromString: 'a=true&b=b1'}); + const mutated = body.appendAll({a: [true, false], b: 'b2'}); + expect(mutated.toString()).toEqual('a=true&a=true&a=false&b=b1&b=b2'); + }); + + it('should allow appending all parameters of different types', () => { + const body = new HttpParams({fromString: 'a=true&b=b1'}); + const mutated = body.appendAll({a: [true, 0, 'a1'] as const, b: 'b2'}); + expect(mutated.toString()).toEqual('a=true&a=true&a=0&a=a1&b=b1&b=b2'); + }); + it('should allow deletion of parameters', () => { const body = new HttpParams({fromString: 'a=b&c=d&e=f'}); const mutated = body.delete('c'); expect(mutated.toString()).toEqual('a=b&e=f'); }); + it('should allow deletion of parameters with specific string value', () => { + const body = new HttpParams({fromString: 'a=b&c=d&e=f'}); + const notMutated = body.delete('c', 'z'); + expect(notMutated.toString()).toEqual('a=b&c=d&e=f'); + const mutated = body.delete('c', 'd'); + expect(mutated.toString()).toEqual('a=b&e=f'); + }); + + it('should allow deletion of parameters with specific number value', () => { + const body = new HttpParams({fromString: 'a=b&c=1&e=f'}); + const notMutated = body.delete('c', 2); + expect(notMutated.toString()).toEqual('a=b&c=1&e=f'); + const mutated = body.delete('c', 1); + expect(mutated.toString()).toEqual('a=b&e=f'); + }); + + it('should allow deletion of parameters with specific boolean value', () => { + const body = new HttpParams({fromString: 'a=b&c=true&e=f'}); + const notMutated = body.delete('c', false); + expect(notMutated.toString()).toEqual('a=b&c=true&e=f'); + const mutated = body.delete('c', true); + expect(mutated.toString()).toEqual('a=b&e=f'); + }); + it('should allow chaining of mutations', () => { const body = new HttpParams({fromString: 'a=b&c=d&e=f'}); const mutated = body.append('e', 'y').delete('c').set('a', 'x').append('e', 'z'); expect(mutated.toString()).toEqual('a=x&e=f&e=y&e=z'); }); - it('should allow deletion of one value of a parameter', () => { + it('should allow deletion of one value of a string parameter', () => { const body = new HttpParams({fromString: 'a=1&a=2&a=3&a=4&a=5'}); const mutated = body.delete('a', '2').delete('a', '4'); expect(mutated.getAll('a')).toEqual(['1', '3', '5']); }); + it('should allow deletion of one value of a number parameter', () => { + const body = new HttpParams({fromString: 'a=0&a=1&a=2&a=3&a=4&a=5'}); + const mutated = body.delete('a', 0).delete('a', 4); + expect(mutated.getAll('a')).toEqual(['1', '2', '3', '5']); + }); + + it('should allow deletion of one value of a boolean parameter', () => { + const body = new HttpParams({fromString: 'a=false&a=true&a=false'}); + const mutated = body.delete('a', false); + expect(mutated.getAll('a')).toEqual(['true', 'false']); + }); + it('should not repeat mutations that have already been materialized', () => { const body = new HttpParams({fromString: 'a=b'}); const mutated = body.append('a', 'c'); @@ -82,10 +179,30 @@ import {HttpParams} from '@angular/common/http/src/params'; const body = new HttpParams({fromObject: {a: '', b: '2', c: '3'}}); expect(body.toString()).toBe('a=&b=2&c=3'); }); - it('should stringify array params', () => { + it('should stringify string array params', () => { const body = new HttpParams({fromObject: {a: '', b: ['21', '22'], c: '3'}}); expect(body.toString()).toBe('a=&b=21&b=22&c=3'); }); + it('should stringify number params', () => { + const body = new HttpParams({fromObject: {a: '', b: 2, c: 3}}); + expect(body.toString()).toBe('a=&b=2&c=3'); + }); + it('should stringify number array params', () => { + const body = new HttpParams({fromObject: {a: '', b: [21, 22], c: 3}}); + expect(body.toString()).toBe('a=&b=21&b=22&c=3'); + }); + it('should stringify boolean params', () => { + const body = new HttpParams({fromObject: {a: '', b: true, c: 3}}); + expect(body.toString()).toBe('a=&b=true&c=3'); + }); + it('should stringify boolean array params', () => { + const body = new HttpParams({fromObject: {a: '', b: [true, false], c: 3}}); + expect(body.toString()).toBe('a=&b=true&b=false&c=3'); + }); + it('should stringify array params of different types', () => { + const body = new HttpParams({fromObject: {a: ['', false, 3] as const}}); + expect(body.toString()).toBe('a=&a=false&a=3'); + }); it('should stringify empty array params', () => { const body = new HttpParams({fromObject: {a: '', b: [], c: '3'}}); expect(body.toString()).toBe('a=&c=3'); diff --git a/packages/common/http/test/request_spec.ts b/packages/common/http/test/request_spec.ts index 560de46513..7519b47e00 100644 --- a/packages/common/http/test/request_spec.ts +++ b/packages/common/http/test/request_spec.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import {HttpContext} from '@angular/common/http/src/context'; import {HttpHeaders} from '@angular/common/http/src/headers'; import {HttpParams} from '@angular/common/http/src/params'; import {HttpRequest} from '@angular/common/http/src/request'; @@ -56,6 +57,11 @@ const TEST_STRING = `I'm a body!`; const req = new HttpRequest('GET', TEST_URL, {headers}); expect(req.headers).toBe(headers); }); + it('uses the provided context if passed', () => { + const context = new HttpContext(); + const req = new HttpRequest('GET', TEST_URL, {context}); + expect(req.context).toBe(context); + }); it('defaults to Json', () => { const req = new HttpRequest('GET', TEST_URL); expect(req.responseType).toBe('json'); @@ -65,8 +71,10 @@ const TEST_STRING = `I'm a body!`; const headers = new HttpHeaders({ 'Test': 'Test header', }); + const context = new HttpContext(); const req = new HttpRequest('POST', TEST_URL, 'test body', { headers, + context, reportProgress: true, responseType: 'text', withCredentials: true, @@ -79,6 +87,8 @@ const TEST_STRING = `I'm a body!`; // Headers should be the same, as the headers are sealed. expect(clone.headers).toBe(headers); expect(clone.headers.get('Test')).toBe('Test header'); + + expect(clone.context).toBe(context); }); it('and updates the url', () => { expect(req.clone({url: '/changed'}).url).toBe('/changed'); @@ -89,6 +99,10 @@ const TEST_STRING = `I'm a body!`; it('and updates the body', () => { expect(req.clone({body: 'changed body'}).body).toBe('changed body'); }); + it('and updates the context', () => { + const newContext = new HttpContext(); + expect(req.clone({context: newContext}).context).toBe(newContext); + }); }); describe('content type detection', () => { const baseReq = new HttpRequest('POST', '/test', null); diff --git a/packages/common/http/test/response_spec.ts b/packages/common/http/test/response_spec.ts index dafcb31b2f..c9d31174d5 100644 --- a/packages/common/http/test/response_spec.ts +++ b/packages/common/http/test/response_spec.ts @@ -7,8 +7,8 @@ */ import {HttpHeaders} from '@angular/common/http/src/headers'; -import {HttpResponse} from '@angular/common/http/src/response'; -import {ddescribe, describe, it} from '@angular/core/testing/src/testing_internal'; +import {HttpResponse, HttpStatusCode} from '@angular/common/http/src/response'; +import {describe, it} from '@angular/core/testing/src/testing_internal'; { describe('HttpResponse', () => { @@ -19,21 +19,21 @@ import {ddescribe, describe, it} from '@angular/core/testing/src/testing_interna headers: new HttpHeaders({ 'Test': 'Test header', }), - status: 201, + status: HttpStatusCode.Created, statusText: 'Created', url: '/test', }); expect(resp.body).toBe('test body'); expect(resp.headers instanceof HttpHeaders).toBeTruthy(); expect(resp.headers.get('Test')).toBe('Test header'); - expect(resp.status).toBe(201); + expect(resp.status).toBe(HttpStatusCode.Created); expect(resp.statusText).toBe('Created'); expect(resp.url).toBe('/test'); }); it('uses defaults if no args passed', () => { const resp = new HttpResponse({}); expect(resp.headers).not.toBeNull(); - expect(resp.status).toBe(200); + expect(resp.status).toBe(HttpStatusCode.Ok); expect(resp.statusText).toBe('OK'); expect(resp.body).toBeNull(); expect(resp.ok).toBeTruthy(); @@ -57,21 +57,22 @@ import {ddescribe, describe, it} from '@angular/core/testing/src/testing_interna describe('.clone()', () => { it('copies the original when given no arguments', () => { const clone = - new HttpResponse({body: 'test', status: 201, statusText: 'created', url: '/test'}) + new HttpResponse( + {body: 'test', status: HttpStatusCode.Created, statusText: 'created', url: '/test'}) .clone(); expect(clone.body).toBe('test'); - expect(clone.status).toBe(201); + expect(clone.status).toBe(HttpStatusCode.Created); expect(clone.statusText).toBe('created'); expect(clone.url).toBe('/test'); expect(clone.headers).not.toBeNull(); }); it('overrides the original', () => { - const orig = - new HttpResponse({body: 'test', status: 201, statusText: 'created', url: '/test'}); - const clone = - orig.clone({body: {data: 'test'}, status: 200, statusText: 'Okay', url: '/bar'}); + const orig = new HttpResponse( + {body: 'test', status: HttpStatusCode.Created, statusText: 'created', url: '/test'}); + const clone = orig.clone( + {body: {data: 'test'}, status: HttpStatusCode.Ok, statusText: 'Okay', url: '/bar'}); expect(clone.body).toEqual({data: 'test'}); - expect(clone.status).toBe(200); + expect(clone.status).toBe(HttpStatusCode.Ok); expect(clone.statusText).toBe('Okay'); expect(clone.url).toBe('/bar'); expect(clone.headers).toBe(orig.headers); diff --git a/packages/common/http/test/xhr_mock.ts b/packages/common/http/test/xhr_mock.ts index 785a9aabae..b912e299bd 100644 --- a/packages/common/http/test/xhr_mock.ts +++ b/packages/common/http/test/xhr_mock.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ +import {XhrFactory} from '@angular/common'; import {HttpHeaders} from '@angular/common/http/src/headers'; -import {XhrFactory} from '@angular/common/http/src/xhr'; export class MockXhrFactory implements XhrFactory { // TODO(issue/24571): remove '!'. @@ -54,6 +54,8 @@ export class MockXMLHttpRequest { listeners: { error?: (event: ErrorEvent) => void, + timeout?: (event: ErrorEvent) => void, + abort?: () => void, load?: () => void, progress?: (event: ProgressEvent) => void, uploadProgress?: (event: ProgressEvent) => void, @@ -70,11 +72,13 @@ export class MockXMLHttpRequest { this.body = body; } - addEventListener(event: 'error'|'load'|'progress'|'uploadProgress', handler: Function): void { + addEventListener( + event: 'error'|'timeout'|'load'|'progress'|'uploadProgress'|'abort', + handler: Function): void { this.listeners[event] = handler as any; } - removeEventListener(event: 'error'|'load'|'progress'|'uploadProgress'): void { + removeEventListener(event: 'error'|'timeout'|'load'|'progress'|'uploadProgress'|'abort'): void { delete this.listeners[event]; } @@ -129,6 +133,18 @@ export class MockXMLHttpRequest { } } + mockTimeoutEvent(error: any): void { + if (this.listeners.timeout) { + this.listeners.timeout(error); + } + } + + mockAbortEvent(): void { + if (this.listeners.abort) { + this.listeners.abort(); + } + } + abort() { this.mockAborted = true; } diff --git a/packages/common/http/test/xhr_spec.ts b/packages/common/http/test/xhr_spec.ts index ae37b9e5d4..e8a462a0b1 100644 --- a/packages/common/http/test/xhr_spec.ts +++ b/packages/common/http/test/xhr_spec.ts @@ -7,9 +7,9 @@ */ import {HttpRequest} from '@angular/common/http/src/request'; -import {HttpDownloadProgressEvent, HttpErrorResponse, HttpEvent, HttpEventType, HttpHeaderResponse, HttpResponse, HttpResponseBase, HttpUploadProgressEvent} from '@angular/common/http/src/response'; +import {HttpDownloadProgressEvent, HttpErrorResponse, HttpEvent, HttpEventType, HttpHeaderResponse, HttpResponse, HttpResponseBase, HttpStatusCode, HttpUploadProgressEvent} from '@angular/common/http/src/response'; import {HttpXhrBackend} from '@angular/common/http/src/xhr'; -import {ddescribe, describe, fit, it} from '@angular/core/testing/src/testing_internal'; +import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {Observable} from 'rxjs'; import {toArray} from 'rxjs/operators'; @@ -78,39 +78,42 @@ const XSSI_PREFIX = ')]}\'\n'; }); it('handles a text response', () => { const events = trackEvents(backend.handle(TEST_POST)); - factory.mock.mockFlush(200, 'OK', 'some response'); + factory.mock.mockFlush(HttpStatusCode.Ok, 'OK', 'some response'); expect(events.length).toBe(2); expect(events[1].type).toBe(HttpEventType.Response); expect(events[1] instanceof HttpResponse).toBeTruthy(); const res = events[1] as HttpResponse; expect(res.body).toBe('some response'); - expect(res.status).toBe(200); + expect(res.status).toBe(HttpStatusCode.Ok); expect(res.statusText).toBe('OK'); }); it('handles a json response', () => { const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'}))); - factory.mock.mockFlush(200, 'OK', JSON.stringify({data: 'some data'})); + factory.mock.mockFlush(HttpStatusCode.Ok, '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'); }); it('handles a blank json response', () => { const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'}))); - factory.mock.mockFlush(200, 'OK', ''); + factory.mock.mockFlush(HttpStatusCode.Ok, 'OK', ''); expect(events.length).toBe(2); const res = events[1] as HttpResponse<{data: string}>; expect(res.body).toBeNull(); }); it('handles a json error response', () => { const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'}))); - factory.mock.mockFlush(500, 'Error', JSON.stringify({data: 'some data'})); + factory.mock.mockFlush( + HttpStatusCode.InternalServerError, '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'); }); 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'})); + factory.mock.mockFlush( + HttpStatusCode.InternalServerError, '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'); @@ -118,14 +121,15 @@ const XSSI_PREFIX = ')]}\'\n'; it('handles a json string response', () => { const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'}))); expect(factory.mock.responseType).toEqual('text'); - factory.mock.mockFlush(200, 'OK', JSON.stringify('this is a string')); + factory.mock.mockFlush(HttpStatusCode.Ok, 'OK', JSON.stringify('this is a string')); expect(events.length).toBe(2); const res = events[1] as HttpResponse; expect(res.body).toEqual('this is a string'); }); it('handles a json response with an XSSI prefix', () => { const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'}))); - factory.mock.mockFlush(200, 'OK', XSSI_PREFIX + JSON.stringify({data: 'some data'})); + factory.mock.mockFlush( + HttpStatusCode.Ok, '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'); @@ -136,7 +140,7 @@ const XSSI_PREFIX = ')]}\'\n'; expect(err.error).toBe('this is the error'); done(); }); - factory.mock.mockFlush(400, 'Bad Request', 'this is the error'); + factory.mock.mockFlush(HttpStatusCode.BadRequest, 'Bad Request', 'this is the error'); }); it('emits real errors via the error path', done => { backend.handle(TEST_POST).subscribe(undefined, (err: HttpErrorResponse) => { @@ -147,6 +151,17 @@ const XSSI_PREFIX = ')]}\'\n'; }); factory.mock.mockErrorEvent(new Error('blah')); }); + it('emits timeout if the request times out', done => { + backend.handle(TEST_POST).subscribe({ + error: (error: HttpErrorResponse) => { + expect(error instanceof HttpErrorResponse).toBeTrue(); + expect(error.error instanceof Error).toBeTrue(); + expect(error.url).toBe('/test'); + done(); + }, + }); + factory.mock.mockTimeoutEvent(new Error('timeout')); + }); it('avoids abort a request when fetch operation is completed', done => { const abort = jasmine.createSpy('abort'); @@ -156,7 +171,14 @@ const XSSI_PREFIX = ')]}\'\n'; }); factory.mock.abort = abort; - factory.mock.mockFlush(200, 'OK', 'Done'); + factory.mock.mockFlush(HttpStatusCode.Ok, 'OK', 'Done'); + }); + it('emits an error when browser cancels a request', done => { + backend.handle(TEST_POST).subscribe(undefined, (err: HttpErrorResponse) => { + expect(err instanceof HttpErrorResponse).toBe(true); + done(); + }); + factory.mock.mockAbortEvent(); }); describe('progress events', () => { it('are emitted for download progress', done => { @@ -187,7 +209,7 @@ const XSSI_PREFIX = ')]}\'\n'; factory.mock.mockDownloadProgressEvent(100, 300); factory.mock.responseText = 'download'; factory.mock.mockDownloadProgressEvent(200, 300); - factory.mock.mockFlush(200, 'OK', 'downloaded'); + factory.mock.mockFlush(HttpStatusCode.Ok, 'OK', 'downloaded'); }); it('are emitted for upload progress', done => { backend.handle(TEST_POST.clone({reportProgress: true})) @@ -211,7 +233,7 @@ const XSSI_PREFIX = ')]}\'\n'; }); factory.mock.mockUploadProgressEvent(100, 300); factory.mock.mockUploadProgressEvent(200, 300); - factory.mock.mockFlush(200, 'OK', 'Done'); + factory.mock.mockFlush(HttpStatusCode.Ok, 'OK', 'Done'); }); it('are emitted when both upload and download progress are available', done => { backend.handle(TEST_POST.clone({reportProgress: true})) @@ -228,7 +250,7 @@ const XSSI_PREFIX = ')]}\'\n'; }); factory.mock.mockUploadProgressEvent(100, 300); factory.mock.mockDownloadProgressEvent(200, 300); - factory.mock.mockFlush(200, 'OK', 'Done'); + factory.mock.mockFlush(HttpStatusCode.Ok, 'OK', 'Done'); }); it('are emitted even if length is not computable', done => { backend.handle(TEST_POST.clone({reportProgress: true})) @@ -245,7 +267,7 @@ const XSSI_PREFIX = ')]}\'\n'; }); factory.mock.mockUploadProgressEvent(100); factory.mock.mockDownloadProgressEvent(200); - factory.mock.mockFlush(200, 'OK', 'Done'); + factory.mock.mockFlush(HttpStatusCode.Ok, 'OK', 'Done'); }); it('include ResponseHeader with headers and status', done => { backend.handle(TEST_POST.clone({reportProgress: true})) @@ -264,7 +286,7 @@ const XSSI_PREFIX = ')]}\'\n'; }); factory.mock.mockResponseHeaders = 'Test: Test header\nContent-Type: text/plain\n'; factory.mock.mockDownloadProgressEvent(200); - factory.mock.mockFlush(200, 'OK', 'Done'); + factory.mock.mockFlush(HttpStatusCode.Ok, 'OK', 'Done'); }); it('are unsubscribed along with the main request', () => { const sub = backend.handle(TEST_POST.clone({reportProgress: true})).subscribe(); @@ -282,16 +304,16 @@ const XSSI_PREFIX = ')]}\'\n'; event.type === HttpEventType.ResponseHeader) .map(event => event as HttpResponseBase) .forEach(event => { - expect(event.status).toBe(203); + expect(event.status).toBe(HttpStatusCode.NonAuthoritativeInformation); expect(event.headers.get('Test')).toEqual('This is a test'); }); done(); }); factory.mock.mockResponseHeaders = 'Test: This is a test\n'; - factory.mock.status = 203; + factory.mock.status = HttpStatusCode.NonAuthoritativeInformation; factory.mock.mockDownloadProgressEvent(100, 300); factory.mock.mockResponseHeaders = 'Test: should never be read\n'; - factory.mock.mockFlush(203, 'OK', 'Testing 1 2 3'); + factory.mock.mockFlush(HttpStatusCode.NonAuthoritativeInformation, 'OK', 'Testing 1 2 3'); }); }); describe('gets response URL', () => { @@ -304,7 +326,7 @@ const XSSI_PREFIX = ')]}\'\n'; done(); }); factory.mock.responseURL = '/response/url'; - factory.mock.mockFlush(200, 'OK', 'Test'); + factory.mock.mockFlush(HttpStatusCode.Ok, 'OK', 'Test'); }); it('from X-Request-URL header if XHR.responseURL is not present', done => { backend.handle(TEST_POST).pipe(toArray()).subscribe(events => { @@ -315,7 +337,7 @@ const XSSI_PREFIX = ')]}\'\n'; done(); }); factory.mock.mockResponseHeaders = 'X-Request-URL: /response/url\n'; - factory.mock.mockFlush(200, 'OK', 'Test'); + factory.mock.mockFlush(HttpStatusCode.Ok, 'OK', 'Test'); }); it('falls back on Request.url if neither are available', done => { backend.handle(TEST_POST).pipe(toArray()).subscribe(events => { @@ -325,7 +347,7 @@ const XSSI_PREFIX = ')]}\'\n'; expect(response.url).toBe('/test'); done(); }); - factory.mock.mockFlush(200, 'OK', 'Test'); + factory.mock.mockFlush(HttpStatusCode.Ok, 'OK', 'Test'); }); }); describe('corrects for quirks', () => { @@ -334,7 +356,7 @@ const XSSI_PREFIX = ')]}\'\n'; expect(events.length).toBe(2); expect(events[1].type).toBe(HttpEventType.Response); const response = events[1] as HttpResponse; - expect(response.status).toBe(204); + expect(response.status).toBe(HttpStatusCode.NoContent); done(); }); factory.mock.mockFlush(1223, 'IE Special Status', 'Test'); @@ -344,7 +366,7 @@ const XSSI_PREFIX = ')]}\'\n'; expect(events.length).toBe(2); expect(events[1].type).toBe(HttpEventType.Response); const response = events[1] as HttpResponse; - expect(response.status).toBe(200); + expect(response.status).toBe(HttpStatusCode.Ok); done(); }); factory.mock.mockFlush(0, 'CORS 0 status', 'Test'); diff --git a/packages/common/http/testing/src/api.ts b/packages/common/http/testing/src/api.ts index 486cee6c14..80a7b4aff4 100644 --- a/packages/common/http/testing/src/api.ts +++ b/packages/common/http/testing/src/api.ts @@ -13,8 +13,6 @@ import {TestRequest} from './request'; /** * Defines a matcher for requests based on URL, method, or both. * - * 为基于 URL 和/或 method 的请求定义匹配器。 - * * @publicApi */ export interface RequestMatch { @@ -26,16 +24,11 @@ export interface RequestMatch { * Controller to be injected into tests, that allows for mocking and flushing * of requests. * - * 控制器将被注入到测试中,从而可以模拟和刷新请求。 - * * @publicApi */ export abstract class HttpTestingController { /** * Search for requests that match the given parameter, without any expectations. - * - * 搜索与给定参数匹配且不带任何期望语句的请求。 - * */ abstract match(match: string|RequestMatch|((req: HttpRequest) => boolean)): TestRequest[]; @@ -43,13 +36,8 @@ export abstract class HttpTestingController { * Expect that a single request has been made which matches the given URL, and return its * mock. * - * 期望发出一个与给定 URL 匹配的单个请求,然后返回其模拟对象。 - * * If no such request has been made, or more than one such request has been made, fail with an * error message including the given request description, if any. - * - * 如果没有发出这样的请求,或者发出过多个这样的请求,则失败,并显示一条错误消息,其中包括给定请求的描述(如果有)。 - * */ abstract expectOne(url: string, description?: string): TestRequest; @@ -57,13 +45,8 @@ export abstract class HttpTestingController { * Expect that a single request has been made which matches the given parameters, and return * its mock. * - * 期望发出一个与给定参数匹配的单个请求,然后返回其模拟对象。 - * * If no such request has been made, or more than one such request has been made, fail with an * error message including the given request description, if any. - * - * 如果没有发出这样的请求,或者发出过多个这样的请求,则失败,并显示一条错误消息,其中包括给定请求的描述(如果有)。 - * */ abstract expectOne(params: RequestMatch, description?: string): TestRequest; @@ -71,13 +54,8 @@ export abstract class HttpTestingController { * Expect that a single request has been made which matches the given predicate function, and * return its mock. * - * 期望发出一个与给定谓词函数匹配的单个请求,然后返回其模拟对象。 - * * If no such request has been made, or more than one such request has been made, fail with an * error message including the given request description, if any. - * - * 如果没有发出这样的请求,或者发出过多个这样的请求,则失败,并显示一条错误消息,其中包括给定请求的描述(如果有)。 - * */ abstract expectOne(matchFn: ((req: HttpRequest) => boolean), description?: string): TestRequest; @@ -86,13 +64,8 @@ export abstract class HttpTestingController { * Expect that a single request has been made which matches the given condition, and return * its mock. * - * 期望发出一个与给定条件匹配的单个请求,然后返回其模拟对象。 - * * If no such request has been made, or more than one such request has been made, fail with an * error message including the given request description, if any. - * - * 如果没有发出这样的请求,或者发出过多个这样的请求,则失败,并显示一条错误消息,其中包括给定请求的描述(如果有)。 - * */ abstract expectOne( match: string|RequestMatch|((req: HttpRequest) => boolean), @@ -101,52 +74,32 @@ export abstract class HttpTestingController { /** * Expect that no requests have been made which match the given URL. * - * 期望没有发出过与给定 URL 匹配的请求。 - * * If a matching request has been made, fail with an error message including the given request * description, if any. - * - * 如果发出了匹配的请求,则失败并显示一条错误消息,其中包括给定请求的描述(如果有)。 - * */ abstract expectNone(url: string, description?: string): void; /** * Expect that no requests have been made which match the given parameters. * - * 期望没有发出过与给定参数匹配的请求。 - * * If a matching request has been made, fail with an error message including the given request * description, if any. - * - * 如果发出了匹配的请求,则失败并显示一条错误消息,其中包括给定请求的描述(如果有)。 - * */ abstract expectNone(params: RequestMatch, description?: string): void; /** * Expect that no requests have been made which match the given predicate function. * - * 期望没有发出过与给定谓词函数匹配的请求。 - * * If a matching request has been made, fail with an error message including the given request * description, if any. - * - * 如果发出了匹配的请求,则失败并显示一条错误消息,其中包括给定请求的描述(如果有)。 - * */ abstract expectNone(matchFn: ((req: HttpRequest) => boolean), description?: string): void; /** * Expect that no requests have been made which match the given condition. * - * 期望没有发出过与给定条件匹配的请求。 - * * If a matching request has been made, fail with an error message including the given request * description, if any. - * - * 如果发出了匹配的请求,则失败并显示一条错误消息,其中包括给定请求的描述(如果有)。 - * */ abstract expectNone( match: string|RequestMatch|((req: HttpRequest) => boolean), description?: string): void; @@ -154,18 +107,11 @@ export abstract class HttpTestingController { /** * Verify that no unmatched requests are outstanding. * - * 验证没有任何未匹配的请求正在等待。 - * * If any requests are outstanding, fail with an error message indicating which requests were not * handled. * - * 如果有任何未完成的请求,则失败并显示一条错误消息,指出未处理哪些请求。 - * * If `ignoreCancelled` is not set (the default), `verify()` will also fail if cancelled requests * were not explicitly matched. - * - * 如果未设置过 `ignoreCancelled`(默认),且未明确匹配已取消的请求,则 `verify()` 就会失败。 - * */ abstract verify(opts?: {ignoreCancelled?: boolean}): void; } diff --git a/packages/common/http/testing/src/module.ts b/packages/common/http/testing/src/module.ts index 8aced4df25..f1c76c0cfa 100644 --- a/packages/common/http/testing/src/module.ts +++ b/packages/common/http/testing/src/module.ts @@ -16,12 +16,8 @@ import {HttpClientTestingBackend} from './backend'; /** * Configures `HttpClientTestingBackend` as the `HttpBackend` used by `HttpClient`. * - * 提供配置 `HttpClientTestingBackend` 作为 `HttpBackend` 所使用 `HttpClient` 。 - * * Inject `HttpTestingController` to expect and flush requests in your tests. * - * 注入 `HttpTestingController` 以期望并刷新测试中的请求。 - * * @publicApi */ @NgModule({ diff --git a/packages/common/http/testing/src/request.ts b/packages/common/http/testing/src/request.ts index 794bf77d76..f2039bc7f3 100644 --- a/packages/common/http/testing/src/request.ts +++ b/packages/common/http/testing/src/request.ts @@ -6,27 +6,20 @@ * found in the LICENSE file at https://angular.io/license */ -import {HttpErrorResponse, HttpEvent, HttpHeaders, HttpRequest, HttpResponse} from '@angular/common/http'; +import {HttpErrorResponse, HttpEvent, HttpHeaders, HttpRequest, HttpResponse, HttpStatusCode} from '@angular/common/http'; import {Observer} from 'rxjs'; /** * A mock requests that was received and is ready to be answered. * - * 已收到并准备好进行应答的模拟请求。 - * * This interface allows access to the underlying `HttpRequest`, and allows * responding with `HttpEvent`s or `HttpErrorResponse`s. * - * 此接口允许访问底层 `HttpRequest`,并允许使用 `HttpEvent` 或 `HttpErrorResponse` 进行响应。 - * * @publicApi */ export class TestRequest { /** * Whether the request was cancelled after it was sent. - * - * 请求在发送后是否已被取消。 - * */ get cancelled(): boolean { return this._cancelled; @@ -45,11 +38,7 @@ export class TestRequest { * If the request specifies an expected body type, the body is converted into the requested type. * Otherwise, the body is converted to `JSON` by default. * - * 通过返回 body 以及其他 HTTP 信息(例如响应标头)(如果提供过)来解析请求。如果请求指定了预期的 body 类型,则将 body 转换为所请求的类型。否则,body 在默认情况下转换成 `JSON`。 - * * Both successful and unsuccessful responses can be delivered via `flush()`. - * - * 成功和失败的响应都可以通过 `flush()` 传递。 */ flush( body: ArrayBuffer|Blob|boolean|string|number|Object|(boolean|string|number|Object|null)[]| @@ -67,10 +56,10 @@ export class TestRequest { (opts.headers instanceof HttpHeaders) ? opts.headers : new HttpHeaders(opts.headers); body = _maybeConvertBody(this.request.responseType, body); let statusText: string|undefined = opts.statusText; - let status: number = opts.status !== undefined ? opts.status : 200; + let status: number = opts.status !== undefined ? opts.status : HttpStatusCode.Ok; if (opts.status === undefined) { if (body === null) { - status = 204; + status = HttpStatusCode.NoContent; statusText = statusText || 'No Content'; } else { statusText = statusText || 'OK'; @@ -89,9 +78,6 @@ export class TestRequest { /** * Resolve the request by returning an `ErrorEvent` (e.g. simulating a network failure). - * - * 通过返回 `ErrorEvent` (例如,模拟网络故障)来解决请求。 - * */ error(error: ErrorEvent, opts: { headers?: HttpHeaders|{[name: string]: string | string[]}, @@ -118,9 +104,6 @@ export class TestRequest { /** * Deliver an arbitrary `HttpEvent` (such as a progress event) on the response stream for this * request. - * - * 在响应流上为此请求传递一个任意的 `HttpEvent` - * */ event(event: HttpEvent): void { if (this.cancelled) { diff --git a/packages/common/locales/BUILD.bazel b/packages/common/locales/BUILD.bazel index c887cc32ae..20c6fa9511 100644 --- a/packages/common/locales/BUILD.bazel +++ b/packages/common/locales/BUILD.bazel @@ -23,7 +23,7 @@ pkg_npm( # dependencies in case require was called. # We don't actually import anything in the locale code so we can # null out the require reference passed into the module. - "factory\(require, exports\)": "factory(null, exports)", + "factory\\(require, exports\\)": "factory(null, exports)", }, deps = [":locales"], ) diff --git a/packages/common/package.json b/packages/common/package.json index b942b2fa21..0545b7b509 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -6,7 +6,7 @@ "license": "MIT", "locales": "locales", "dependencies": { - "tslib": "^2.0.0" + "tslib": "^2.1.0" }, "peerDependencies": { "@angular/core": "0.0.0-PLACEHOLDER", diff --git a/packages/common/src/common.ts b/packages/common/src/common.ts index 5f0c0b1068..50ba9daa5a 100644 --- a/packages/common/src/common.ts +++ b/packages/common/src/common.ts @@ -26,3 +26,4 @@ export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCase export {PLATFORM_BROWSER_ID as ɵPLATFORM_BROWSER_ID, PLATFORM_SERVER_ID as ɵPLATFORM_SERVER_ID, PLATFORM_WORKER_APP_ID as ɵPLATFORM_WORKER_APP_ID, PLATFORM_WORKER_UI_ID as ɵPLATFORM_WORKER_UI_ID, isPlatformBrowser, isPlatformServer, isPlatformWorkerApp, isPlatformWorkerUi} from './platform_id'; export {VERSION} from './version'; export {ViewportScroller, NullViewportScroller as ɵNullViewportScroller} from './viewport_scroller'; +export {XhrFactory} from './xhr'; diff --git a/packages/common/src/common_module.ts b/packages/common/src/common_module.ts index 67d4eec3f8..044e78fc84 100644 --- a/packages/common/src/common_module.ts +++ b/packages/common/src/common_module.ts @@ -20,19 +20,11 @@ import {COMMON_PIPES} from './pipes/index'; * Re-exported by `BrowserModule`, which is included automatically in the root * `AppModule` when you create a new app with the CLI `new` command. * - * 导出所有基本的 Angular 指令和管道,例如 `NgIf`、`NgForOf`、`DecimalPipe` 等。 - * 它会由 `BrowserModule` 进行二次导出,当你使用 CLI 的 `new` 命令创建新应用时,`BrowserModule` 会自动包含在根模块 `AppModule` 中。 - * * * The `providers` options configure the NgModule's injector to provide * localization dependencies to members. - * - * `providers` 选项配置了 NgModule 的注入器,来为其成员提供本地化依赖。 - * * * The `exports` options make the declared directives and pipes available for import * by other NgModules. * - * `exports` 选项让这里声明的指令和管道可以被导入到其它 NgModule 中。 - * * @publicApi */ @NgModule({ diff --git a/packages/common/src/directives/ng_class.ts b/packages/common/src/directives/ng_class.ts index f7e76d0a97..8b8c69d3b3 100644 --- a/packages/common/src/directives/ng_class.ts +++ b/packages/common/src/directives/ng_class.ts @@ -13,7 +13,6 @@ type NgClassSupportedTypes = string[]|Set|{[klass: string]: any}|null|un * @ngModule CommonModule * * @usageNotes - * * ``` * ... * @@ -30,25 +29,12 @@ type NgClassSupportedTypes = string[]|Set|{[klass: string]: any}|null|un * * Adds and removes CSS classes on an HTML element. * - * 从 HTML 元素上添加和移除 CSS 类。 - * * The CSS classes are updated as follows, depending on the type of the expression evaluation: - * - * CSS 类会根据表达式求值结果进行更新,更新逻辑取决于结果的类型: - * * - `string` - the CSS classes listed in the string (space delimited) are added, - * - * `string` - 会把列在字符串中的 CSS 类(空格分隔)添加进来, - * * - `Array` - the CSS classes declared as Array elements are added, - * - * `Array` - 会把数组中的各个元素作为 CSS 类添加进来, - * * - `Object` - keys are CSS classes that get added when the expression given in the value * evaluates to a truthy value, otherwise they are removed. * - * `Object` - 每个 key 都是要处理的 CSS 类,当表达式求值为真的时候则添加,为假则移除。 - * * @publicApi */ @Directive({selector: '[ngClass]'}) @@ -130,15 +116,10 @@ export class NgClass implements DoCheck { /** * Applies a collection of CSS classes to the DOM element. * - * 将 CSS 类的集合应用于 DOM 元素。 - * * For argument of type Set and Array CSS class names contained in those collections are always * added. * For argument of type Map CSS class name in the map's key is toggled based on the value (added * for truthy and removed for falsy). - * - * 对于 Set 和 Array 类型的参数,总是添加那些集合中包含的 CSS 类名称。对于 Map 类型的参数,与映射表中的键名对应的 CSS 类名称会根据该值进行切换(为 true 添加,为 falsy 删除)。 - * */ private _applyClasses(rawClassVal: NgClassSupportedTypes) { if (rawClassVal) { @@ -153,9 +134,6 @@ export class NgClass implements DoCheck { /** * Removes a collection of CSS classes from the DOM element. This is mostly useful for cleanup * purposes. - * - * 从 DOM 元素中删除 CSS 类的集合。这主要用于清理目的。 - * */ private _removeClasses(rawClassVal: NgClassSupportedTypes) { if (rawClassVal) { diff --git a/packages/common/src/directives/ng_component_outlet.ts b/packages/common/src/directives/ng_component_outlet.ts index 7868e59d1b..26eb178c2b 100644 --- a/packages/common/src/directives/ng_component_outlet.ts +++ b/packages/common/src/directives/ng_component_outlet.ts @@ -13,54 +13,32 @@ import {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, NgMo * Instantiates a single {@link Component} type and inserts its Host View into current View. * `NgComponentOutlet` provides a declarative approach for dynamic component creation. * - * 实例化单个 {@link Component} 类型,并将其宿主视图插入当前视图。`NgComponentOutlet` 为动态组件创建提供了一种声明式方法。 - * * `NgComponentOutlet` requires a component type, if a falsy value is set the view will clear and * any existing component will get destroyed. * - * `NgComponentOutlet` 所需的组件类型,如果设置为假值,则视图将被清除并且任何现有组件将被销毁。 - * * @usageNotes * * ### Fine tune control * - * ### 微调控制 - * * You can control the component creation process by using the following optional attributes: * - * 你可以使用以下可选属性来控制组件的创建过程: - * * * `ngComponentOutletInjector`: Optional custom {@link Injector} that will be used as parent for * the Component. Defaults to the injector of the current view container. * - * `ngComponentOutletInjector`:可选的自定义 {@link Injector},将用作此组件的父级。默认为当前视图容器的注入器。 - * * * `ngComponentOutletContent`: Optional list of projectable nodes to insert into the content * section of the component, if exists. * - * `ngComponentOutletContent`:要插入到组件内容部分的可投影节点的可选列表(如果存在)。 - * * * `ngComponentOutletNgModuleFactory`: Optional module factory to allow dynamically loading other * module, then load a component from that module. * - * `ngComponentOutletNgModuleFactory`:可选模块工厂,允许动态加载其他模块,然后从该模块加载组件。 - * * ### Syntax * - * ### 语法 - * * Simple - * - * 简单 - * * ``` * * ``` * * Customized injector/content - * - * 定制的注入器/内容 - * * ``` * @@ -80,8 +55,6 @@ import {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, NgMo * * ### A simple example * - * ### 一个简单的例子 - * * {@example common/ngComponentOutlet/ts/module.ts region='SimpleExample'} * * A more complete example with additional options: diff --git a/packages/common/src/directives/ng_for_of.ts b/packages/common/src/directives/ng_for_of.ts index a35053d9a9..c39daa3c82 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, isDevMode, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, NgIterable, TemplateRef, TrackByFunction, ViewContainerRef} from '@angular/core'; +import {Directive, DoCheck, EmbeddedViewRef, Input, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, NgIterable, TemplateRef, TrackByFunction, ViewContainerRef} from '@angular/core'; /** * @publicApi @@ -37,20 +37,14 @@ export class NgForOfContext = NgIterable> { * The directive is placed on an element, which becomes the parent * of the cloned templates. * - * 一种[结构型指令](guide/structural-directives),为集合中的每个条目渲染一个模板。如果指令放置在一个元素上,该元素就会成为克隆后的模板的父级。 - * * The `ngForOf` directive is generally used in the - * [shorthand form](guide/structural-directives#the-asterisk--prefix) `*ngFor`. + * [shorthand form](guide/structural-directives#asterisk) `*ngFor`. * In this form, the template to be rendered for each iteration is the content * of an anchor element containing the directive. * - * `ngForOf` 指令通常在 `*ngFor` 的[简写形式](guide/structural-directives#the-asterisk--prefix)内部使用。在这种形式下,每次迭代要渲染的模板是包含指令的锚点元素的内容。 - * * The following example shows the shorthand syntax with some options, * contained in an `
  • ` element. * - * `
  • ` 元素中包含一些选项的简写语法。 - * * ``` *
  • ...
  • * ``` @@ -60,12 +54,8 @@ export class NgForOfContext = NgIterable> { * The content of the `` element is the `
  • ` element that held the * short-form directive. * - * 简写形式会扩展为使用 `` 元素 `ngForOf` 选择器的长形式。`` 元素的内容是包裹此简写格式指令的 `
  • ` 元素。 - * * Here is the expanded version of the short-form example. * - * 这是简写形式示例的扩展版本。 - * * ``` * *
  • ...
  • @@ -76,29 +66,21 @@ export class NgForOfContext = NgIterable> { * The context for each embedded view is logically merged to the current component * context according to its lexical position. * - * Angular 在编译模板时会自动扩展简写语法。每个嵌入式视图的上下文都会根据其词法位置在逻辑上合并到当前组件上下文。 - * * When using the shorthand syntax, Angular allows only [one structural directive - * on an element](guide/structural-directives#one-structural-directive-per-host-element). + * on an element](guide/built-in-directives#one-per-element). * If you want to iterate conditionally, for example, * put the `*ngIf` on a container element that wraps the `*ngFor` element. * For futher discussion, see - * [Structural Directives](guide/structural-directives#one-per-element). - * - * 使用简写语法时,Angular 在[一个元素上只允许有一个结构型指令](guide/structural-directives#one-structural-directive-per-host-element)。例如,如果要根据条件进行迭代,请将 `*ngIf` 放在 `*ngFor` 元素的容器元素上。欲知详情,请参见[《结构型指令》](guide/structural-directives#one-per-element) 。 + * [Structural Directives](guide/built-in-directives#one-per-element). * * @usageNotes * * ### Local variables * - * ### 局部变量 - * * `NgForOf` provides exported values that can be aliased to local variables. * For example: * - * `NgForOf` 可以为所提供的导出值指定一个局部变量别名。例如: - * - * ``` + * ``` *
  • * {{i}}/{{users.length}}. {{user}} default *
  • @@ -106,63 +88,25 @@ export class NgForOfContext = NgIterable> { * * The following exported values can be aliased to local variables: * - * `NgForOf` 导出了一系列值,可以指定别名后作为局部变量使用: - * * - `$implicit: T`: The value of the individual items in the iterable (`ngForOf`). - * - * `$implicit: T`:迭代目标(绑定到 `ngForOf`)中每个条目的值。 - * * - `ngForOf: NgIterable`: The value of the iterable expression. Useful when the expression is * more complex then a property access, for example when using the async pipe (`userStreams | * async`). - * - * `ngForOf: NgIterable`:迭代表达式的值。当表达式不局限于访问某个属性时,这会非常有用,比如在使用 `async` 管道时(`userStreams | - * async`)。 - * * - `index: number`: The index of the current item in the iterable. - * - * `index: number`:可迭代对象中当前条目的索引。 - * * - `count: number`: The length of the iterable. - * - * `count: number`:可迭代对象的长度。 - * * - `first: boolean`: True when the item is the first item in the iterable. - * - * `first: boolean`:如果当前条目是可迭代对象中的第一个条目则为 `true`。 - * * - `last: boolean`: True when the item is the last item in the iterable. - * - * `last: boolean`:如果当前条目是可迭代对象中的最后一个条目则为 `true`。 - * * - `even: boolean`: True when the item has an even index in the iterable. - * - * `even: boolean`:如果当前条目在可迭代对象中的索引号为偶数则为 `true`。 - * * - `odd: boolean`: True when the item has an odd index in the iterable. * - * `odd: boolean`:如果当前条目在可迭代对象中的索引号为奇数则为 `true`。 - * * ### Change propagation * - * ### 变更的传导机制 - * * When the contents of the iterator changes, `NgForOf` makes the corresponding changes to the DOM: * - * 当迭代器的内容变化时,`NgForOf` 会对 DOM 做出相应的修改: - * * * When an item is added, a new instance of the template is added to the DOM. - * - * 当新增条目时,就会往 DOM 中添加一个模板实例。 - * * * When an item is removed, its template instance is removed from the DOM. - * - * 当移除条目时,其对应的模板实例也会被从 DOM 中移除。 - * * * When items are reordered, their respective templates are reordered in the DOM. * - * 当条目集被重新排序时,他们对应的模板实例也会在 DOM 中重新排序。 - * * Angular uses object identity to track insertions and deletions within the iterator and reproduce * those changes in the DOM. This has important implications for animations and any stateful * controls that are present, such as `` elements that accept user input. Inserted rows can @@ -170,33 +114,18 @@ export class NgForOfContext = NgIterable> { * such as user input. * For more on animations, see [Transitions and Triggers](guide/transition-and-triggers). * - * Angular 使用对象标识符(对象引用)来跟踪迭代器中的添加和删除操作,并把它们同步到 DOM 中。 - * 这对于动画和有状态的控件(如用来接收用户输入的 `` 元素)具有重要意义。添加的行可以带着动画效果进来,删除的行也可以带着动画效果离开。 - * 而未变化的行则会保留那些尚未保存的状态,比如用户的输入。 - * * The identities of elements in the iterator can change while the data does not. * This can happen, for example, if the iterator is produced from an RPC to the server, and that * RPC is re-run. Even if the data hasn't changed, the second response produces objects with * different identities, and Angular must tear down the entire DOM and rebuild it (as if all old * elements were deleted and all new elements inserted). * - * 即使数据没有变化,迭代器中的元素标识符也可能会发生变化。比如,如果迭代器处理的目标是通过 RPC 从服务器取来的, - * 而 RPC 又重新执行了一次。那么即使数据没有变化,第二次的响应体还是会生成一些具有不同标识符的对象。Angular 将会清除整个 DOM, - * 并重建它(就仿佛把所有老的元素都删除,并插入所有新元素)。这是很昂贵的操作,应该尽力避免。 - * * To avoid this expensive operation, you can customize the default tracking algorithm. * by supplying the `trackBy` option to `NgForOf`. * `trackBy` takes a function that has two arguments: `index` and `item`. * If `trackBy` is given, Angular tracks changes by the return value of the function. * - * 要想自定义默认的跟踪算法,`NgForOf` 支持 `trackBy` 选项。 - * `trackBy` 接受一个带两个参数(`index` 和 `item`)的函数。 - * 如果给出了 `trackBy`,Angular 就会使用该函数的返回值来跟踪变化。 - * * @see [Structural Directives](guide/structural-directives) - * - * [结构型指令](guide/structural-directives) - * * @ngModule CommonModule * @publicApi */ @@ -204,10 +133,7 @@ export class NgForOfContext = NgIterable> { export class NgForOf = NgIterable> implements DoCheck { /** * The value of the iterable expression, which can be used as a - * [template input variable](guide/structural-directives#template-input-variable). - * - * 可迭代表达式的值,可以将其用作[模板输入变量](guide/structural-directives#template-input-variable)。 - * + * [template input variable](guide/structural-directives#shorthand). */ @Input() set ngForOf(ngForOf: U&NgIterable|undefined|null) { @@ -217,32 +143,23 @@ export class NgForOf = NgIterable> implements DoCh /** * A function that defines how to track changes for items in the iterable. * - * 定义如何跟踪可迭代项的更改的函数。 - * * When items are added, moved, or removed in the iterable, * the directive must re-render the appropriate DOM nodes. * To minimize churn in the DOM, only nodes that have changed * are re-rendered. * - * 在迭代器中添加、移动或删除条目时,指令必须重新渲染适当的 DOM 节点。为了最大程度地减少 DOM 中的搅动,仅重新渲染已更改的节点。 - * * By default, the change detector assumes that * the object instance identifies the node in the iterable. * When this function is supplied, the directive uses * the result of calling this function to identify the item node, * rather than the identity of the object itself. * - * 默认情况下,变更检测器假定对象实例标识可迭代对象。提供此函数后,指令将使用调用此函数的结果来标识项节点,而不是对象本身的标识。 - * * The function receives two inputs, * the iteration index and the associated node data. - * - * 该函数接收两个输入,即迭代索引和关联的节点数据。 - * */ @Input() set ngForTrackBy(fn: TrackByFunction) { - if (isDevMode() && fn != null && typeof fn !== 'function') { + if ((typeof ngDevMode === 'undefined' || ngDevMode) && fn != null && typeof fn !== 'function') { // TODO(vicb): use a log service once there is a public one available if (console && console.warn) { console.warn( @@ -269,13 +186,7 @@ export class NgForOf = NgIterable> implements DoCh /** * A reference to the template that is stamped out for each item in the iterable. - * - * 此模板引用用来为 iterable 中的生成每个条目。 - * * @see [template reference variable](guide/template-reference-variables) - * - * [模板引用变量](guide/template-reference-variables) - * */ @Input() set ngForTemplate(value: TemplateRef>) { @@ -289,9 +200,6 @@ export class NgForOf = NgIterable> implements DoCh /** * Applies the changes when needed. - * - * 要按需应用的更改。 - * */ ngDoCheck(): void { if (this._ngForOfDirty) { @@ -364,13 +272,8 @@ export class NgForOf = NgIterable> implements DoCh /** * Asserts the correct type of the context for the template that `NgForOf` will render. * - * 为 `NgForOf` 将要渲染的模板确保正确的上下文类型。 - * * The presence of this method is a signal to the Ivy template type-check compiler that the * `NgForOf` structural directive renders its template with a specific context type. - * - * 此方法的存在向 Ivy 模板类型检查编译器发出信号,即 `NgForOf` 结构型指令使用特定的上下文类型渲染其模板。 - * */ static ngTemplateContextGuard>(dir: NgForOf, ctx: any): ctx is NgForOfContext { diff --git a/packages/common/src/directives/ng_if.ts b/packages/common/src/directives/ng_if.ts index 5c88823f77..b9f22e960c 100644 --- a/packages/common/src/directives/ng_if.ts +++ b/packages/common/src/directives/ng_if.ts @@ -17,28 +17,20 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri * Angular renders the template provided in an optional `else` clause. The default * template for the `else` clause is blank. * - * 本结构型指令用于根据表达式的值(强转为 boolean)是否为真值,来有条件的包含某个模板。当表达式计算为 true 时,Angular 会渲染 `then` 子句中提供的模板,当为 false 或 null 时则渲染可选的 `else` 子句中的模板。`else` 子句的默认模板是空白模板。 - * - * A [shorthand form](guide/structural-directives#the-asterisk--prefix) of the directive, + * A [shorthand form](guide/structural-directives#asterisk) of the directive, * `*ngIf="condition"`, is generally used, provided * as an attribute of the anchor element for the inserted template. * Angular expands this into a more explicit version, in which the anchor element * is contained in an `` element. * - * 通常使用指令的[简写形式](guide/structural-directives#the-asterisk--prefix) `*ngIf="condition"`,作为插入模板的锚点元素的属性提供。Angular 将其扩展为更明确的版本,其中锚点元素包含在 `` 元素中。 - * * Simple form with shorthand syntax: * - * 具有简写语法的简单形式: - * * ``` *
    Content to render when condition is true.
    * ``` * * Simple form with expanded syntax: * - * 具有扩展语法的简单形式: - * * ``` *
    Content to render when condition is * true.
    @@ -46,8 +38,6 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri * * Form with an "else" block: * - * 带有 “else” 块的格式: - * * ``` *
    Content to render when condition is true.
    * Content to render when condition is false. @@ -55,8 +45,6 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri * * Shorthand form with "then" and "else" blocks: * - * 带 “then” 和 “else” 块的简写形式: - * * ``` *
    * Content to render when condition is true. @@ -65,8 +53,6 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri * * Form with storing the value locally: * - * 本地存储值的形式: - * * ``` *
    {{value}}
    * Content to render when value is null. @@ -78,47 +64,33 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri * as seen in the following example. * The default `else` template is blank. * - * `*ngIf` 指令通常用于根据条件显示内联模板,就像下面的例子展示的一样。默认的 `else` 模板是空白的。 - * * {@example common/ngIf/ts/module.ts region='NgIfSimple'} * * ### Showing an alternative template using `else` * - * ### 使用 `else` 显示替代模板 - * * To display a template when `expression` evaluates to false, use an `else` template * binding as shown in the following example. * The `else` binding points to an `` element labeled `#elseBlock`. * The template can be defined anywhere in the component view, but is typically placed right after * `ngIf` for readability. * - * 要在 `expression` 计算为 false 时显示一个模板,请使用如下所示的 `else` 模板绑定。`else` 绑定指向一个带有 `#elseBlock` 标签的 ``。该模板可以定义在组件视图中的任何地方,但通常放在 `ngIf` 的紧后方,以提高可读性。 - * * {@example common/ngIf/ts/module.ts region='NgIfElse'} * * ### Using an external `then` template * - * ### 使用内部 `then` 模板 - * * In the previous example, the then-clause template is specified inline, as the content of the * tag that contains the `ngIf` directive. You can also specify a template that is defined * externally, by referencing a labeled `` element. When you do this, you can * change which template to use at runtime, as shown in the following example. * - * 在前面的例子中,then 子句的模板是内联的,也就是作为 `ngIf` 指令所在标签的内容。你还可以通过引用一个带标签的 `` 元素来指定一个在外部定义的模板。这样就可以让你在运行时更改模板,就像下面的例子所演示的。 - * * {@example common/ngIf/ts/module.ts region='NgIfThenElse'} * * ### Storing a conditional result in a variable * - * ### 把条件结果保存在变量中 - * * You might want to show a set of properties from the same object. If you are waiting * for asynchronous data, the object can be undefined. * In this case, you can use `ngIf` and store the result of the condition in a local - * variable as shown in the the following example. - * - * 比如你想显示同一个对象中的一组属性。如果你在等待异步数据,此对象可能是未定义的。这时候,你可以使用 `ngIf`,并且把此条件结果保存在一个局部变量中,如下例所示。 + * variable as shown in the following example. * * {@example common/ngIf/ts/module.ts region='NgIfAs'} * @@ -126,26 +98,18 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri * The conditional statement stores the result of `userStream|async` in the local variable `user`. * You can then bind the local `user` repeatedly. * - * 这段代码只使用了一个 `AsyncPipe`,所以只会创建一个订阅。此条件表达式把 `userStream|async` 的结果保存在局部变量 `user` 中。然后你就可以反复绑定这个局部变量 `user` 了。 - * * The conditional displays the data only if `userStream` returns a value, * so you don't need to use the * safe-navigation-operator (`?.`) * to guard against null values when accessing properties. * You can display an alternative template while waiting for the data. * - * 只有当 `userStream` 返回了值的时候,才会有条件的显示此数据。所以你不用使用安全导航操作符 (`?.`) 来在访问属性时避免空值。你可以在等待数据时显示一个备用模板。 - * * ### Shorthand syntax * - * ### 简写语法 - * * The shorthand syntax `*ngIf` expands into two separate template specifications * for the "then" and "else" clauses. For example, consider the following shorthand statement, * that is meant to show a loading page while waiting for data to be loaded. * - * `*ngIf` 的简写语法会把 "then" 和 "else" 子句分别扩展成两个独立的模板。比如,考虑下列简写语句,它要在等待数据加载期间显示一个加载中页面。 - * * ``` *
    * ... @@ -160,15 +124,11 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri * with the `#loading` label, and the template for the "then" clause * is provided as the content of the anchor element. * - * 你可以看到,"else" 子句引用了带有 `#loading` 标签的 ``,而 "then" 子句的模板是作为宿主元素的内容提供的。 - * * However, when Angular expands the shorthand syntax, it creates * another `` tag, with `ngIf` and `ngIfElse` directives. * The anchor element containing the template for the "then" clause becomes * the content of this unlabeled `` tag. * - * 不过,当 Angular 扩展此简写语法的时候,它创建了另一个带有 `ngIf` 和 `ngIfElse` 指令的 ``。此宿主元素包含的 "then" 子句的模板变成了无标签的 `` 的内容。 - * * ``` * *
    @@ -183,9 +143,8 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri * * The presence of the implicit template object has implications for the nesting of * structural directives. For more on this subject, see - * [Structural Directives](https://angular.io/guide/structural-directives#one-per-element). + * [Structural Directives](https://angular.io/guide/built-in-directives#one-per-element). * - * 隐式模板对象的存在,影响了结构型指令的嵌套规则。欲知详情,参见[结构型指令](https://angular.io/guide/structural-directives#one-per-element)。 * @ngModule CommonModule * @publicApi */ @@ -203,9 +162,6 @@ export class NgIf { /** * The Boolean expression to evaluate as the condition for showing a template. - * - * 布尔表达式,将其作为显示模板的条件进行计算。 - * */ @Input() set ngIf(condition: T) { @@ -215,9 +171,6 @@ export class NgIf { /** * A template to show if the condition expression evaluates to true. - * - * 当此条件表达式计算为 true 时要显示的模板。 - * */ @Input() set ngIfThen(templateRef: TemplateRef>|null) { @@ -229,9 +182,6 @@ export class NgIf { /** * A template to show if the condition expression evaluates to false. - * - * 当此条件表达式计算为 false 时要显示的模板。 - * */ @Input() set ngIfElse(templateRef: TemplateRef>|null) { @@ -269,28 +219,18 @@ export class NgIf { /** * Assert the correct type of the expression bound to the `ngIf` input within the template. * - * 为绑定到 `ngIf` 输入属性上的模板确保正确的类型。 - * * The presence of this static field is a signal to the Ivy template type check compiler that * when the `NgIf` structural directive renders its template, the type of the expression bound * to `ngIf` should be narrowed in some way. For `NgIf`, the binding expression itself is used to * narrow its type, which allows the strictNullChecks feature of TypeScript to work with `NgIf`. - * - * 该静态字段的存在向 Ivy 模板类型检查编译器发出信号,即当 `NgIf` 结构化指令渲染其模板时,应以某种方式窄化 `ngIf`。对于 `NgIf`,绑定表达式本身用于窄化其类型,这允许 TypeScript 的 strictNullChecks 功能与 `NgIf` 一起使用。 - * */ static ngTemplateGuard_ngIf: 'binding'; /** * Asserts the correct type of the context for the template that `NgIf` will render. * - * 为 `NgIf` 将要渲染的模板确保正确的上下文类型。 - * * The presence of this method is a signal to the Ivy template type-check compiler that the * `NgIf` structural directive renders its template with a specific context type. - * - * 该方法用于向 Ivy 模板类型检查编译器发出信号,即 `NgIf` 结构化指令会使用特定的上下文类型渲染其模板。 - * */ static ngTemplateContextGuard(dir: NgIf, ctx: any): ctx is NgIfContext> { diff --git a/packages/common/src/directives/ng_plural.ts b/packages/common/src/directives/ng_plural.ts index 86be29f7eb..fbd8244724 100644 --- a/packages/common/src/directives/ng_plural.ts +++ b/packages/common/src/directives/ng_plural.ts @@ -17,7 +17,6 @@ import {SwitchView} from './ng_switch'; * @ngModule CommonModule * * @usageNotes - * * ``` * * there is nothing @@ -30,33 +29,19 @@ import {SwitchView} from './ng_switch'; * * Adds / removes DOM sub-trees based on a numeric value. Tailored for pluralization. * - * 根据数字值添加/删除 DOM 子树。为支持复数词量身定制。 - * * Displays DOM sub-trees that match the switch expression value, or failing that, DOM sub-trees * that match the switch expression's pluralization category. * - * 显示与开关表达式值匹配的 DOM 子树,否则显示与开关表达式的复数类别匹配的 DOM 子树。 - * * To use this directive you must provide a container element that sets the `[ngPlural]` attribute * to a switch expression. Inner elements with a `[ngPluralCase]` will display based on their * expression: - * - * 要使用此指令,必须提供一个容器元素,该元素将 `[ngPlural]` 属性设置为 switch 表达式。 `[ngPluralCase]` 内部元素将根据其表达式显示: - * * - if `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value * matches the switch expression exactly, - * - * 如果 `[ngPluralCase]` 设置为以 `=` 开头的值,则仅在该值与 switch 表达式完全匹配时才会显示, - * * - otherwise, the view will be treated as a "category match", and will only display if exact * value matches aren't found and the value maps to its category for the defined locale. * - * 否则,该视图将被视为“类别匹配”,并且仅在未找到精确值匹配且该值映射到已定义语言环境的类别时才会显示。 - * * See http://cldr.unicode.org/index/cldr-spec/plural-rules * - * 参见 - * * @publicApi */ @Directive({selector: '[ngPlural]'}) @@ -107,10 +92,7 @@ export class NgPlural { * Creates a view that will be added/removed from the parent {@link NgPlural} when the * given expression matches the plural expression according to CLDR rules. * - * 创建一个视图,当给定表达式根据 CLDR 规则与复数表达式匹配时,将在父视图 {@link NgPlural} 中添加/删除该视图。 - * * @usageNotes - * * ``` * * ... @@ -120,8 +102,6 @@ export class NgPlural { * * See {@link NgPlural} for more details and example. * - * 参见 {@link NgPlural} 以了解详情和范例。 - * * @publicApi */ @Directive({selector: '[ngPluralCase]'}) diff --git a/packages/common/src/directives/ng_style.ts b/packages/common/src/directives/ng_style.ts index e36415069c..1252ee1628 100644 --- a/packages/common/src/directives/ng_style.ts +++ b/packages/common/src/directives/ng_style.ts @@ -15,24 +15,18 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChanges, KeyValueDiffer, * * Set the font of the containing element to the result of an expression. * - * 将容器元素的字体设置为表达式的结果。 - * * ``` * ... * ``` * * Set the width of the containing element to a pixel value returned by an expression. * - * 将容器元素的宽度设置为表达式返回的像素值。 - * * ``` * ... * ``` * * Set a collection of style values using an expression that returns key-value pairs. * - * 使用返回键值对的表达式来设置样式值的集合。 - * * ``` * ... * ``` @@ -48,8 +42,6 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChanges, KeyValueDiffer, * is assigned to the given style property. * If the result of evaluation is null, the corresponding style is removed. * - * 一个属性指令,用于更新容器元素的样式。可以通过指定用冒号分隔的键值对来设置一个或多个样式属性。其键是样式名称,带有可选的 `` 后缀(比如 'top.px','font-style.em');其值是待求值的表达式。如果求值结果不是 null,则把用指定单位表示的结果赋值给指定的样式属性;如果是 null,则删除相应的样式。 - * * @publicApi */ @Directive({selector: '[ngStyle]'}) diff --git a/packages/common/src/directives/ng_switch.ts b/packages/common/src/directives/ng_switch.ts index 6f36836693..5847340383 100644 --- a/packages/common/src/directives/ng_switch.ts +++ b/packages/common/src/directives/ng_switch.ts @@ -39,29 +39,15 @@ export class SwitchView { * @description * The `[ngSwitch]` directive on a container specifies an expression to match against. * The expressions to match are provided by `ngSwitchCase` directives on views within the container. - * - * 容器上的 `[ngSwitch]` 指令指定要匹配的表达式。匹配的表达式由容器内视图上的 `ngSwitchCase` 指令提供。 - * * - Every view that matches is rendered. - * - * 如果有匹配项,则渲染匹配的视图。 - * * - If there are no matches, a view with the `ngSwitchDefault` directive is rendered. - * - * 如果没有匹配项,则渲染 `ngSwitchDefault` - * * - Elements within the `[NgSwitch]` statement but outside of any `NgSwitchCase` * or `ngSwitchDefault` directive are preserved at the location. * - * `[NgSwitch]` 语句内任何除 `NgSwitchCase` 或 `ngSwitchDefault` 指令之外的元素都保留在原位。 - * * @usageNotes - * * Define a container element for the directive, and specify the switch expression * to match against as an attribute: * - * 为指令定义一个容器元素,并指定要匹配的 switch 表达式作为属性: - * * ``` * * ``` @@ -69,8 +55,6 @@ export class SwitchView { * Within the container, `*ngSwitchCase` statements specify the match expressions * as attributes. Include `*ngSwitchDefault` as the final case. * - * 在容器内, `*ngSwitchCase` 语句将匹配表达式指定为属性。包括用 `*ngSwitchDefault` 作为最后一种情况。 - * * ``` * * ... @@ -81,12 +65,8 @@ export class SwitchView { * * ### Usage Examples * - * ### 使用范例 - * * The following example shows how to use more than one case to display the same view: * - * 下面的示例演示如何使用多个分支来显示同一视图: - * * ``` * * @@ -99,9 +79,6 @@ export class SwitchView { * ``` * * The following example shows how cases can be nested: - * - * 以下示例演示如何嵌套案例: - * * ``` * * ... @@ -121,7 +98,6 @@ export class SwitchView { * @see `NgSwitchDefault` * @see [Structural Directives](guide/structural-directives) * - * [结构型指令](guide/structural-directives) */ @Directive({selector: '[ngSwitch]'}) export class NgSwitch { @@ -186,15 +162,11 @@ export class NgSwitch { * When the expressions match, the given `NgSwitchCase` template is rendered. * If multiple match expressions match the switch expression value, all of them are displayed. * - * 提供一个 switch case 表达式来匹配一个封闭的 `ngSwitch` 表达式。当表达式匹配时,将渲染给定的 `NgSwitchCase` 模板。如果多个匹配表达式与开关表达式值相匹配,则会全部显示。 - * * @usageNotes * * Within a switch container, `*ngSwitchCase` statements specify the match expressions * as attributes. Include `*ngSwitchDefault` as the final case. * - * 在开关容器中, `*ngSwitchCase` 语句将匹配表达式指定为属性。包括用 `*ngSwitchDefault` 作为最后一种情况。 - * * ``` * * ... @@ -207,13 +179,9 @@ export class NgSwitch { * that defines the subtree to be selected if the value of the match expression * matches the value of the switch expression. * - * 每个 switch-case 语句包含一个内联 HTML 模板或模板引用,该模板或模板引用定义了 match 表达式的值与 switch 表达式的值匹配时要选择的子树。 - * * Unlike JavaScript, which uses strict equality, Angular uses loose equality. * This means that the empty string, `""` matches 0. * - * 与 JavaScript 使用严格相等性的方式不同,Angular 使用宽松相等性。这意味着空字符串 `""` 能匹配 0。 - * * @publicApi * @see `NgSwitch` * @see `NgSwitchDefault` @@ -224,9 +192,6 @@ export class NgSwitchCase implements DoCheck { private _view: SwitchView; /** * Stores the HTML template to be selected on match. - * - * 存储要在匹配时选择的 HTML 模板。 - * */ @Input() ngSwitchCase: any; @@ -239,9 +204,6 @@ export class NgSwitchCase implements DoCheck { /** * Performs case matching. For internal use only. - * - * 执行大小写匹配。仅限内部使用。 - * */ ngDoCheck() { this._view.enforceState(this.ngSwitch._matchCase(this.ngSwitchCase)); @@ -257,8 +219,6 @@ export class NgSwitchCase implements DoCheck { * match the `NgSwitch` expression. * This statement should be the final case in an `NgSwitch`. * - * 创建一个当没有任何 `NgSwitchCase` 表达式能匹配 `NgSwitch` 表达时要渲染的视图。该语句应该是 `NgSwitch` 的最后一种情况。 - * * @publicApi * @see `NgSwitch` * @see `NgSwitchCase` diff --git a/packages/common/src/directives/ng_template_outlet.ts b/packages/common/src/directives/ng_template_outlet.ts index 7bcdb401f2..4ddb84391b 100644 --- a/packages/common/src/directives/ng_template_outlet.ts +++ b/packages/common/src/directives/ng_template_outlet.ts @@ -15,29 +15,19 @@ import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChange, SimpleChange * * Inserts an embedded view from a prepared `TemplateRef`. * - * 根据一个提前备好的 `TemplateRef` 插入一个内嵌视图。 - * * You can attach a context object to the `EmbeddedViewRef` by setting `[ngTemplateOutletContext]`. * `[ngTemplateOutletContext]` should be an object, the object's keys will be available for binding * by the local template `let` declarations. * - * 你可以通过设置 `[ngTemplateOutletContext]` 来给 `EmbeddedViewRef` 附加一个上下文对象。 - * `[ngTemplateOutletContext]` 是一个对象,该对象的 key 可在模板中使用 `let` 语句进行绑定。 - * * @usageNotes - * * ``` * * ``` * * Using the key `$implicit` in the context object will set its value as default. * - * 在上下文对象中使用 `$implicit` 这个 key 会把对应的值设置为默认值。 - * * ### Example * - * ### 例子 - * * {@example common/ngTemplateOutlet/ts/module.ts region='NgTemplateOutlet'} * * @publicApi @@ -51,26 +41,18 @@ export class NgTemplateOutlet implements OnChanges { * object, the object's keys will be available for binding by the local template `let` * declarations. * Using the key `$implicit` in the context object will set its value as default. - * - * 附加到 {@link EmbeddedViewRef} 的上下文对象。这应该是一个对象,该对象的键名将可以在局部模板中使用 `let` 声明中进行绑定。在上下文对象中使用 `$implicit` 为键名时,将把它作为默认值。 - * */ @Input() public ngTemplateOutletContext: Object|null = null; /** * A string defining the template reference and optionally the context object for the template. - * - * 一个字符串,用于定义模板引用以及模板的上下文对象。 - * */ @Input() public ngTemplateOutlet: TemplateRef|null = null; constructor(private _viewContainerRef: ViewContainerRef) {} ngOnChanges(changes: SimpleChanges) { - const recreateView = this._shouldRecreateView(changes); - - if (recreateView) { + if (changes['ngTemplateOutlet']) { const viewContainerRef = this._viewContainerRef; if (this._viewRef) { @@ -80,55 +62,9 @@ export class NgTemplateOutlet implements OnChanges { this._viewRef = this.ngTemplateOutlet ? viewContainerRef.createEmbeddedView(this.ngTemplateOutlet, this.ngTemplateOutletContext) : null; - } else if (this._viewRef && this.ngTemplateOutletContext) { - this._updateExistingContext(this.ngTemplateOutletContext); - } - } - - /** - * We need to re-create existing embedded view if: - * - * 在下列情况下,我们要重新创建现存的内嵌视图: - * - * - templateRef has changed - * - * templateRef 变化了 - * - * - context has changes - * - * context 发生了变化 - * - * We mark context object as changed when the corresponding object - * shape changes (new properties are added or existing properties are removed). - * In other words we consider context with the same properties as "the same" even - * if object reference changes (see https://github.com/angular/angular/issues/13407). - * - * 当相应的对象的形态(而不是值)发生变化时,我们就会把上下文对象标记为已更改(添加了新的属性或移除了现有属性)。 - * 换句话说,即使对象的引用发生了变化,我们也会把具有相同属性的上下文对象视作 "相同的"(参见 )。 - */ - private _shouldRecreateView(changes: SimpleChanges): boolean { - const ctxChange = changes['ngTemplateOutletContext']; - return !!changes['ngTemplateOutlet'] || (ctxChange && this._hasContextShapeChanged(ctxChange)); - } - - private _hasContextShapeChanged(ctxChange: SimpleChange): boolean { - const prevCtxKeys = Object.keys(ctxChange.previousValue || {}); - const currCtxKeys = Object.keys(ctxChange.currentValue || {}); - - if (prevCtxKeys.length === currCtxKeys.length) { - for (let propName of currCtxKeys) { - if (prevCtxKeys.indexOf(propName) === -1) { - return true; - } - } - return false; - } - return true; - } - - private _updateExistingContext(ctx: Object): void { - for (let propName of Object.keys(ctx)) { - (this._viewRef!.context)[propName] = (this.ngTemplateOutletContext)[propName]; + } else if ( + this._viewRef && changes['ngTemplateOutletContext'] && this.ngTemplateOutletContext) { + this._viewRef.context = this.ngTemplateOutletContext; } } } diff --git a/packages/common/src/dom_adapter.ts b/packages/common/src/dom_adapter.ts index 0e2d832dbe..3b71475f85 100644 --- a/packages/common/src/dom_adapter.ts +++ b/packages/common/src/dom_adapter.ts @@ -31,16 +31,11 @@ export function setRootDomAdapter(adapter: DomAdapter) { */ export abstract class DomAdapter { // Needs Domino-friendly test utility - abstract getProperty(el: Element, name: string): any; abstract dispatchEvent(el: any, evt: any): any; - - // Used by router - abstract log(error: any): any; - abstract logGroup(error: any): any; - abstract logGroupEnd(): any; + abstract readonly supportsDOMEvents: boolean; // Used by Meta - abstract remove(el: any): Node; + abstract remove(el: any): void; abstract createElement(tagName: any, doc?: any): HTMLElement; abstract createHtmlDocument(): HTMLDocument; abstract getDefaultDocument(): Document; @@ -53,25 +48,17 @@ export abstract class DomAdapter { // Used by KeyEventsPlugin abstract onAndCancel(el: any, evt: any, listener: any): Function; - abstract supportsDOMEvents(): boolean; // Used by PlatformLocation and ServerEventManagerPlugin abstract getGlobalEventTarget(doc: Document, target: string): any; // Used by PlatformLocation - abstract getHistory(): History; - abstract getLocation(): - any; /** This is the ambient Location definition, NOT Location from @angular/common. */ abstract getBaseHref(doc: Document): string|null; abstract resetBaseElement(): void; // TODO: remove dependency in DefaultValueAccessor abstract getUserAgent(): string; - // Used by AngularProfiler - abstract performanceNow(): number; - - // Used by CookieXSRFStrategy - abstract supportsCookies(): boolean; + // Used in the legacy @angular/http package which has some usage in g3. abstract getCookie(name: string): string|null; } diff --git a/packages/common/src/dom_tokens.ts b/packages/common/src/dom_tokens.ts index 9bd13647a6..e7c8d9fc30 100644 --- a/packages/common/src/dom_tokens.ts +++ b/packages/common/src/dom_tokens.ts @@ -11,13 +11,9 @@ import {InjectionToken} from '@angular/core'; /** * A DI Token representing the main rendering context. In a browser this is the DOM Document. * - * 表示主要渲染上下文的 DI 令牌。在浏览器中,这是 DOM 文档。 - * * Note: Document might not be available in the Application Context when Application and Rendering * Contexts are not the same (e.g. when running the application in a Web Worker). * - * 注意:当应用程序上下文和渲染上下文不同时(例如,在 Web Worker 中运行应用程序时),document 可能在应用程序上下文中不可用。 - * * @publicApi */ export const DOCUMENT = new InjectionToken('DocumentToken'); diff --git a/packages/common/src/i18n/format_date.ts b/packages/common/src/i18n/format_date.ts index c221ccf309..75e94af556 100644 --- a/packages/common/src/i18n/format_date.ts +++ b/packages/common/src/i18n/format_date.ts @@ -13,7 +13,7 @@ export const ISO8601_DATE_REGEX = // 1 2 3 4 5 6 7 8 9 10 11 const NAMED_FORMATS: {[localeId: string]: {[format: string]: string}} = {}; const DATE_FORMATS_SPLIT = - /((?:[^GyYMLwWdEabBhHmsSzZO']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|Y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/; + /((?:[^BEGHLMOSWYZabcdhmswyz']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|Y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|c{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/; enum ZoneWidth { Short, @@ -46,37 +46,19 @@ enum TranslationType { * * Formats a date according to locale rules. * - * 基于区域规则格式化日期。 - * * @param value The date to format, as a Date, or a number (milliseconds since UTC epoch) * or an [ISO date-time string](https://www.w3.org/TR/NOTE-datetime). - * - * 要格式化的日期,是一个日期、数字(从 UTC 时代以来的毫秒数)或 ISO 字符串 (https://www.w3.org/TR/NOTE-datetime)。 - * * @param format The date-time components to include. See `DatePipe` for details. - * - * 表示要包含的日期/时间部件。欲知详情,参见 `DatePipe`。 - * * @param locale A locale code for the locale format rules to use. - * - * 一个区域代码,用来表示要使用的区域格式规则。 - * * @param timezone The time zone. A time zone offset from GMT (such as `'+0430'`), * or a standard UTC/GMT or continental US time zone abbreviation. * If not specified, uses host system settings. * - * 时区。可以是 GMT 中的时区偏移(如 `'+0430'`),或一个标准的 UTC/GMT 或美国大陆时区的缩写。 - * 如果没有指定,就会使用宿主系统中的设定。 - * * @returns The formatted date string. * - * 格式化之后的日期字符串。 - * * @see `DatePipe` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化 (i18n) 指南](https://angular.cn/guide/i18n) - * * @publicApi */ export function formatDate( @@ -119,6 +101,38 @@ export function formatDate( return text; } +/** + * Create a new Date object with the given date value, and the time set to midnight. + * + * We cannot use `new Date(year, month, date)` because it maps years between 0 and 99 to 1900-1999. + * See: https://github.com/angular/angular/issues/40377 + * + * Note that this function returns a Date object whose time is midnight in the current locale's + * timezone. In the future we might want to change this to be midnight in UTC, but this would be a + * considerable breaking change. + */ +function createDate(year: number, month: number, date: number): Date { + // The `newDate` is set to midnight (UTC) on January 1st 1970. + // - In PST this will be December 31st 1969 at 4pm. + // - In GMT this will be January 1st 1970 at 1am. + // Note that they even have different years, dates and months! + const newDate = new Date(0); + + // `setFullYear()` allows years like 0001 to be set correctly. This function does not + // change the internal time of the date. + // Consider calling `setFullYear(2019, 8, 20)` (September 20, 2019). + // - In PST this will now be September 20, 2019 at 4pm + // - In GMT this will now be September 20, 2019 at 1am + + newDate.setFullYear(year, month, date); + // We want the final date to be at local midnight, so we reset the time. + // - In PST this will now be September 20, 2019 at 12am + // - In GMT this will now be September 20, 2019 at 12am + newDate.setHours(0, 0, 0); + + return newDate; +} + function getNamedFormat(locale: string, format: string): string { const localeId = getLocaleId(locale); NAMED_FORMATS[localeId] = NAMED_FORMATS[localeId] || {}; @@ -221,9 +235,6 @@ function formatFractionalSeconds(milliseconds: number, digits: number): string { /** * Returns a date formatter that transforms a date into its locale digit representation - * - * 返回一个日期格式化器,它负责把日期转换成它的本地数字表示法 - * */ function dateGetter( name: DateType, size: number, offset: number = 0, trim = false, @@ -272,9 +283,6 @@ function getDatePart(part: DateType, date: Date): number { /** * Returns a date formatter that transforms a date into its locale string representation - * - * 返回一个日期格式化器,它负责把日期转换成它的本地字符串表示法 - * */ function dateStrGetter( name: TranslationType, width: TranslationWidth, form: FormStyle = FormStyle.Format, @@ -286,8 +294,6 @@ function dateStrGetter( /** * Returns the locale translation of a date for a given form, type and width - * - * 返回一个日期的指定格式、类型和宽度的本地化格式 */ function getDateTranslation( date: Date, locale: string, name: TranslationType, width: TranslationWidth, form: FormStyle, @@ -357,10 +363,6 @@ function getDateTranslation( * Returns a date formatter that transforms a date and an offset into a timezone with ISO8601 or * GMT format depending on the width (eg: short = +0430, short:GMT = GMT+4, long = GMT+04:30, * extended = +04:30) - * - * 返回一个日期格式化器,它会根据宽度把日期和偏移转换成 ISO8601 或 GMT 格式的时区 - * (如 short = +0430, short:GMT = GMT+4, long = GMT+04:30, - * extended = +04:30 )。 */ function timeZoneGetter(width: ZoneWidth): DateFormatter { return function(date: Date, locale: string, offset: number) { @@ -392,13 +394,13 @@ function timeZoneGetter(width: ZoneWidth): DateFormatter { const JANUARY = 0; const THURSDAY = 4; function getFirstThursdayOfYear(year: number) { - const firstDayOfYear = (new Date(year, JANUARY, 1)).getDay(); - return new Date( + const firstDayOfYear = createDate(year, JANUARY, 1).getDay(); + return createDate( year, 0, 1 + ((firstDayOfYear <= THURSDAY) ? THURSDAY : THURSDAY + 7) - firstDayOfYear); } function getThursdayThisWeek(datetime: Date) { - return new Date( + return createDate( datetime.getFullYear(), datetime.getMonth(), datetime.getDate() + (THURSDAY - datetime.getDay())); } @@ -443,7 +445,7 @@ const DATE_FORMATS: {[format: string]: DateFormatter} = {}; // Based on CLDR formats: // See complete list: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table // See also explanations: http://cldr.unicode.org/translation/date-time -// TODO(ocombe): support all missing cldr formats: Y, U, Q, D, F, e, c, j, J, C, A, v, V, X, x +// TODO(ocombe): support all missing cldr formats: U, Q, D, F, e, j, J, C, A, v, V, X, x function getDateFormatter(format: string): DateFormatter|null { if (DATE_FORMATS[format]) { return DATE_FORMATS[format]; @@ -555,6 +557,26 @@ function getDateFormatter(format: string): DateFormatter|null { formatter = dateGetter(DateType.Date, 2); break; + // Day of the Week StandAlone (1, 1, Mon, Monday, M, Mo) + case 'c': + case 'cc': + formatter = dateGetter(DateType.Day, 1); + break; + case 'ccc': + formatter = + dateStrGetter(TranslationType.Days, TranslationWidth.Abbreviated, FormStyle.Standalone); + break; + case 'cccc': + formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Wide, FormStyle.Standalone); + break; + case 'ccccc': + formatter = + dateStrGetter(TranslationType.Days, TranslationWidth.Narrow, FormStyle.Standalone); + break; + case 'cccccc': + formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Short, FormStyle.Standalone); + break; + // Day of the Week case 'E': case 'EE': @@ -720,29 +742,14 @@ function convertTimezoneToLocal(date: Date, timezone: string, reverse: boolean): /** * Converts a value to date. * - * 把值转换为日期。 - * * Supported input formats: - * - * 支持的输入格式: - * * - `Date` * - number: timestamp - * - * 数字:时间戳 - * * - string: numeric (e.g. "1234"), ISO and date strings in a format supported by * [Date.parse()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse). * Note: ISO strings without time return a date without timeoffset. * - * 字符串:数字(如 "1234")、ISO 格式和 [Date.parse()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) - * 所支持的日期字符串格式。 - * 注意:不带时间的 ISO 字符串会返回一个没有时区偏移量的日期。 - * * Throws if unable to convert to a date. - * - * 如果不能转换成日期,则抛出异常。 - * */ export function toDate(value: string|number|Date): Date { if (isDate(value)) { @@ -756,14 +763,7 @@ export function toDate(value: string|number|Date): Date { if (typeof value === 'string') { value = value.trim(); - const parsedNb = parseFloat(value); - - // any string that only contains numbers, like "1234" but not like "1234hello" - if (!isNaN(value as any - parsedNb)) { - return new Date(parsedNb); - } - - if (/^(\d{4}-\d{1,2}-\d{1,2})$/.test(value)) { + if (/^(\d{4}(-\d{1,2}(-\d{1,2})?)?)$/.test(value)) { /* For ISO Strings without time the day, month and year must be extracted from the ISO String before Date creation to avoid time offset and errors in the new Date. If we only replace '-' with ',' in the ISO String ("2015,01,01"), and try to create a new @@ -771,8 +771,15 @@ export function toDate(value: string|number|Date): Date { If we leave the '-' ("2015-01-01") and try to create a new Date("2015-01-01") the timeoffset is applied. Note: ISO months are 0 for January, 1 for February, ... */ - const [y, m, d] = value.split('-').map((val: string) => +val); - return new Date(y, m - 1, d); + const [y, m = 1, d = 1] = value.split('-').map((val: string) => +val); + return createDate(y, m - 1, d); + } + + const parsedNb = parseFloat(value); + + // any string that only contains numbers, like "1234" but not like "1234hello" + if (!isNaN(value as any - parsedNb)) { + return new Date(parsedNb); } let match: RegExpMatchArray|null; @@ -791,9 +798,6 @@ export function toDate(value: string|number|Date): Date { /** * Converts a date in ISO8601 to a Date. * Used instead of `Date.parse` because of browser discrepancies. - * - * 把 ISO8601 格式的字符串转换成 `Date` 对象。 - * 由于浏览器的差异而不能使用 `Date.parse`。 */ export function isoStringToDate(match: RegExpMatchArray): Date { const date = new Date(0); diff --git a/packages/common/src/i18n/format_number.ts b/packages/common/src/i18n/format_number.ts index ea6691e6f4..abea0626f6 100644 --- a/packages/common/src/i18n/format_number.ts +++ b/packages/common/src/i18n/format_number.ts @@ -128,43 +128,23 @@ function formatNumberToLocaleString( * * Formats a number as currency using locale rules. * - * 使用区域设置规则将数字格式化为货币。 - * * @param value The number to format. - * - * 要格式化的数字。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @param currency A string containing the currency symbol or its name, * such as "$" or "Canadian Dollar". Used in output string, but does not affect the operation * of the function. - * - * 包含货币符号或其名称的字符串,例如 “$” 或 “加元”。在输出字符串中使用,但不影响该函数的操作。 - * * @param currencyCode The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) * currency code, such as `USD` for the US dollar and `EUR` for the euro. * Used to determine the number of digits in the decimal part. - * - * [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) 货币代码,例如 `USD` 表示美元,`EUR` 表示欧元。用于确定小数部分的位数。 - * - * @param digitInfo Decimal representation options, specified by a string in the following format: + * @param digitsInfo Decimal representation options, specified by a string in the following format: * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details. * - * 十进制表示形式的选项,通过字符串以如下格式指定:`{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}` 。欲知详情,请参见 `DecimalPipe`。 - * * @returns The formatted currency value. * - * 要格式化的货币值。 - * * @see `formatNumber()` * @see `DecimalPipe` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function formatCurrency( @@ -195,31 +175,16 @@ export function formatCurrency( * * Formats a number as a percentage according to locale rules. * - * 根据语言环境规则将数字格式化为百分比。 - * * @param value The number to format. - * - * 要格式化的数字。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * - * @param digitInfo Decimal representation options, specified by a string in the following format: + * @param digitsInfo Decimal representation options, specified by a string in the following format: * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details. * - * 十进制表示形式的选项,通过字符串以如下格式指定:`{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`。欲知详情,请参见 `DecimalPipe`。 - * * @returns The formatted percentage value. * - * 已格式化的百分比值。 - * * @see `formatNumber()` * @see `DecimalPipe` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) - * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi * */ @@ -239,29 +204,14 @@ export function formatPercent(value: number, locale: string, digitsInfo?: string * Formats a number as text, with group sizing, separator, and other * parameters based on the locale. * - * 将数字格式化为文本格式,并根据区域来设置组大小、分隔符和其他参数。 - * * @param value The number to format. - * - * 要格式化的数字。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * - * @param digitInfo Decimal representation options, specified by a string in the following format: + * @param digitsInfo Decimal representation options, specified by a string in the following format: * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details. * - * 十进制表示形式的选项,通过字符串以如下格式指定:`{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}` 。欲知详情,请参见 `DecimalPipe`。 - * * @returns The formatted text string. - * - * 已格式化的文本字符串。 - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function formatNumber(value: number, locale: string, digitsInfo?: string): string { diff --git a/packages/common/src/i18n/locale_data.ts b/packages/common/src/i18n/locale_data.ts index 44c90219fb..ace7b33d0a 100644 --- a/packages/common/src/i18n/locale_data.ts +++ b/packages/common/src/i18n/locale_data.ts @@ -12,12 +12,8 @@ import {ɵregisterLocaleData} from '@angular/core'; * Register global data to be used internally by Angular. See the * ["I18n guide"](guide/i18n#i18n-pipes) to know how to import additional locale data. * - * 注册全局数据以供 Angular 内部使用。请参阅 [“I18n 指南”](guide/i18n#i18n-pipes)以了解如何导入其他语言环境的数据。 - * * The signature registerLocaleData(data: any, extraData?: any) is deprecated since v5.1 * - * 从 v5.1 开始不推荐使用 registerLocaleData(data:any,extraData ?: any)签名 - * * @publicApi */ export function registerLocaleData(data: any, localeId?: string|any, extraData?: any): void { diff --git a/packages/common/src/i18n/locale_data_api.ts b/packages/common/src/i18n/locale_data_api.ts index 3aa93de47e..82abc6ece4 100644 --- a/packages/common/src/i18n/locale_data_api.ts +++ b/packages/common/src/i18n/locale_data_api.ts @@ -13,15 +13,9 @@ import {CURRENCIES_EN, CurrenciesSymbols} from './currencies'; /** * Format styles that can be used to represent numbers. - * - * 可用来表示数字的格式化样式。 - * * @see `getLocaleNumberFormat()`. - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export enum NumberFormatStyle { @@ -34,15 +28,10 @@ export enum NumberFormatStyle { /** * Plurality cases used for translating plurals to different languages. * - * 用于将复数形式转换为不同语言的复数形式。 - * * @see `NgPlural` - * * @see `NgPluralCase` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export enum Plural { @@ -58,17 +47,9 @@ export enum Plural { * Context-dependant translation forms for strings. * Typically the standalone version is for the nominative form of the word, * and the format version is used for the genitive case. - * - * 字符串的上下文相关翻译形式。通常,独立版本用于单词的主格形式,格式化的版本则用于所有格。 - * * @see [CLDR website](http://cldr.unicode.org/translation/date-time-1/date-time#TOC-Standalone-vs.-Format-Styles) - * - * [CLDR 网站](http://cldr.unicode.org/translation/date-time-1/date-time#TOC-Standalone-vs.-Format-Styles) - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export enum FormStyle { @@ -81,38 +62,16 @@ export enum FormStyle { * The specific character widths are locale-specific. * Examples are given for the word "Sunday" in English. * - * 字符串宽度可用于翻译。特定的字符宽度是特定于语言环境的。这里给出了英语中 “Sunday” 一词的示例。 - * * @publicApi */ export enum TranslationWidth { - /** - * 1 character for `en-US`. For example: 'S' - * - * 对 `en-US` 是 1 字符。比如:'S' - * - */ + /** 1 character for `en-US`. For example: 'S' */ Narrow, - /** - * 3 characters for `en-US`. For example: 'Sun' - * - * 对 `en-US` 是 3 字符。比如:'Sun' - * - */ + /** 3 characters for `en-US`. For example: 'Sun' */ Abbreviated, - /** - * Full length for `en-US`. For example: "Sunday" - * - * 对 `en-US` 是全长。例如:“星期日” - * - */ + /** Full length for `en-US`. For example: "Sunday" */ Wide, - /** - * 2 characters for `en-US`, For example: "Su" - * - * 对 `en-US` 是 2 个字符,例如:“Su” - * - */ + /** 2 characters for `en-US`, For example: "Su" */ Short } @@ -121,48 +80,31 @@ export enum TranslationWidth { * The specific character widths are locale-specific. * Examples are given for `en-US`. * - * 可用于日期时间格式的字符串宽度。特定的字符宽度是特定于语言环境的。示例中是给 `en-US` 的示例。 - * * @see `getLocaleDateFormat()` * @see `getLocaleTimeFormat()`` * @see `getLocaleDateTimeFormat()` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) - * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export enum FormatWidth { /** * For `en-US`, 'M/d/yy, h:mm a'` * (Example: `6/15/15, 9:03 AM`) - * - * 对于 `en-US`,是 'M/d/yy, h:mm a'`(例如:`6/15/15, 9:03 AM`) - * */ Short, /** * For `en-US`, `'MMM d, y, h:mm:ss a'` * (Example: `Jun 15, 2015, 9:03:01 AM`) - * - * 对于 `en-US`,是 `'MMM d, y, h:mm:ss a'`(例如: `Jun 15, 2015, 9:03:01 AM` ) - * */ Medium, /** * For `en-US`, `'MMMM d, y, h:mm:ss a z'` * (Example: `June 15, 2015 at 9:03:01 AM GMT+1`) - * - * 对于 `en-US`,是 `'MMMM d, y, h:mm:ss a z'`,(例如: `June 15, 2015 at 9:03:01 AM GMT+1` ) - * */ Long, /** * For `en-US`, `'EEEE, MMMM d, y, h:mm:ss a zzzz'` * (Example: `Monday, June 15, 2015 at 9:03:01 AM GMT+01:00`) - * - * 对于 `en-US`,是 `'EEEE, MMMM d, y, h:mm:ss a zzzz'`(例如:`Monday, June 15, 2015 at 9:03:01 AM GMT+01:00` ) - * */ Full } @@ -171,129 +113,82 @@ export enum FormatWidth { * Symbols that can be used to replace placeholders in number patterns. * Examples are based on `en-US` values. * - * 可用于替换数字模式中占位符的符号。例如基于 `en-US` 的值。 - * * @see `getLocaleNumberSymbol()` - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export enum NumberSymbol { /** * Decimal separator. * For `en-US`, the dot character. - * Example : 2,345`.`67 - * - * 小数点分隔符。对于 `en-US`,是点字符。例如:2,345`.`67 - * + * Example: 2,345`.`67 */ Decimal, /** * Grouping separator, typically for thousands. * For `en-US`, the comma character. * Example: 2`,`345.67 - * - * 分组分隔符,通常为千位。对于 `en-US`,是逗号字符。例如:2`,`345.67 - * */ Group, /** * List-item separator. * Example: "one, two, and three" - * - * 列表项分隔符。例如:"one, two, and three" - * */ List, /** * Sign for percentage (out of 100). * Example: 23.4% - * - * 百分号(最大为 100)。例如:23.4% - * */ PercentSign, /** * Sign for positive numbers. * Example: +23 - * - * 正数的符号。例如:+23 - * */ PlusSign, /** * Sign for negative numbers. * Example: -23 - * - * 负数的符号。例如:-23 - * */ MinusSign, /** * Computer notation for exponential value (n times a power of 10). * Example: 1.2E3 - * - * 指数值的计算机表示法(10 的 n 次幂)。例如:1.2E3 - * */ Exponential, /** * Human-readable format of exponential. * Example: 1.2x103 - * - * 可读的指数格式。例如:1.2x103 - * */ SuperscriptingExponent, /** * Sign for permille (out of 1000). * Example: 23.4‰ - * - * 千分号(最大为 1000)。例如:23.4‰ - * */ PerMille, /** * Infinity, can be used with plus and minus. * Example: ∞, +∞, -∞ - * - * 无穷大,可与正负一起使用。例如:∞,+∞,-∞ - * */ Infinity, /** * Not a number. * Example: NaN - * - * 非数字。例如:NaN - * */ NaN, /** * Symbol used between time units. * Example: 10:52 - * - * 时间单位之间使用的符号。例如:10:52 - * */ TimeSeparator, /** * Decimal separator for currency values (fallback to `Decimal`). * Example: $2,345.67 - * - * 货币值的小数分隔符(回退为 `Decimal` )。例如:$2,345.67 - * */ CurrencyDecimal, /** * Group separator for currency values (fallback to `Group`). * Example: $2,345.67 - * - * 货币值的组分隔符(回退为 `Group` )。例如:$2,345.67 - * */ CurrencyGroup } @@ -301,8 +196,6 @@ export enum NumberSymbol { /** * The value for each day of the week, based on the `en-US` locale * - * 一周中每一天的值(基于 `en-US` 语言环境) - * * @publicApi */ export enum WeekDay { @@ -318,21 +211,10 @@ export enum WeekDay { /** * Retrieves the locale ID from the currently loaded locale. * The loaded locale could be, for example, a global one rather than a regional one. - * - * 从当前已加载的语言环境中检索语言环境 ID。加载的语言环境也可能是全球语言环境,而不是区域性语言环境。 - * * @param locale A locale code, such as `fr-FR`. - * - * 语言环境代码,例如 `fr-FR` 。 - * * @returns The locale code. For example, `fr`. - * - * 语言环境代码。例如, `fr` 。 - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getLocaleId(locale: string): string { @@ -342,28 +224,12 @@ export function getLocaleId(locale: string): string { /** * Retrieves day period strings for the given locale. * - * 检索给定语言环境的一天时段字符串。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @param formStyle The required grammatical form. - * - * 所需的语法形式。 - * * @param width The required character width. - * - * 所需的字符宽度。 - * * @returns An array of localized period strings. For example, `[AM, PM]` for `en-US`. - * - * 本地化的区间字符串数组。例如,`en-US` `[AM, PM]` 。 - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getLocaleDayPeriods( @@ -379,29 +245,13 @@ export function getLocaleDayPeriods( /** * Retrieves days of the week for the given locale, using the Gregorian calendar. * - * 使用公历来检索给定语言环境下的星期几。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @param formStyle The required grammatical form. - * - * 所需的语法形式。 - * * @param width The required character width. - * - * 所需的字符宽度。 - * * @returns An array of localized name strings. * For example,`[Sunday, Monday, ... Saturday]` for `en-US`. - * - * 本地化名称字符串的数组。例如,`en-US` `[Sunday, Monday, ... Saturday]` 。 - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getLocaleDayNames( @@ -416,29 +266,13 @@ export function getLocaleDayNames( /** * Retrieves months of the year for the given locale, using the Gregorian calendar. * - * 使用公历来检索给定语言环境下一年中的月份。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @param formStyle The required grammatical form. - * - * 所需的语法形式。 - * * @param width The required character width. - * - * 所需的字符宽度。 - * * @returns An array of localized name strings. * For example, `[January, February, ...]` for `en-US`. - * - * 本地化名称字符串的数组,例如,对于 `en-US` 是 `[January, February, ...]`。 - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getLocaleMonthNames( @@ -452,27 +286,13 @@ export function getLocaleMonthNames( /** * Retrieves Gregorian-calendar eras for the given locale. - * - * 检索给定语言环境的格里高利历日历。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @param width The required character width. - * - * 所需的字符宽度。 - * * @returns An array of localized era strings. * For example, `[AD, BC]` for `en-US`. - * - * 本地化年代字符串的数组。例如,对于 `en-US`,是 `[AD, BC]`。 - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getLocaleEraNames( @@ -485,22 +305,12 @@ export function getLocaleEraNames( /** * Retrieves the first day of the week for the given locale. * - * 检索给定语言环境中一周的第一天。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @returns A day index number, using the 0-based week-day index for `en-US` * (Sunday = 0, Monday = 1, ...). * For example, for `fr-FR`, returns 1 to indicate that the first day is Monday. - * - * 工作日索引号,使用基于 0 的 `en-US` 的工作日索引(星期日= 0,星期一= 1,...)。例如,对于 `fr-FR` ,返回 1 表示第一天是星期一。 - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getLocaleFirstDayOfWeek(locale: string): WeekDay { @@ -511,20 +321,10 @@ export function getLocaleFirstDayOfWeek(locale: string): WeekDay { /** * Range of week days that are considered the week-end for the given locale. * - * 在给定语言环境中被视为周末的工作日范围。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @returns The range of day values, `[startDay, endDay]`. - * - * 日期值的范围 `[startDay, endDay]` 。 - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getLocaleWeekEndRange(locale: string): [WeekDay, WeekDay] { @@ -535,26 +335,12 @@ export function getLocaleWeekEndRange(locale: string): [WeekDay, WeekDay] { /** * Retrieves a localized date-value formating string. * - * 检索本地化的日期-值格式字符串。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @param width The format type. - * - * 格式类型。 - * * @returns The localized formating string. - * - * 本地化的格式字符串。 - * * @see `FormatWidth` - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getLocaleDateFormat(locale: string, width: FormatWidth): string { @@ -565,26 +351,11 @@ export function getLocaleDateFormat(locale: string, width: FormatWidth): string /** * Retrieves a localized time-value formatting string. * - * 检索本地化的时间值格式字符串。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @param width The format type. - * - * 格式类型。 - * * @returns The localized formatting string. - * - * 本地化的格式字符串。 - * * @see `FormatWidth` - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) - * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ @@ -596,26 +367,12 @@ export function getLocaleTimeFormat(locale: string, width: FormatWidth): string /** * Retrieves a localized date-time formatting string. * - * 检索本地化的日期时间格式字符串。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @param width The format type. - * - * 格式类型。 - * * @returns The localized formatting string. - * - * 本地化的格式字符串。 - * * @see `FormatWidth` - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getLocaleDateTimeFormat(locale: string, width: FormatWidth): string { @@ -626,27 +383,12 @@ export function getLocaleDateTimeFormat(locale: string, width: FormatWidth): str /** * Retrieves a localized number symbol that can be used to replace placeholders in number formats. - * - * 检索本地化的数字符号,该符号可用于替换数字格式的占位符。 - * * @param locale The locale code. - * - * 语言环境代码。 - * * @param symbol The symbol to localize. - * - * 要本地化的符号。 - * * @returns The character for the localized symbol. - * - * 本地化符号的字符。 - * * @see `NumberSymbol` - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getLocaleNumberSymbol(locale: string, symbol: NumberSymbol): string { @@ -665,67 +407,36 @@ export function getLocaleNumberSymbol(locale: string, symbol: NumberSymbol): str /** * Retrieves a number format for a given locale. * - * 检索给定语言环境下的数字格式。 - * * Numbers are formatted using patterns, like `#,###.00`. For example, the pattern `#,###.00` * when used to format the number 12345.678 could result in "12'345,678". That would happen if the * grouping separator for your language is an apostrophe, and the decimal separator is a comma. * - * 数字会以类似 `#,###.00` 的模式进行格式化。例如,模式 `#,###.00` 用于格式化数字 12345.678 时可能的结果是 “12'345,678”。如果你的语言的分组分隔符是撇号,而十进制分隔符是逗号,就会是这样的。 - * * Important: The characters `.` `,` `0` `#` (and others below) are special placeholders * that stand for the decimal separator, and so on, and are NOT real characters. * You must NOT "translate" the placeholders. For example, don't change `.` to `,` even though in * your language the decimal point is written with a comma. The symbols should be replaced by the * local equivalents, using the appropriate `NumberSymbol` for your language. * - * *重要:*`.` `,` `0` `#`(以及以下的)字符是特殊的占位符,它们代表数字分隔符等,而不是真实的字符。你绝不能“翻译”这些占位符。例如,不要把 `.` 修改成 `,`,虽然在你的语言中小数点分隔符是写成逗号的。该符号会被其本地化等价物替换,也就是适合你的语言的 `NumberSymbol`。 - * * Here are the special characters used in number patterns: * - * 以下是数字模式中使用的特殊字符: - * * | Symbol | Meaning | * |--------|---------| - * | 符号 | 含义 | * | . | Replaced automatically by the character used for the decimal point. | - * | . | 自动替换为用作小数点的字符。 | * | , | Replaced by the "grouping" (thousands) separator. | - * | . | 替换为(千)“分组”分隔符。 | * | 0 | Replaced by a digit (or zero if there aren't enough digits). | - * | 0 | 替换为一个数字(如果没有足够的数字,则为零)。 | * | # | Replaced by a digit (or nothing if there aren't enough). | - * | # | 用数字代替(如果数字不足,则不进行任何替换)。 | * | ¤ | Replaced by a currency symbol, such as $ or USD. | - * | ¤ | 替换为货币符号,例如 $ 或 USD。 | * | % | Marks a percent format. The % symbol may change position, but must be retained. | - * | % | 标记百分比格式。%符号可能会更改位置,但必须保留。 | * | E | Marks a scientific format. The E symbol may change position, but must be retained. | - * | E | 标记科学计数法格式。 E 符号可能会改变位置,但必须保留。 | * | ' | Special characters used as literal characters are quoted with ASCII single quotes. | - * | ' | 表示文本字面量的特殊字符,用 ASCII 单引号引起来。 | * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @param type The type of numeric value to be formatted (such as `Decimal` or `Currency`.) - * - * 要格式化的数值类型(例如 `Decimal` 或 `Currency`)。 - * * @returns The localized format string. - * - * 本地化的格式字符串。 - * * @see `NumberFormatStyle` * @see [CLDR website](http://cldr.unicode.org/translation/number-patterns) - * - * [CLDR 官网](http://cldr.unicode.org/translation/number-patterns) - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getLocaleNumberFormat(locale: string, type: NumberFormatStyle): string { @@ -737,21 +448,11 @@ export function getLocaleNumberFormat(locale: string, type: NumberFormatStyle): * Retrieves the symbol used to represent the currency for the main country * corresponding to a given locale. For example, '$' for `en-US`. * - * 检索用于表示与给定语言环境对应的主要国家/地区的货币的符号。对于 `en-US` 为 `$`。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @returns The localized symbol character, * or `null` if the main country cannot be determined. - * - * 本地化的符号字符;如果无法确定主要国家则为 `null`。 - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getLocaleCurrencySymbol(locale: string): string|null { @@ -762,22 +463,11 @@ export function getLocaleCurrencySymbol(locale: string): string|null { /** * Retrieves the name of the currency for the main country corresponding * to a given locale. For example, 'US Dollar' for `en-US`. - * - * 检索与给定语言环境相对应的主要国家/地区的货币名称。对于 `en-US` 是 “US Dollar”。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @returns The currency name, * or `null` if the main country cannot be determined. - * - * 货币名称;如果无法确定主要国家/地区,则为 `null`。 - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getLocaleCurrencyName(locale: string): string|null { @@ -788,20 +478,11 @@ export function getLocaleCurrencyName(locale: string): string|null { /** * Retrieves the default currency code for the given locale. * - * 检索给定语言环境的默认货币代码。 - * * The default is defined as the first currency which is still in use. * - * 默认值定义为仍在使用的第一种货币。 - * * @param locale The code of the locale whose currency code we want. - * - * 我们想要获取货币代码的语言环境的代码。 - * * @returns The code of the default currency for the given locale. * - * 给定语言环境的默认货币代码。 - * * @publicApi */ export function getLocaleCurrencyCode(locale: string): string|null { @@ -810,21 +491,9 @@ export function getLocaleCurrencyCode(locale: string): string|null { /** * Retrieves the currency values for a given locale. - * - * 获取给定语言环境的货币值。 - * * @param locale A locale code for the locale format rules to use. - * - * 语言格式规则使用的语言环境代码。 - * * @returns The currency values. - * - * 货币值。 - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) - * - * [国际化(i18n)指南](guide/i18n) - * */ function getLocaleCurrencies(locale: string): {[code: string]: CurrenciesSymbols} { const data = ɵfindLocaleData(locale); @@ -833,7 +502,6 @@ function getLocaleCurrencies(locale: string): {[code: string]: CurrenciesSymbols /** * @alias core/ɵgetLocalePluralCase - * * @publicApi */ export const getLocalePluralCase: (locale: string) => ((value: number) => Plural) = @@ -851,38 +519,22 @@ function checkFullData(data: any) { * Retrieves locale-specific rules used to determine which day period to use * when more than one period is defined for a locale. * - * 检索特定于语言环境的规则,该规则用于确定在为一个语言环境中定义了多个时段时要使用的一天时段。 - * * There is a rule for each defined day period. The * first rule is applied to the first day period and so on. * Fall back to AM/PM when no rules are available. * - * 每个预定义的一天时段都有一个规则。第一条规则适用于第一个一天时段,依此类推。如果没有可用的规则,请回退为 AM / PM。 - * * A rule can specify a period as time range, or as a single time value. * - * 本规则可以将时间段指定为时间范围或单个时间值。 - * * This functionality is only available when you have loaded the full locale data. * See the ["I18n guide"](guide/i18n#i18n-pipes). * - * 仅当你加载了完整的语言环境数据时,此功能才可用。请参阅 [“I18n 指南”](guide/i18n#i18n-pipes) 。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @returns The rules for the locale, a single time value or array of *from-time, to-time*, * or null if no periods are available. * - * 语言环境的规则,单个时间值或 *from-time,to-time* 或 null 的数组(如果没有可用时段)。 - * * @see `getLocaleExtraDayPeriods()` - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getLocaleExtraDayPeriodRules(locale: string): (Time|[Time, Time])[] { @@ -902,34 +554,16 @@ export function getLocaleExtraDayPeriodRules(locale: string): (Time|[Time, Time] * in different languages. * For example, for `en-US`, periods are morning, noon, afternoon, evening, and midnight. * - * 检索特定于语言环境的一天时段,该时段大致指示如何用不同的语言分解一天。例如,对于 `en-US`,这些时段为 morning、noon、afternoon、evening 和 midnight。 - * * This functionality is only available when you have loaded the full locale data. * See the ["I18n guide"](guide/i18n#i18n-pipes). * - * 仅当你加载了完整的语言环境数据时,此功能才可用。请参阅 [“I18n 指南”](guide/i18n#i18n-pipes) 。 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @param formStyle The required grammatical form. - * - * 所需的语法形式。 - * * @param width The required character width. - * - * 所需的字符宽度。 - * * @returns The translated day-period strings. - * - * 翻译后的一天时段字符串。 - * * @see `getLocaleExtraDayPeriodRules()` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getLocaleExtraDayPeriods( @@ -946,23 +580,10 @@ export function getLocaleExtraDayPeriods( /** * Retrieves the writing direction of a specified locale - * - * 检索指定语言环境的书写方向 - * * @param locale A locale code for the locale format rules to use. - * - * 用于要使用的语言环境格式规则的语言环境代码。 - * * @publicApi - * * @returns 'rtl' or 'ltr' - * - * 'rtl' 或 'ltr' - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) - * - * [国际化(i18n)指南](guide/i18n) - * */ export function getLocaleDirection(locale: string): 'ltr'|'rtl' { const data = ɵfindLocaleData(locale); @@ -972,29 +593,14 @@ export function getLocaleDirection(locale: string): 'ltr'|'rtl' { /** * Retrieves the first value that is defined in an array, going backwards from an index position. * - * 检索数组中定义的第一个值,从索引位置开始向后找。 - * * To avoid repeating the same data (as when the "format" and "standalone" forms are the same) * add the first value to the locale data arrays, and add other values only if they are different. * - * 为避免重复相同的数据(比如当 "format" 和 "standalone" 的格式相同时),将第一个值添加到语言环境数据数组,并仅在它们不同时添加其他值。 - * * @param data The data array to retrieve from. - * - * 要检索的数据数组。 - * * @param index A 0-based index into the array to start from. - * - * 从 0 开始的数组索引。 - * * @returns The value immediately before the given index position. - * - * 给定索引位置之前的值。 - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ function getLastDefinedValue(data: T[], index: number): T { @@ -1009,8 +615,6 @@ function getLastDefinedValue(data: T[], index: number): T { /** * Represents a time value with hours and minutes. * - * 用小时和分钟表示的时间值。 - * * @publicApi */ export type Time = { @@ -1031,33 +635,16 @@ function extractTime(time: string): Time { /** * Retrieves the currency symbol for a given currency code. * - * 检索给定货币代码的货币符号。 - * * For example, for the default `en-US` locale, the code `USD` can * be represented by the narrow symbol `$` or the wide symbol `US$`. * - * 例如,对于默认 `en-US` 语言环境,代码 `USD` 可以由窄符号 `$` 或宽符号 `US$` 表示。 - * * @param code The currency code. - * - * 货币代码。 - * * @param format The format, `wide` or `narrow`. - * - * 格式,如 `wide` 或 `narrow` 。 - * * @param locale A locale code for the locale format rules to use. * - * 要用作语言环境格式规则的语言环境代码。 - * * @returns The symbol, or the currency code if no symbol is available. - * - * 符号,或货币代码(如果没有可用的符号)。 - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getCurrencySymbol(code: string, format: 'wide'|'narrow', locale = 'en'): string { @@ -1078,20 +665,10 @@ const DEFAULT_NB_OF_CURRENCY_DIGITS = 2; * Reports the number of decimal digits for a given currency. * The value depends upon the presence of cents in that particular currency. * - * 报告给定货币的小数位数。其值取决于该特定货币中分币是否存在。 - * * @param code The currency code. - * - * 货币代码。 - * * @returns The number of decimal digits, typically 0 or 2. - * - * 小数位数,通常为 0 或 2。 - * * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n) * - * [国际化(i18n)指南](guide/i18n) - * * @publicApi */ export function getNumberOfCurrencyDigits(code: string): number { diff --git a/packages/common/src/i18n/localization.ts b/packages/common/src/i18n/localization.ts index 61b83d4dc9..096cadccbb 100644 --- a/packages/common/src/i18n/localization.ts +++ b/packages/common/src/i18n/localization.ts @@ -48,8 +48,6 @@ export function getPluralCategory( /** * Returns the plural case based on the locale * - * 根据语言环境返回复数形式 - * * @publicApi */ @Injectable() diff --git a/packages/common/src/location/hash_location_strategy.ts b/packages/common/src/location/hash_location_strategy.ts index 35a526c25f..b46ff1ccfa 100644 --- a/packages/common/src/location/hash_location_strategy.ts +++ b/packages/common/src/location/hash_location_strategy.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {Inject, Injectable, Optional} from '@angular/core'; +import {Inject, Injectable, OnDestroy, Optional} from '@angular/core'; + import {APP_BASE_HREF, LocationStrategy} from './location_strategy'; import {LocationChangeListener, PlatformLocation} from './platform_location'; import {joinWithSlash, normalizeQueryParams} from './util'; @@ -20,26 +21,22 @@ import {joinWithSlash, normalizeQueryParams} from './util'; * [hash fragment](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) * of the browser's URL. * - * 此 {@link LocationStrategy} 用来配置 {@link Location} 服务,以便在浏览器 URL 的 [hash 片段](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax)中表示其状态。 - * * For instance, if you call `location.go('/foo')`, the browser's URL will become * `example.com#/foo`. * - * 例如,如果你调用 `location.go('/foo')` ,则浏览器的 URL 将变为 `example.com#/foo` 。 - * * @usageNotes * * ### Example * - * ### 例子 - * * {@example common/location/ts/hash_location_component.ts region='LocationComponent'} * * @publicApi */ @Injectable() -export class HashLocationStrategy extends LocationStrategy { +export class HashLocationStrategy extends LocationStrategy implements OnDestroy { private _baseHref: string = ''; + private _removeListenerFns: (() => void)[] = []; + constructor( private _platformLocation: PlatformLocation, @Optional() @Inject(APP_BASE_HREF) _baseHref?: string) { @@ -49,9 +46,15 @@ export class HashLocationStrategy extends LocationStrategy { } } + ngOnDestroy(): void { + while (this._removeListenerFns.length) { + this._removeListenerFns.pop()!(); + } + } + onPopState(fn: LocationChangeListener): void { - this._platformLocation.onPopState(fn); - this._platformLocation.onHashChange(fn); + this._removeListenerFns.push( + this._platformLocation.onPopState(fn), this._platformLocation.onHashChange(fn)); } getBaseHref(): string { diff --git a/packages/common/src/location/location.ts b/packages/common/src/location/location.ts index 54cc5c7a81..55af1e738d 100644 --- a/packages/common/src/location/location.ts +++ b/packages/common/src/location/location.ts @@ -25,44 +25,24 @@ export interface PopStateEvent { * * A service that applications can use to interact with a browser's URL. * - * 一个服务,应用可以用它来与浏览器的 URL 互动。 - * * Depending on the `LocationStrategy` used, `Location` persists * to the URL's path or the URL's hash segment. * - * 这取决于使用了哪个 {@link LocationStrategy},`Location` 可能会使用 URL 的路径进行持久化,也可能使用 URL 的哈希片段(`#`)进行持久化。 - * * @usageNotes * * It's better to use the `Router#navigate` service to trigger route changes. Use * `Location` only if you need to interact with or create normalized URLs outside of * routing. * - * 最好使用 {@link Router#navigate} 服务来触发路由变更。只有当你要在路由体系之外创建规范化 URL 或与之交互时才会用到 `Location`。 - * * `Location` is responsible for normalizing the URL against the application's base href. * A normalized URL is absolute from the URL host, includes the application's base href, and has no * trailing slash: - * - * `Location` 负责基于应用的基地址(base href)对 URL 进行标准化。 - * 所谓标准化的 URL 就是一个从主机(host)开始算的绝对地址,包括应用的基地址,但不包括结尾的斜杠: - * * - `/my/app/user/123` is normalized - * - * `/my/app/user/123` 是标准化的 - * * - `my/app/user/123` **is not** normalized - * - * `my/app/user/123` 不是标准化的 - * * - `/my/app/user/123/` **is not** normalized * - * `/my/app/user/123/` 不是标准化的 - * * ### Example * - * ### 例子 - * * * @@ -105,16 +85,9 @@ export class Location { /** * Normalizes the URL path for this location. * - * 返回标准化之后的 URL 路径 - * * @param includeHash True to include an anchor fragment in the path. * - * 路径中是否包含一个锚点片段(Anchor fragment)。 - * * @returns The normalized URL path. - * - * 标准化之后的 URL 路径。 - * */ // TODO: vsavkin. Remove the boolean flag and always include hash once the deprecated router is // removed. @@ -124,13 +97,7 @@ export class Location { /** * Reports the current state of the location history. - * - * 报告位置历史记录的当前状态。 - * * @returns The current value of the `history.state` object. - * - * `history.state` 对象的当前值。 - * */ getState(): unknown { return this._platformLocation.getState(); @@ -139,21 +106,11 @@ export class Location { /** * Normalizes the given path and compares to the current normalized path. * - * 对指定的路径进行标准化,并和当前的标准化路径进行比较。 - * * @param path The given URL path. - * - * 指定的 URL 路径。 - * * @param query Query parameters. * - * 查询参数。 - * * @returns True if the given URL path is equal to the current normalized path, false * otherwise. - * - * 如果指定的 URL 路径和标准化之后的路径一样,则返回 `true`,否则返回 `false`。 - * */ isCurrentPathEqualTo(path: string, query: string = ''): boolean { return this.path() == this.normalize(path + normalizeQueryParams(query)); @@ -162,16 +119,9 @@ export class Location { /** * Normalizes a URL path by stripping any trailing slashes. * - * 给出一个字符串形式的 URL,返回一个去掉末尾斜杠之后的 URL 路径。 - * * @param url String representing a URL. * - * 表示一个 URL。 - * * @returns The normalized URL string. - * - * 标准化之后的 URL 字符串。 - * */ normalize(url: string): string { return Location.stripTrailingSlash(_stripBaseHref(this._baseHref, _stripIndexHtml(url))); @@ -183,16 +133,9 @@ export class Location { * before normalizing. Adds a hash if `HashLocationStrategy` is * in use, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use. * - * 标准化外部 URL 路径。如果给定的 URL 并非以斜杠( `'/'` )开头,就会在规范化之前添加一个。如果使用 `HashLocationStrategy` 则添加哈希;如果使用 `PathLocationStrategy` 则添加 `APP_BASE_HREF`。 - * * @param url String representing a URL. * - * 表示一个 URL。 - * * @returns A normalized platform-specific URL. - * - * 标准化之后的平台相关 URL。 - * */ prepareExternalUrl(url: string): string { if (url && url[0] !== '/') { @@ -206,20 +149,10 @@ export class Location { * Changes the browser's URL to a normalized version of a given URL, and pushes a * new item onto the platform's history. * - * 把浏览器的 URL 修改为指定 URL 的标准化版本,并往所属平台(如浏览器)的历史堆栈中追加一个新条目。 - * * @param path URL path to normalize. - * - * 要标准化的路径。 - * * @param query Query parameters. - * - * 查询参数。 - * * @param state Location history state. * - * 历史状态。 - * */ go(path: string, query: string = '', state: any = null): void { this._platformStrategy.pushState(state, '', path, query); @@ -231,20 +164,9 @@ export class Location { * Changes the browser's URL to a normalized version of the given URL, and replaces * the top item on the platform's history stack. * - * 把浏览器的 URL 修改为指定 URL 的标准化版本,并替换所属平台(如浏览器)的历史堆栈的顶部条目。 - * * @param path URL path to normalize. - * - * 要标准化的路径 - * * @param query Query parameters. - * - * 查询参数 - * * @param state Location history state. - * - * 历史状态 - * */ replaceState(path: string, query: string = '', state: any = null): void { this._platformStrategy.replaceState(state, '', path, query); @@ -254,8 +176,6 @@ export class Location { /** * Navigates forward in the platform's history. - * - * 在所属平台(如浏览器)的历史堆栈中前进一步。 */ forward(): void { this._platformStrategy.forward(); @@ -263,8 +183,6 @@ export class Location { /** * Navigates back in the platform's history. - * - * 在所属平台(如浏览器)的历史堆栈中后退一步。 */ back(): void { this._platformStrategy.back(); @@ -274,12 +192,7 @@ export class Location { * Registers a URL change listener. Use to catch updates performed by the Angular * framework that are not detectible through "popstate" or "hashchange" events. * - * 注册 URL 更改监听器。被 Angular 框架用于捕获那些无法通过 “popstate” 或 “hashchange” 事件检测到的更新。 - * * @param fn The change handler function, which take a URL and a location history state. - * - * 更改处理器函数,接受 URL 和位置历史记录的状态。 - * */ onUrlChange(fn: (url: string, state: unknown) => void) { this._urlChangeListeners.push(fn); @@ -299,20 +212,10 @@ export class Location { /** * Subscribes to the platform's `popState` events. * - * 订阅所属平台(如浏览器)的 `popState` 事件。 - * * @param value Event that is triggered when the state history changes. - * - * 当状态历史发生变化时触发的事件 - * * @param exception The exception to throw. * - * 要抛出的异常。 - * * @returns Subscribed events. - * - * 已订阅的事件。 - * */ subscribe( onNext: (value: PopStateEvent) => void, onThrow?: ((exception: any) => void)|null, @@ -323,36 +226,20 @@ export class Location { /** * Normalizes URL parameters by prepending with `?` if needed. * - * 给定 URL 参数字符串,如果需要则增加 '?' 前缀,否则原样返回。 - * - * @param params String of URL parameters. - * - * URL 参数字符串 - * - * @returns The normalized URL parameters string. - * - * 为 URL 参数加上 `?` 前缀,如果原来就有,则原样返回。 + * @param params String of URL parameters. * + * @returns The normalized URL parameters string. */ public static normalizeQueryParams: (params: string) => string = normalizeQueryParams; /** * Joins two parts of a URL with a slash if needed. * - * 给定 url 的两个部分,把它们连接(join)在一起,如有必要则添加一个斜杠。 - * * @param start URL string - * - * URL 字符串 - * * @param end URL string * - * URL 字符串 * * @returns The joined URL string. - * - * 给定的一组 URL 字符串,如果需要,就用斜杠合在一起。 - * */ public static joinWithSlash: (start: string, end: string) => string = joinWithSlash; @@ -361,17 +248,9 @@ export class Location { * Looks for the first occurrence of either `#`, `?`, or the end of the * line as `/` characters and removes the trailing slash if one exists. * - * 如果 url 具有结尾斜杠,则移除它,否则原样返回。 - * 该方法会查找第一个 `#`、`?` 之前的结尾 `/` 字符,之后的则不管。如果 url 中没有 `#`、`?`,则替换行尾的。 - * * @param url URL string. * - * URL 字符串。 - * * @returns The URL string, modified if needed. - * - * 返回一个 URL 字符串,如果有结尾斜杠,则移除,否则原样返回。 - * */ public static stripTrailingSlash: (url: string) => string = stripTrailingSlash; } diff --git a/packages/common/src/location/location_strategy.ts b/packages/common/src/location/location_strategy.ts index 23156ec555..731f5c6091 100644 --- a/packages/common/src/location/location_strategy.ts +++ b/packages/common/src/location/location_strategy.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Inject, Injectable, InjectionToken, Optional, ɵɵinject} from '@angular/core'; +import {Inject, Injectable, InjectionToken, OnDestroy, Optional, ɵɵinject} from '@angular/core'; import {DOCUMENT} from '../dom_tokens'; import {LocationChangeListener, PlatformLocation} from './platform_location'; import {joinWithSlash, normalizeQueryParams} from './util'; @@ -16,24 +16,16 @@ import {joinWithSlash, normalizeQueryParams} from './util'; * Angular provides two strategies: * `HashLocationStrategy` and `PathLocationStrategy`. * - * 使 `Location` 服务能够从浏览器的 URL 读取路由状态。 Angular 提供了两种策略: `HashLocationStrategy` 和 `PathLocationStrategy` 。 - * * Applications should use the `Router` or `Location` services to * interact with application route state. * - * 应用程序应使用 `Router` 或 `Location` 服务与应用程序的路由状态进行交互。 - * * For instance, `HashLocationStrategy` produces URLs like * http://example.com#/foo, * and `PathLocationStrategy` produces * http://example.com/foo as an equivalent URL. * - * 例如,`HashLocationStrategy` 会处理像 http://example.com#/foo 这样的 URL,而 `PathLocationStrategy` 会处理像 http://example.com/foo 这样的等价 URL。 - * * See these two classes for more. * - * 有关更多信息,请参见这两个类。 - * * @publicApi */ @Injectable({providedIn: 'root', useFactory: provideLocationStrategy}) @@ -62,15 +54,11 @@ export function provideLocationStrategy(platformLocation: PlatformLocation) { * The base href is the URL prefix that should be preserved when generating * and recognizing URLs. * - * 预定义的 [DI 令牌](guide/glossary#di-token),用于和 `PathLocationStrategy` 一起使用的 base href。base href 是在生成和识别 URL 时应保留的 URL 前缀。 - * * @usageNotes * * The following example shows how to use this token to configure the root app injector * with a base href value, so that the DI framework can supply the dependency anywhere in the app. * - * 以下示例演示了如何使用此令牌为基本应用注入器配置 base href 值,以便 DI 框架可以在应用中的任何位置提供依赖项。 - * * ```typescript * import {Component, NgModule} from '@angular/core'; * import {APP_BASE_HREF} from '@angular/common'; @@ -92,45 +80,34 @@ export const APP_BASE_HREF = new InjectionToken('appBaseHref'); * [path](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) of the * browser's URL. * - * 此 {@link LocationStrategy} 用来配置 {@link Location} 服务,以便在浏览器 URL 的 [path](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) 中表示其状态。 - * * If you're using `PathLocationStrategy`, you must provide a {@link APP_BASE_HREF} * or add a `` element to the document. * - * 如果你使用 `PathLocationStrategy` ,则必须提供一个 {@link APP_BASE_HREF} 或在文档中添加 ``。 - * * For instance, if you provide an `APP_BASE_HREF` of `'/my/app/'` and call * `location.go('/foo')`, the browser's URL will become * `example.com/my/app/foo`. To ensure all relative URIs resolve correctly, * the `` and/or `APP_BASE_HREF` should end with a `/`. * - * 例如,如果你提供的 `APP_BASE_HREF` 是 `'/my/app/'`,并调用 `location.go('/foo')`,则浏览器的 URL 将变为 `example.com/my/app/foo`。为了确保所有相对 URI 都能正确解析,`` 和/或 `APP_BASE_HREF` 都应该以 `/` 结尾。 - * * Similarly, if you add `` to the document and call * `location.go('/foo')`, the browser's URL will become * `example.com/my/app/foo`. * - * 同样,如果将 `` 添加到文档中并调用 `location.go('/foo')` ,则浏览器的 URL 将变为 `example.com/my/app/foo` 。 - * * Note that when using `PathLocationStrategy`, neither the query nor * the fragment in the `` will be preserved, as outlined * by the [RFC](https://tools.ietf.org/html/rfc3986#section-5.2.2). * - * 请注意,使用 `PathLocationStrategy` 时,如 [RFC](https://tools.ietf.org/html/rfc3986#section-5.2.2) 所述,查询和 `` 的片段部分都不会保留。 - * * @usageNotes * * ### Example * - * ### 例子 - * * {@example common/location/ts/path_location_component.ts region='LocationComponent'} * * @publicApi */ @Injectable() -export class PathLocationStrategy extends LocationStrategy { +export class PathLocationStrategy extends LocationStrategy implements OnDestroy { private _baseHref: string; + private _removeListenerFns: (() => void)[] = []; constructor( private _platformLocation: PlatformLocation, @@ -149,9 +126,15 @@ export class PathLocationStrategy extends LocationStrategy { this._baseHref = href; } + ngOnDestroy(): void { + while (this._removeListenerFns.length) { + this._removeListenerFns.pop()!(); + } + } + onPopState(fn: LocationChangeListener): void { - this._platformLocation.onPopState(fn); - this._platformLocation.onHashChange(fn); + this._removeListenerFns.push( + this._platformLocation.onPopState(fn), this._platformLocation.onHashChange(fn)); } getBaseHref(): string { diff --git a/packages/common/src/location/platform_location.ts b/packages/common/src/location/platform_location.ts index 81c762ba4f..42371b9e5e 100644 --- a/packages/common/src/location/platform_location.ts +++ b/packages/common/src/location/platform_location.ts @@ -14,8 +14,6 @@ import {DOCUMENT} from '../dom_tokens'; * This class should not be used directly by an application developer. Instead, use * {@link Location}. * - * 此类不应由应用程序开发人员直接使用。而应使用 {@link Location}。 - * * `PlatformLocation` encapsulates all calls to DOM APIs, which allows the Router to be * platform-agnostic. * This means that we can have different implementation of `PlatformLocation` for the different @@ -23,21 +21,15 @@ import {DOCUMENT} from '../dom_tokens'; * implementation specific to the browser environment, while `@angular/platform-server` provides * one suitable for use with server-side rendering. * - * `PlatformLocation` 封装了对 DOM API 的所有调用,这可以让路由器与平台无关。这意味着我们可以为 Angular 支持的不同平台提供 `PlatformLocation` 的不同实现。例如, `@angular/platform-browser` 提供了特定于浏览器环境的实现,而 `@angular/platform-server` 提供了适合与服务器端渲染一起使用的实现。 - * * The `PlatformLocation` class is used directly by all implementations of {@link LocationStrategy} * when they need to interact with the DOM APIs like pushState, popState, etc. * - * {@link LocationStrategy} 的所有实现在需要与 DOM API(例如 pushState,popState 等)进行交互时,都直接使用 `PlatformLocation` - * * {@link LocationStrategy} in turn is used by the {@link Location} service which is used directly * by the {@link Router} in order to navigate between routes. Since all interactions between {@link * Router} / * {@link Location} / {@link LocationStrategy} and DOM APIs flow through the `PlatformLocation` * class, they are all platform-agnostic. * - * {@link LocationStrategy} 由 {@link Router} 直接使用的 {@link Location} 服务使用,以便在路由之间导航。由于 {@link Router} / {@link Location} / {@link LocationStrategy}与 DOM API 之间的所有交互都是通过 `PlatformLocation` 类进行的,因此它们都是与平台无关的。 - * * @publicApi */ @Injectable({ @@ -48,8 +40,14 @@ import {DOCUMENT} from '../dom_tokens'; export abstract class PlatformLocation { abstract getBaseHrefFromDOM(): string; abstract getState(): unknown; - abstract onPopState(fn: LocationChangeListener): void; - abstract onHashChange(fn: LocationChangeListener): void; + /** + * Returns a function that, when executed, removes the `popstate` event handler. + */ + abstract onPopState(fn: LocationChangeListener): VoidFunction; + /** + * Returns a function that, when executed, removes the `hashchange` event handler. + */ + abstract onHashChange(fn: LocationChangeListener): VoidFunction; abstract get href(): string; abstract get protocol(): string; @@ -76,8 +74,6 @@ export function useBrowserPlatformLocation() { * @description * Indicates when a location is initialized. * - * 指示何时初始化 location。 - * * @publicApi */ export const LOCATION_INITIALIZED = new InjectionToken>('Location Initialized'); @@ -86,8 +82,6 @@ export const LOCATION_INITIALIZED = new InjectionToken>('Location I * @description * A serializable version of the event from `onPopState` or `onHashChange` * - * 来自 `onPopState` 或 `onHashChange` 的事件的可序列化版本 - * * @publicApi */ export interface LocationChangeEvent { @@ -126,20 +120,24 @@ 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._history = getDOM().getHistory(); + (this as {location: Location}).location = window.location; + this._history = window.history; } getBaseHrefFromDOM(): string { return getDOM().getBaseHref(this._doc)!; } - onPopState(fn: LocationChangeListener): void { - getDOM().getGlobalEventTarget(this._doc, 'window').addEventListener('popstate', fn, false); + onPopState(fn: LocationChangeListener): VoidFunction { + const window = getDOM().getGlobalEventTarget(this._doc, 'window'); + window.addEventListener('popstate', fn, false); + return () => window.removeEventListener('popstate', fn); } - onHashChange(fn: LocationChangeListener): void { - getDOM().getGlobalEventTarget(this._doc, 'window').addEventListener('hashchange', fn, false); + onHashChange(fn: LocationChangeListener): VoidFunction { + const window = getDOM().getGlobalEventTarget(this._doc, 'window'); + window.addEventListener('hashchange', fn, false); + return () => window.removeEventListener('hashchange', fn); } get href(): string { diff --git a/packages/common/src/pipes/async_pipe.ts b/packages/common/src/pipes/async_pipe.ts index fb11601716..6365dbc067 100644 --- a/packages/common/src/pipes/async_pipe.ts +++ b/packages/common/src/pipes/async_pipe.ts @@ -58,34 +58,23 @@ const _subscribableStrategy = new SubscribableStrategy(); * * Unwraps a value from an asynchronous primitive. * - * 从一个异步回执中解出一个值。 - * * The `async` pipe subscribes to an `Observable` or `Promise` and returns the latest value it has * emitted. When a new value is emitted, the `async` pipe marks the component to be checked for * changes. When the component gets destroyed, the `async` pipe unsubscribes automatically to avoid * potential memory leaks. * - * `async` 管道会订阅一个 `Observable` 或 `Promise`,并返回它发出的最近一个值。 - * 当新值到来时,`async` 管道就会把该组件标记为需要进行变更检测。当组件被销毁时,`async` 管道就会自动取消订阅,以消除潜在的内存泄露问题。 - * * @usageNotes * * ### Examples * - * ### 例子 - * * This example binds a `Promise` to the view. Clicking the `Resolve` button resolves the * promise. * - * 这个例子把一个 `Promise` 绑定到了视图中。点击 `Resolve` 按钮就会解析此 Promise。 - * * {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'} * * It's also possible to use `async` with Observables. The example below binds the `time` Observable * to the view. The Observable continuously updates the view with the current time. * - * 还可以把 `async` 用于 `Observable`。下面的例子就把 `time` 这个 `Observable` 绑定到了视图上。这个 `Observable` 会不断使用当前时间更新视图。 - * * {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'} * * @publicApi diff --git a/packages/common/src/pipes/case_conversion_pipes.ts b/packages/common/src/pipes/case_conversion_pipes.ts index 0a7222efad..9b0612cb5b 100644 --- a/packages/common/src/pipes/case_conversion_pipes.ts +++ b/packages/common/src/pipes/case_conversion_pipes.ts @@ -12,8 +12,6 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; /** * Transforms text to all lower case. * - * 把文本转换成全小写形式。 - * * @see `UpperCasePipe` * @see `TitleCasePipe` * @usageNotes @@ -21,8 +19,6 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; * The following example defines a view that allows the user to enter * text, and then uses the pipe to convert the input text to all lower case. * - * 下面的例子定义了一个视图,允许用户输入文本,然后就会用该管道把输入的文本转换成全小写形式。 - * * * * @ngModule CommonModule @@ -32,8 +28,6 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; export class LowerCasePipe implements PipeTransform { /** * @param value The string to transform to lower case. - * - * 要转换成小写形式的字符串。 */ transform(value: string): string; transform(value: null|undefined): null; @@ -65,19 +59,12 @@ const unicodeWordMatch = * rest of the word to lower case. * Words are delimited by any whitespace character, such as a space, tab, or line-feed character. * - * 把文本转换成标题形式。 - * 把每个单词的第一个字母转成大写形式,并把单词的其余部分转成小写形式。 - * 单词之间用任意空白字符进行分隔,比如空格、Tab 或换行符。 - * * @see `LowerCasePipe` * @see `UpperCasePipe` * * @usageNotes - * * The following example shows the result of transforming various strings into title case. * - * 下面的例子示范了如何把多种字符串转成标题形式。 - * * * * @ngModule CommonModule @@ -87,8 +74,6 @@ const unicodeWordMatch = export class TitleCasePipe implements PipeTransform { /** * @param value The string to transform to title case. - * - * 要转换成标题形式的字符串。 */ transform(value: string): string; transform(value: null|undefined): null; @@ -106,9 +91,6 @@ export class TitleCasePipe implements PipeTransform { /** * Transforms text to all upper case. - * - * 把文本转换成全大写形式。 - * * @see `LowerCasePipe` * @see `TitleCasePipe` * @@ -119,8 +101,6 @@ export class TitleCasePipe implements PipeTransform { export class UpperCasePipe implements PipeTransform { /** * @param value The string to transform to upper case. - * - * 要转换成大写的字符串。 */ transform(value: string): string; transform(value: null|undefined): null; diff --git a/packages/common/src/pipes/date_pipe.ts b/packages/common/src/pipes/date_pipe.ts index 0e08f8540c..5fced0e337 100644 --- a/packages/common/src/pipes/date_pipe.ts +++ b/packages/common/src/pipes/date_pipe.ts @@ -17,15 +17,10 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; * * Formats a date value according to locale rules. * - * 根据区域设置规则格式化日期值。 - * * Only the `en-US` locale data comes with Angular. To localize dates * in another language, you must import the corresponding locale data. * See the [I18n guide](guide/i18n#i18n-pipes) for more information. * - * Angular 只自带了 `en-US` 区域的数据。要想在其它语言中对日期进行本地化,你必须导入相应的区域数据。 - * 欲知详情,参见 [I18n guide](guide/i18n#i18n-pipes)。 - * * @see `formatDate()` * * @@ -35,164 +30,103 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; * reformat the date on every change-detection cycle, treat the date as an immutable object * and change the reference when the pipe needs to run again. * - * 当输入值发生变化时,该管道的结果并不会改变。如果不想在每个变更检测周期中都强制重新格式化该日期,请把日期看做一个不可变对象, - * 当需要让该管道重新运行时,请赋给它一个新的对象,以更改它的引用。 - * * ### Pre-defined format options * - * ### 预定义的格式选项 - * - * Examples are given in `en-US` locale. - * - * 下面是 `en-US` 区域的例子。 - * - * - `'short'`: equivalent to `'M/d/yy, h:mm a'` (`6/15/15, 9:03 AM`). - * - * `'short'`: 等价于 `'M/d/yy, h:mm a'` (`6/15/15, 9:03 AM`). - * - * - `'medium'`: equivalent to `'MMM d, y, h:mm:ss a'` (`Jun 15, 2015, 9:03:01 AM`). - * - * `'medium'`: 等价于 `'MMM d, y, h:mm:ss a'` (`Jun 15, 2015, 9:03:01 AM`). - * - * - `'long'`: equivalent to `'MMMM d, y, h:mm:ss a z'` (`June 15, 2015 at 9:03:01 AM - * GMT+1`). - * - * `'long'`: 等价于 `'MMMM d, y, h:mm:ss a z'` (`June 15, 2015 at 9:03:01 AM - * GMT+1`)。 - * - * - `'full'`: equivalent to `'EEEE, MMMM d, y, h:mm:ss a zzzz'` (`Monday, June 15, 2015 at - * 9:03:01 AM GMT+01:00`). - * - * `'full'`: 等价于 `'EEEE, MMMM d, y, h:mm:ss a zzzz'` (`Monday, June 15, 2015 at - * 9:03:01 AM GMT+01:00`)。 - * - * - `'shortDate'`: equivalent to `'M/d/yy'` (`6/15/15`). - * - * `'shortDate'`: 等价于 `'M/d/yy'` (`6/15/15`). - * - * - `'mediumDate'`: equivalent to `'MMM d, y'` (`Jun 15, 2015`). - * - * `'mediumDate'`: 等价于 `'MMM d, y'` (`Jun 15, 2015`). - * - * - `'longDate'`: equivalent to `'MMMM d, y'` (`June 15, 2015`). - * - * `'longDate'`: 等价于 `'MMMM d, y'` (`June 15, 2015`). - * - * - `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` (`Monday, June 15, 2015`). - * - * `'fullDate'`: 等价于 `'EEEE, MMMM d, y'` (`Monday, June 15, 2015`). - * - * - `'shortTime'`: equivalent to `'h:mm a'` (`9:03 AM`). - * - * `'shortTime'`: 等价于 `'h:mm a'` (`9:03 AM`). - * - * - `'mediumTime'`: equivalent to `'h:mm:ss a'` (`9:03:01 AM`). - * - * `'mediumTime'`: 等价于 `'h:mm:ss a'` (`9:03:01 AM`). - * - * - `'longTime'`: equivalent to `'h:mm:ss a z'` (`9:03:01 AM GMT+1`). - * - * `'longTime'`: 等价于 `'h:mm:ss a z'` (`9:03:01 AM GMT+1`). - * - * - `'fullTime'`: equivalent to `'h:mm:ss a zzzz'` (`9:03:01 AM GMT+01:00`). - * - * `'fullTime'`: 等价于 `'h:mm:ss a zzzz'` (`9:03:01 AM GMT+01:00`). - * + * | Option | Equivalent to | Examples (given in `en-US` locale) | + * |---------------|-------------------------------------|-------------------------------------------------| + * | `'short'` | `'M/d/yy, h:mm a'` | `6/15/15, 9:03 AM` | + * | `'medium'` | `'MMM d, y, h:mm:ss a'` | `Jun 15, 2015, 9:03:01 AM` | + * | `'long'` | `'MMMM d, y, h:mm:ss a z'` | `June 15, 2015 at 9:03:01 AM GMT+1` | + * | `'full'` | `'EEEE, MMMM d, y, h:mm:ss a zzzz'` | `Monday, June 15, 2015 at 9:03:01 AM GMT+01:00` | + * | `'shortDate'` | `'M/d/yy'` | `6/15/15` | + * | `'mediumDate'`| `'MMM d, y'` | `Jun 15, 2015` | + * | `'longDate'` | `'MMMM d, y'` | `June 15, 2015` | + * | `'fullDate'` | `'EEEE, MMMM d, y'` | `Monday, June 15, 2015` | + * | `'shortTime'` | `'h:mm a'` | `9:03 AM` | + * | `'mediumTime'`| `'h:mm:ss a'` | `9:03:01 AM` | + * | `'longTime'` | `'h:mm:ss a z'` | `9:03:01 AM GMT+1` | + * | `'fullTime'` | `'h:mm:ss a zzzz'` | `9:03:01 AM GMT+01:00` | * * ### Custom format options * - * ### 自定义格式选项 - * * You can construct a format string using symbols to specify the components * of a date-time value, as described in the following table. * Format details depend on the locale. * Fields marked with (*) are only available in the extra data set for the given locale. * - * 你可以使用符号来构造出格式字符串,以指定日期-时间值的各个部分,如下表所示。 - * 具体格式取决于区域设置。 - * 标 `*` 的字段表示仅在特定区域的数据中才有效。 + * | Field type | Format | Description | Example Value | + * |-------------------- |-------------|---------------------------------------------------------------|------------------------------------------------------------| + * | Era | G, GG & GGG | Abbreviated | AD | + * | | GGGG | Wide | Anno Domini | + * | | GGGGG | Narrow | A | + * | Year | y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | + * | | yy | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | + * | | yyy | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | + * | | yyyy | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | + * | Week-numbering year | Y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | + * | | YY | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | + * | | YYY | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | + * | | YYYY | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | + * | Month | M | Numeric: 1 digit | 9, 12 | + * | | MM | Numeric: 2 digits + zero padded | 09, 12 | + * | | MMM | Abbreviated | Sep | + * | | MMMM | Wide | September | + * | | MMMMM | Narrow | S | + * | Month standalone | L | Numeric: 1 digit | 9, 12 | + * | | LL | Numeric: 2 digits + zero padded | 09, 12 | + * | | LLL | Abbreviated | Sep | + * | | LLLL | Wide | September | + * | | LLLLL | Narrow | S | + * | Week of year | w | Numeric: minimum digits | 1... 53 | + * | | ww | Numeric: 2 digits + zero padded | 01... 53 | + * | Week of month | W | Numeric: 1 digit | 1... 5 | + * | Day of month | d | Numeric: minimum digits | 1 | + * | | dd | Numeric: 2 digits + zero padded | 01 | + * | Week day | E, EE & EEE | Abbreviated | Tue | + * | | EEEE | Wide | Tuesday | + * | | EEEEE | Narrow | T | + * | | EEEEEE | Short | Tu | + * | Week day standalone | c, cc | Numeric: 1 digit | 2 | + * | | ccc | Abbreviated | Tue | + * | | cccc | Wide | Tuesday | + * | | ccccc | Narrow | T | + * | | cccccc | Short | Tu | + * | Period | a, aa & aaa | Abbreviated | am/pm or AM/PM | + * | | aaaa | Wide (fallback to `a` when missing) | ante meridiem/post meridiem | + * | | aaaaa | Narrow | a/p | + * | Period* | B, BB & BBB | Abbreviated | mid. | + * | | BBBB | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | + * | | BBBBB | Narrow | md | + * | Period standalone* | b, bb & bbb | Abbreviated | mid. | + * | | bbbb | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | + * | | bbbbb | Narrow | md | + * | Hour 1-12 | h | Numeric: minimum digits | 1, 12 | + * | | hh | Numeric: 2 digits + zero padded | 01, 12 | + * | Hour 0-23 | H | Numeric: minimum digits | 0, 23 | + * | | HH | Numeric: 2 digits + zero padded | 00, 23 | + * | Minute | m | Numeric: minimum digits | 8, 59 | + * | | mm | Numeric: 2 digits + zero padded | 08, 59 | + * | Second | s | Numeric: minimum digits | 0... 59 | + * | | ss | Numeric: 2 digits + zero padded | 00... 59 | + * | Fractional seconds | S | Numeric: 1 digit | 0... 9 | + * | | SS | Numeric: 2 digits + zero padded | 00... 99 | + * | | SSS | Numeric: 3 digits + zero padded (= milliseconds) | 000... 999 | + * | Zone | z, zz & zzz | Short specific non location format (fallback to O) | GMT-8 | + * | | zzzz | Long specific non location format (fallback to OOOO) | GMT-08:00 | + * | | Z, ZZ & ZZZ | ISO8601 basic format | -0800 | + * | | ZZZZ | Long localized GMT format | GMT-8:00 | + * | | ZZZZZ | ISO8601 extended format + Z indicator for offset 0 (= XXXXX) | -08:00 | + * | | O, OO & OOO | Short localized GMT format | GMT-8 | + * | | OOOO | Long localized GMT format | GMT-08:00 | * - * | Field type字段类型 | Format格式 | Description说明 | Example Value范例值 | - * | -------------------------------- | ------------------------ | ----------------------------- | --------------------------------- | - * | Era纪元 | G, GG & GGG | Abbreviated缩略 | AD | - * | | GGGG | Wide全称 | Anno Domini | - * | | GGGGG | Narrow最简 | A | - * | Year | y | Numeric数字: minimum digits最小位数 | 2, 20, 201, 2017, 20173 | - * | | yy | Numeric数字: 2 digits + zero padded数字 + 0 补位 | 02, 20, 01, 17, 73 | - * | | yyy | Numeric数字: 3 digits + zero padded数字 + 0 补位 | 002, 020, 201, 2017, 20173 | - * | | yyyy | Numeric数字: 4 digits or more + zero padded或更多数字 + 0 补位 | 0002, 0020, 0201, 2017, 20173 | - * | Week-numbering year | Y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | - * | 周日历年 | Y | 数字: 最少化数字 | 2, 20, 201, 2017, 20173 | - * | | YY | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | - * | | YY | 数字:2 字符+补零 | 02, 20, 01, 17, 73 | - * | | YYY | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | - * | | YYY | 数字:3 字符+补零 | 002, 020, 201, 2017, 20173 | - * | | YYYY | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | - * | | YYYY | 数字:4 字符+补零 | 0002, 0020, 0201, 2017, 20173 | - * | Month | M | Numeric数字: 1 digit1 数字 | 9, 12 | - * | | MM | Numeric数字: 2 digits + zero padded数字 + 0 补位 | 09, 12 | - * | | MMM | Abbreviated缩略 | Sep | - * | | MMMM | Wide全称 | September | - * | | MMMMM | Narrow最简 | S | - * | Month standalone独立月份 | L | Numeric数字: 1 digit1 数字 | 9, 12 | - * | | LL | Numeric数字: 2 digits + zero padded数字 + 0 补位 | 09, 12 | - * | | LLL | Abbreviated缩略 | Sep | - * | | LLLL | Wide全称 | September | - * | | LLLLL | Narrow最简 | S | - * | Week of year年内周次 | w | Numeric数字: minimum digits最小位数 | 1... 53 | - * | | ww | Numeric数字: 2 digits + zero padded数字 + 0 补位 | 01... 53 | - * | Week of month月内周次 | W | Numeric数字: 1 digit1 数字 | 1... 5 | - * | Day of month月内日 | d | Numeric数字: minimum digits最小位数 | 1 | - * | | dd | Numeric数字: 2 digits + zero padded数字 + 0 补位 | 01 | - * | Week day周内日 | E, EE & EEE | Abbreviated缩略 | Tue | - * | | EEEE | Wide全称 | Tuesday | - * | | EEEEE | Narrow最简 | T | - * | | EEEEEE | Short | Tu | - * | Period日内时段 | a, aa & aaa | Abbreviated缩略 | am/pm or AM/PM | - * | | aaaa | Wide全称(fallback to `a` when missing)(缺少时等同于 `a`) | ante meridiem/post meridiem | - * | | aaaaa | Narrow最简 | a/p | - * | Period\*日内时段 | B, BB & BBB | Abbreviated缩略 | mid. | - * | | BBBB | Wide全称 | am, pm, midnight, noon, morning, afternoon, evening, night | - * | | BBBBB | Narrow最简 | md | - * | Period standalone\*独立时段 | b, bb & bbb | Abbreviated缩略 | mid. | - * | | bbbb | Wide全称 | am, pm, midnight, noon, morning, afternoon, evening, night | - * | | bbbbb | Narrow最简 | md | - * | Hour 1-12小时(1-12) | h | Numeric数字: minimum digits最小位数 | 1, 12 | - * | | hh | Numeric数字: 2 digits + zero padded数字 + 0 补位 | 01, 12 | - * | Hour 0-23小时(0-23) | H | Numeric数字: minimum digits最小位数 | 0, 23 | - * | | HH | Numeric数字: 2 digits + zero padded数字 + 0 补位 | 00, 23 | - * | Minute | m | Numeric数字: minimum digits最小位数 | 8, 59 | - * | | mm | Numeric数字: 2 digits + zero padded数字 + 0 补位 | 08, 59 | - * | Second | s | Numeric数字: minimum digits最小位数 | 0... 59 | - * | | ss | Numeric数字: 2 digits + zero padded数字 + 0 补位 | 00... 59 | - * | Fractional seconds分数秒 | S | Numeric数字: 1 digit1 数字 | 0... 9 | - * | | SS | Numeric数字: 2 digits + zero padded数字 + 0 补位 | 00... 99 | - * | | SSS | Numeric数字: 3 digits + zero padded数字 + 0 补位 (= milliseconds毫秒) | 000... 999 | - * | Zone时区 | z, zz & zzz | Short specific non location format (fallback to O)位置无关短格式(默认为 0) | GMT-8 | - * | | zzzz | Long specific non location format (fallback to OOOO)位置无关长格式(默认为 0000) | GMT-08:00 | - * | | Z, ZZ & ZZZ | ISO8601 basic format基本格式 | -0800 | - * | | ZZZZ | Long localized GMT format本地化 GMT 长格式 | GMT-8:00 | - * | | ZZZZZ | ISO8601 extended format + Z indicator for offset 0扩展格式 + 偏移为 0 时用 Z 表示 (= XXXXX) | -08:00 | - * | | O, OO & OOO | Short localized GMT format本地化 GMT 短格式 | GMT-8 | - * | | OOOO | Long localized GMT format本地化 GMT 长格式 | GMT-08:00 | - * - * Note that timezone correction is not applied to an ISO string that has no time component, such as "2016-09-19" - * - * 请注意,时区校正不适用于没有时间部分的 ISO 字符串,例如“2016-09-19” * * ### Format examples * - * ### 格式范例 - * * These examples transform a date into various formats, * assuming that `dateObj` is a JavaScript `Date` object for * year: 2015, month: 6, day: 15, hour: 21, minute: 43, second: 11, * given in the local time for the `en-US` locale. * - * 下面这些例子会把日期转换成多种格式。 - * 这里假设 `dateObj` 是个 JavaScript 的 `Date` 对象: 2015 年 6 月 15 日 21 时 43 分 11 秒, - * 使用的是 `en-US` 区域的当地时间。 - * * ``` * {{ dateObj | date }} // output is 'Jun 15, 2015' * {{ dateObj | date:'medium' }} // output is 'Jun 15, 2015, 9:43:11 PM' @@ -202,12 +136,8 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; * * ### Usage example * - * ### 使用范例 - * * The following component uses a date pipe to display the current date in different formats. * - * 下列组件借助一个日期管道来以不同的格式显示当前日期。 - * * ``` * @Component({ * selector: 'date-pipe', @@ -233,31 +163,15 @@ export class DatePipe implements PipeTransform { /** * @param value The date expression: a `Date` object, a number * (milliseconds since UTC epoch), or an ISO string (https://www.w3.org/TR/NOTE-datetime). - * - * 日期表达式:`Date` 对象、数字(从 UTC 时代以来的毫秒数)或一个 ISO 字符串 (https://www.w3.org/TR/NOTE-datetime)。 - * * @param format The date/time components to include, using predefined options or a * custom format string. - * - * 要包含的日期、时间部分的格式,使用预定义选项或自定义格式字符串。 - * * @param timezone A timezone offset (such as `'+0430'`), or a standard * UTC/GMT or continental US timezone abbreviation. * When not supplied, uses the end-user's local system timezone. - * - * 一个时区偏移(比如 `'+0430'`)或标准的 UTC/GMT 或美国大陆时区的缩写。默认为最终用户机器上的本地系统时区。 - * * @param locale A locale code for the locale format rules to use. * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. * See [Setting your app locale](guide/i18n#setting-up-the-locale-of-your-app). - * - * 要使用的区域格式规则的区域代码。 - * 如果不提供,就使用 `LOCALE_ID` 的值,默认为 `en-US`。 - * 参见[设置应用的区域](guide/i18n#setting-up-the-locale-of-your-app)。 - * * @returns A date string in the desired format. - * - * 指定格式的日期字符串。 */ transform(value: Date|string|number, format?: string, timezone?: string, locale?: string): string |null; diff --git a/packages/common/src/pipes/i18n_plural_pipe.ts b/packages/common/src/pipes/i18n_plural_pipe.ts index cf9c2e1a66..252da96c3e 100644 --- a/packages/common/src/pipes/i18n_plural_pipe.ts +++ b/packages/common/src/pipes/i18n_plural_pipe.ts @@ -20,14 +20,10 @@ const _INTERPOLATION_REGEXP: RegExp = /#/g; * * Maps a value to a string that pluralizes the value according to locale rules. * - * 将值映射到根据语言环境规则对该值进行复数化的字符串。 - * * @usageNotes * * ### Example * - * ### 例子 - * * {@example common/pipes/ts/i18n_pipe.ts region='I18nPluralPipeComponent'} * * @publicApi @@ -38,18 +34,10 @@ export class I18nPluralPipe implements PipeTransform { /** * @param value the number to be formatted - * - * 要格式化的数字 - * * @param pluralMap an object that mimics the ICU format, see * http://userguide.icu-project.org/formatparse/messages. - * - * 模仿 ICU 格式的对象,请参见 。 - * * @param locale a `string` defining the locale to use (uses the current {@link LOCALE_ID} by * default). - * - * 定义要使用的语言环境的 `string`(默认情况下使用当前的 {@link LOCALE_ID})。 */ transform(value: number|null|undefined, pluralMap: {[count: string]: string}, locale?: string): string { diff --git a/packages/common/src/pipes/i18n_select_pipe.ts b/packages/common/src/pipes/i18n_select_pipe.ts index 7c45185cc0..d5fac44493 100644 --- a/packages/common/src/pipes/i18n_select_pipe.ts +++ b/packages/common/src/pipes/i18n_select_pipe.ts @@ -15,19 +15,13 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; * * Generic selector that displays the string that matches the current value. * - * 通用选择器,用于显示与当前值匹配的字符串。 - * * If none of the keys of the `mapping` match the `value`, then the content * of the `other` key is returned when present, otherwise an empty string is returned. * - * 如果 `mapping` 中的任何键都不与 `value` 匹配,`other` 键的内容如果存在则返回,否则返回空字符串。 - * * @usageNotes * * ### Example * - * ### 例子 - * * {@example common/pipes/ts/i18n_pipe.ts region='I18nSelectPipeComponent'} * * @publicApi @@ -36,14 +30,8 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; export class I18nSelectPipe implements PipeTransform { /** * @param value a string to be internationalized. - * - * 要国际化的字符串。 - * * @param mapping an object that indicates the text that should be displayed * for different values of the provided `value`. - * - * 一个对象,指示对不同于所提供 `value` 的值应该显示的文本。 - * */ transform(value: string|null|undefined, mapping: {[key: string]: string}): string { if (value == null) return ''; diff --git a/packages/common/src/pipes/json_pipe.ts b/packages/common/src/pipes/json_pipe.ts index 5169465c85..5a4515a0e6 100644 --- a/packages/common/src/pipes/json_pipe.ts +++ b/packages/common/src/pipes/json_pipe.ts @@ -14,15 +14,11 @@ import {Pipe, PipeTransform} from '@angular/core'; * * Converts a value into its JSON-format representation. Useful for debugging. * - * 把一个值转换成 JSON 字符串格式。在调试时很有用。 - * * @usageNotes * * The following component uses a JSON pipe to convert an object * to JSON format, and displays the string in both formats for comparison. * - * 下列组件使用了一个 `JSON` 管道来把对象转换成 JSON 格式,并以两种格式显示字符串供对比。 - * * {@example common/pipes/ts/json_pipe.ts region='JsonPipe'} * * @publicApi @@ -31,8 +27,6 @@ import {Pipe, PipeTransform} from '@angular/core'; export class JsonPipe implements PipeTransform { /** * @param value A value of any type to convert into a JSON-format string. - * - * 任何类型的要转换成 JSON 字符串格式的值 */ 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 caf62c9154..163b83f3e6 100644 --- a/packages/common/src/pipes/keyvalue_pipe.ts +++ b/packages/common/src/pipes/keyvalue_pipe.ts @@ -16,8 +16,6 @@ function makeKeyValuePair(key: K, value: V): KeyValue { * A key value pair. * Usually used to represent the key value pairs from a Map or Object. * - * 一个键值对。通常用于表示 Map 或 Object 中的键值对。 - * * @publicApi */ export interface KeyValue { @@ -31,25 +29,16 @@ export interface KeyValue { * * Transforms Object or Map into an array of key value pairs. * - * 将 Object 或 Map 转换为键值对数组。 - * * The output array will be ordered by keys. * By default the comparator will be by Unicode point value. * You can optionally pass a compareFn if your keys are complex types. * - * 输出数组将通过键名排序。默认情况下,比较器将使用 Unicode 点位值。如果你的键名是复杂类型,则可以选择传入一个 compareFn。 - * * @usageNotes - * * ### Examples * - * ### 例子 - * * This examples show how an Object or a Map can be iterated by ngFor with the use of this * keyvalue pipe. * - * 此示例演示了 ngFor 如何使用此键值管道对 Object 或 Map 进行迭代。 - * * {@example common/pipes/ts/keyvalue_pipe.ts region='KeyValuePipe'} * * @publicApi diff --git a/packages/common/src/pipes/number_pipe.ts b/packages/common/src/pipes/number_pipe.ts index 4f4820ca03..fa25ff6017 100644 --- a/packages/common/src/pipes/number_pipe.ts +++ b/packages/common/src/pipes/number_pipe.ts @@ -17,44 +17,60 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; * @ngModule CommonModule * @description * - * Transforms a number into a string, - * formatted according to locale rules that determine group sizing and - * separator, decimal-point character, and other locale-specific - * configurations. - * - * 把数字转换成字符串, - * 根据本地化规则进行格式化,这些规则会决定分组大小和分组分隔符、小数点字符以及其它与本地化环境有关的配置项。 - * - * If no parameters are specified, the function rounds off to the nearest value using this - * [rounding method](https://en.wikibooks.org/wiki/Arithmetic/Rounding). - * The behavior differs from that of the JavaScript ```Math.round()``` function. - * In the following case for example, the pipe rounds down where - * ```Math.round()``` rounds up: - * - * 如果没有指定参数,则该函数会使用这个[舍入方法](https://en.wikibooks.org/wiki/Arithmetic/Rounding)。 - * 但其行为与 JavaScript 的 ```Math.round()``` 函数不同。 - * 下面的例子展示了管道与 ```Math.round()``` 的对比: - * - * ```html - * -2.5 | number:'1.0-0' - * > -3 - * Math.round(-2.5) - * > -2 - * ``` + * Formats a value according to digit options and locale rules. + * Locale determines group sizing and separator, + * decimal point character, and other locale-specific configurations. * * @see `formatNumber()` * * @usageNotes * - * The following code shows how the pipe transforms numbers - * into text strings, according to various format specifications, - * where the caller's default locale is `en-US`. + * ### digitsInfo * - * 下列代码展示了该管道如何根据多种格式规范来把数字转换成字符串,这里使用的默认语言环境是 `en-US`。 + * The value's decimal representation is specified by the `digitsInfo` + * parameter, written in the following format:
    + * + * ``` + * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits} + * ``` + * + * - `minIntegerDigits`: + * The minimum number of integer digits before the decimal point. + * Default is 1. + * + * - `minFractionDigits`: + * The minimum number of digits after the decimal point. + * Default is 0. + * + * - `maxFractionDigits`: + * The maximum number of digits after the decimal point. + * Default is 3. + * + * If the formatted value is truncated it will be rounded using the "to-nearest" method: + * + * ``` + * {{3.6 | number: '1.0-0'}} + * + * + * {{-3.6 | number:'1.0-0'}} + * + * ``` + * + * ### locale + * + * `locale` will format a value according to locale rules. + * Locale determines group sizing and separator, + * decimal point character, and other locale-specific configurations. + * + * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. + * + * See [Setting your app locale](guide/i18n#setting-up-the-locale-of-your-app). * * ### Example * - * ### 例子 + * The following code shows how the pipe transforms values + * according to various format specifications, + * where the caller's default locale is `en-US`. * * * @@ -63,45 +79,17 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; @Pipe({name: 'number'}) export class DecimalPipe implements PipeTransform { constructor(@Inject(LOCALE_ID) private _locale: string) {} - - /** - * @param value The number to be formatted. - * - * 要格式化的数字。 - * - * @param digitsInfo Decimal representation options, specified by a string - * in the following format:
    - * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}. - * - * 数字展现的选项,通过如下格式的字符串指定:
    - * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}。 - * - * - `minIntegerDigits`: The minimum number of integer digits before the decimal point. - * Default is `1`. - * - * `minIntegerDigits`:在小数点前的最小位数。默认为 `1`。 - * - * - `minFractionDigits`: The minimum number of digits after the decimal point. - * Default is `0`. - * - * `minFractionDigits`:小数点后的最小位数。默认为 `0`。 - * - * - `maxFractionDigits`: The maximum number of digits after the decimal point. - * Default is `3`. - * - * `maxFractionDigits`:小数点后的最大为数,默认为 `3`。 - * - * @param locale A locale code for the locale format rules to use. - * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. - * See [Setting your app locale](guide/i18n#setting-up-the-locale-of-your-app). - * - * 要使用的本地化格式代码。 - * 如果未提供,则使用 `LOCALE_ID` 的值,默认为 `en-US`。 - * 参见[为你的应用设置地区(locale)](guide/i18n#setting-up-the-locale-of-your-app)。 - */ transform(value: number|string, digitsInfo?: string, locale?: string): string|null; transform(value: null|undefined, digitsInfo?: string, locale?: string): null; transform(value: number|string|null|undefined, digitsInfo?: string, locale?: string): string|null; + + /** + * @param value The value to be formatted. + * @param digitsInfo Sets digit and decimal representation. + * [See more](#digitsinfo). + * @param locale Specifies what locale format rules to use. + * [See more](#locale). + */ transform(value: number|string|null|undefined, digitsInfo?: string, locale?: string): string |null { if (!isValue(value)) return null; @@ -126,20 +114,13 @@ export class DecimalPipe implements PipeTransform { * separator, decimal-point character, and other locale-specific * configurations. * - * 把数字转换成百分比字符串, - * 根据本地化规则进行格式化,这些规则会决定分组大小和分组分隔符、小数点字符以及其它与本地化环境有关的配置项。 - * * @see `formatPercent()` * * @usageNotes - * * The following code shows how the pipe transforms numbers * into text strings, according to various format specifications, * where the caller's default locale is `en-US`. * - * 下列代码展示了该管道如何根据多种格式规范把数字转换成文本字符串, - * 这里使用的默认语言环境是 `en-US`。 - * * * * @publicApi @@ -151,39 +132,18 @@ export class PercentPipe implements PipeTransform { /** * * @param value The number to be formatted as a percentage. - * - * 要格式化为百分比的数字。 - * * @param digitsInfo Decimal representation options, specified by a string * in the following format:
    * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}. - * - * 数字展现的选项,通过如下格式的字符串指定:
    - * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}。 - * - * * - `minIntegerDigits`: The minimum number of integer digits before the decimal point. * Default is `1`. - * - * `minIntegerDigits`:在小数点前的最小位数。默认为 `1`。 - * * - `minFractionDigits`: The minimum number of digits after the decimal point. * Default is `0`. - * - * `minFractionDigits`:小数点后的最小位数。默认为 `0`。 - * * - `maxFractionDigits`: The maximum number of digits after the decimal point. * Default is `0`. - * - * `maxFractionDigits`:小数点后的最大为数,默认为 `3`。 - * * @param locale A locale code for the locale format rules to use. * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. * See [Setting your app locale](guide/i18n#setting-up-the-locale-of-your-app). - * - * 要使用的本地化格式代码。 - * 如果未提供,则使用 `LOCALE_ID` 的值,默认为 `en-US`。 - * 参见[为你的应用设置地区(locale)](guide/i18n#setting-up-the-locale-of-your-app)。 */ transform(value: number|string, digitsInfo?: string, locale?: string): string|null; transform(value: null|undefined, digitsInfo?: string, locale?: string): null; @@ -209,9 +169,6 @@ export class PercentPipe implements PipeTransform { * that determine group sizing and separator, decimal-point character, * and other locale-specific configurations. * - * 把数字转换成金额字符串, - * 根据本地化规则进行格式化,这些规则会决定分组大小和分组分隔符、小数点字符以及其它与本地化环境有关的配置项。 - * * {@a currency-code-deprecation} *
    * @@ -220,7 +177,7 @@ export class PercentPipe implements PipeTransform { * The default currency code is currently always `USD` but this is deprecated from v9. * * **In v11 the default currency code will be taken from the current locale identified by - * the `LOCAL_ID` token. See the [i18n guide](guide/i18n#setting-up-the-locale-of-your-app) for + * the `LOCALE_ID` token. See the [i18n guide](guide/i18n#setting-up-the-locale-of-your-app) for * more information.** * * If you need the previous behavior then set it by creating a `DEFAULT_CURRENCY_CODE` provider in @@ -236,14 +193,10 @@ export class PercentPipe implements PipeTransform { * @see `formatCurrency()` * * @usageNotes - * * The following code shows how the pipe transforms numbers * into text strings, according to various format specifications, * where the caller's default locale is `en-US`. * - * 下列代码展示了该管道如何根据多种格式规范把数字转换成文本字符串, - * 这里使用的默认语言环境是 `en-US`。 - * * * * @publicApi @@ -257,80 +210,35 @@ export class CurrencyPipe implements PipeTransform { /** * * @param value The number to be formatted as currency. - * - * 要格式化为货币的数字。 - * * @param currencyCode The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code, * such as `USD` for the US dollar and `EUR` for the euro. The default currency code can be * configured using the `DEFAULT_CURRENCY_CODE` injection token. - * - * [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) 中的货币代码,比如 `USD` 表示美元,`EUR` 表示欧元。可以用 `DEFAULT_CURRENCY_CODE` 这个注入令牌来配置默认货币代码。 - * * @param display The format for the currency indicator. One of the following: - * - * 货币指示器的格式,有效值包括: - * * - `code`: Show the code (such as `USD`). - * - * `code`: 展示货币代码(如 `USD`)。 - * * - `symbol`(default): Show the symbol (such as `$`). - * - * `symbol`(default): 展示货币符号(如 `$`) - * * - `symbol-narrow`: Use the narrow symbol for locales that have two symbols for their * currency. * For example, the Canadian dollar CAD has the symbol `CA$` and the symbol-narrow `$`. If the * locale has no narrow symbol, uses the standard symbol for the locale. - * - * `symbol-narrow`: 使用区域的窄化符号,它包括两个符号。 - * 比如,加拿大元的符号是 `CA$`,而其窄化符号是 `$`。如果该区域没有窄化符号,则使用它的标准符号。 - * * - String: Use the given string value instead of a code or a symbol. * For example, an empty string will suppress the currency & symbol. - * - * String: 使用指定的字符串值代替货币代码或符号。 - * 比如,空字符串将会去掉货币代码或符号。 - * * - Boolean (marked deprecated in v5): `true` for symbol and false for `code`. * - * Boolean(从 v5 开始已弃用):`true` 表示货币符号,`false` 表示货币代码。 - * * @param digitsInfo Decimal representation options, specified by a string * in the following format:
    * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}. - * - * 数字展现的选项,通过如下格式的字符串指定:
    - * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}。 - * - * * - `minIntegerDigits`: The minimum number of integer digits before the decimal point. * Default is `1`. - * - * `minIntegerDigits`:在小数点前的最小位数。默认为 `1`。 - * * - `minFractionDigits`: The minimum number of digits after the decimal point. * Default is `2`. - * - * `minFractionDigits`:小数点后的最小位数。默认为 `0`。 - * * - `maxFractionDigits`: The maximum number of digits after the decimal point. * Default is `2`. * If not provided, the number will be formatted with the proper amount of digits, * depending on what the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) specifies. * For example, the Canadian dollar has 2 digits, whereas the Chilean peso has none. - * - * `maxFractionDigits`:小数点后的最大为数,默认为 `3`。 - * 如果没有提供,该数字就会根据 [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) 规范进行适当的格式化。 - * 比如,加拿大元具有 2 位数字,而智利比索则没有。 - * * @param locale A locale code for the locale format rules to use. * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. * See [Setting your app locale](guide/i18n#setting-up-the-locale-of-your-app). - * - * 要使用的本地化格式代码。 - * 如果未提供,则使用 `LOCALE_ID` 的值,默认为 `en-US`。 - * 参见[为你的应用设置地区(locale)](guide/i18n#setting-up-the-locale-of-your-app)。 */ transform( value: number|string, currencyCode?: string, @@ -353,7 +261,7 @@ export class CurrencyPipe implements PipeTransform { locale = locale || this._locale; if (typeof display === 'boolean') { - if (console && console.warn) { + if ((typeof ngDevMode === 'undefined' || ngDevMode) && console && console.warn) { console.warn( `Warning: the currency pipe has been changed in Angular v5. The symbolDisplay option (third parameter) is now a string instead of a boolean. The accepted values are "code", "symbol" or "symbol-narrow".`); } @@ -384,8 +292,6 @@ function isValue(value: number|string|null|undefined): value is number|string { /** * Transforms a string into a number (if needed). - * - * 把字符串转换成数字(如果需要)。 */ function strToNumber(value: number|string): number { // Convert strings to numbers diff --git a/packages/common/src/pipes/slice_pipe.ts b/packages/common/src/pipes/slice_pipe.ts index 78f4d1e67c..90bf602f92 100644 --- a/packages/common/src/pipes/slice_pipe.ts +++ b/packages/common/src/pipes/slice_pipe.ts @@ -15,38 +15,24 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; * * Creates a new `Array` or `String` containing a subset (slice) of the elements. * - * 从一个 `Array` 或 `String` 中创建其元素一个新子集(slice)。 - * * @usageNotes * * All behavior is based on the expected behavior of the JavaScript API `Array.prototype.slice()` * and `String.prototype.slice()`. * - * 所有行为都基于 JavaScript API `Array.prototype.slice()` 和 `String.prototype.slice()` 的预期行为。 - * * When operating on an `Array`, the returned `Array` is always a copy even when all * the elements are being returned. * - * 当操作 `Array` 时,返回的 `Array` 始终是一个副本 —— 即使返回了所有元素也是一样。 - * * When operating on a blank value, the pipe returns the blank value. * - * 当操作空白值时,该管道也会返回空白值。 - * * ### List Example * - * ### 列表范例 - * * This `ngFor` example: * - * `ngFor` 例子: - * * {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_list'} * * produces the following: * - * 生成下列内容: - * * ```html *
  • b
  • *
  • c
  • @@ -54,8 +40,6 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; * * ### String Examples * - * ### 字符串范例 - * * {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_string'} * * @publicApi @@ -64,47 +48,18 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; export class SlicePipe implements PipeTransform { /** * @param value a list or a string to be sliced. - * - * 要截取的列表或字符串。 - * * @param start the starting index of the subset to return: - * - * 要返回的子集的初始索引: - * * - **a positive integer**: return the item at `start` index and all items after * in the list or string expression. - * - * **一个正整数**:从列表或字符串表达式中返回从 `start` 索引处及之后的所有条目。 - * * - **a negative integer**: return the item at `start` index from the end and all items after * in the list or string expression. - * - * **一个负整数**:从列表或字符串表达式中返回从结尾开始的第 `start` 索引处及之后的所有条目。 - * * - **if positive and greater than the size of the expression**: return an empty list or * string. - * - * **如果是正数而且大于表达式的条目数**:返回空列表或空字符串。 - * * - **if negative and greater than the size of the expression**: return entire list or string. - * - * **如果是复数而且大于表达式的条目数**:返回整个列表或字符串。 - * * @param end the ending index of the subset to return: - * - * 所要返回的子集的结尾索引: - * * - **omitted**: return all items until the end. - * - * **省略**:返回结尾之前的全部条目。 - * * - **if positive**: return all items before `end` index of the list or string. - * - * **如果为正数**:从列表或字符串中返回 `end` 索引之前的所有条目。 - * * - **if negative**: return all items before `end` index from the end of the list or string. - * - * **如果为负数**:从列表或字符串中返回 `end` 索引之前的所有条目。 */ transform(value: ReadonlyArray, start: number, end?: number): Array; transform(value: null|undefined, start: number, end?: number): null; diff --git a/packages/common/src/platform_id.ts b/packages/common/src/platform_id.ts index f944c4e6c1..eaf25fb31c 100644 --- a/packages/common/src/platform_id.ts +++ b/packages/common/src/platform_id.ts @@ -13,9 +13,6 @@ export const PLATFORM_WORKER_UI_ID = 'browserWorkerUi'; /** * Returns whether a platform id represents a browser platform. - * - * 返回平台 ID 是否代表浏览器平台。 - * * @publicApi */ export function isPlatformBrowser(platformId: Object): boolean { @@ -24,9 +21,6 @@ export function isPlatformBrowser(platformId: Object): boolean { /** * Returns whether a platform id represents a server platform. - * - * 返回平台 ID 是否代表服务器平台。 - * * @publicApi */ export function isPlatformServer(platformId: Object): boolean { @@ -35,9 +29,6 @@ export function isPlatformServer(platformId: Object): boolean { /** * Returns whether a platform id represents a web worker app platform. - * - * 返回平台 ID 是否代表 Web Worker 应用平台。 - * * @publicApi */ export function isPlatformWorkerApp(platformId: Object): boolean { @@ -46,9 +37,6 @@ export function isPlatformWorkerApp(platformId: Object): boolean { /** * Returns whether a platform id represents a web worker UI platform. - * - * 返回平台 ID 是否代表 Web Worker UI 平台。 - * * @publicApi */ export function isPlatformWorkerUi(platformId: Object): boolean { diff --git a/packages/common/src/viewport_scroller.ts b/packages/common/src/viewport_scroller.ts index 6569255e35..a8061f62d0 100644 --- a/packages/common/src/viewport_scroller.ts +++ b/packages/common/src/viewport_scroller.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ErrorHandler, ɵɵdefineInjectable, ɵɵinject} from '@angular/core'; +import {ɵɵdefineInjectable, ɵɵinject} from '@angular/core'; import {DOCUMENT} from './dom_tokens'; @@ -15,66 +15,41 @@ import {DOCUMENT} from './dom_tokens'; /** * Defines a scroll position manager. Implemented by `BrowserViewportScroller`. * - * 定义滚动位置管理器。由 `BrowserViewportScroller` 实现。 - * * @publicApi */ export abstract class ViewportScroller { // De-sugared tree-shakable injection // See #23917 /** @nocollapse */ - static ɵprov = ɵɵdefineInjectable({ + static ɵprov = /** @pureOrBreakMyCode */ ɵɵdefineInjectable({ token: ViewportScroller, providedIn: 'root', - factory: () => new BrowserViewportScroller(ɵɵinject(DOCUMENT), window, ɵɵinject(ErrorHandler)) + factory: () => new BrowserViewportScroller(ɵɵinject(DOCUMENT), window) }); /** * Configures the top offset used when scrolling to an anchor. - * - * 配置滚动到锚点时使用的顶部偏移量。 - * * @param offset A position in screen coordinates (a tuple with x and y values) * or a function that returns the top offset position. * - * 屏幕坐标中的位置(具有 x 和 y 值的元组)或返回顶部偏移位置的函数。 - * */ abstract setOffset(offset: [number, number]|(() => [number, number])): void; /** * Retrieves the current scroll position. - * - * 检索当前滚动位置。 - * * @returns A position in screen coordinates (a tuple with x and y values). - * - * 屏幕坐标中的位置(具有 x 和 y 值的元组)。 - * */ abstract getScrollPosition(): [number, number]; /** * Scrolls to a specified position. - * - * 滚动到指定位置。 - * * @param position A position in screen coordinates (a tuple with x and y values). - * - * 屏幕坐标中的位置(具有 x 和 y 值的元组)。 - * */ abstract scrollToPosition(position: [number, number]): void; /** * Scrolls to an anchor element. - * - * 滚动到锚点元素。 - * * @param anchor The ID of the anchor element. - * - * 锚点元素的 ID。 - * */ abstract scrollToAnchor(anchor: string): void; @@ -82,9 +57,6 @@ export abstract class ViewportScroller { * Disables automatic scroll restoration provided by the browser. * See also [window.history.scrollRestoration * info](https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration). - * - * 禁用浏览器提供的自动滚动恢复功能。另请参见 [window.history.scrollRestoration 信息](https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration)。 - * */ abstract setHistoryScrollRestoration(scrollRestoration: 'auto'|'manual'): void; } @@ -95,7 +67,7 @@ export abstract class ViewportScroller { export class BrowserViewportScroller implements ViewportScroller { private offset: () => [number, number] = () => [0, 0]; - constructor(private document: any, private window: any, private errorHandler: ErrorHandler) {} + constructor(private document: Document, private window: Window) {} /** * Configures the top offset used when scrolling to an anchor. @@ -134,17 +106,33 @@ export class BrowserViewportScroller implements ViewportScroller { } /** - * Scrolls to an anchor element. - * @param anchor The ID of the anchor element. + * Scrolls to an element and attempts to focus the element. + * + * Note that the function name here is misleading in that the target string may be an ID for a + * non-anchor element. + * + * @param target The ID of an element or name of the anchor. + * + * @see https://html.spec.whatwg.org/#the-indicated-part-of-the-document + * @see https://html.spec.whatwg.org/#scroll-to-fragid */ - scrollToAnchor(anchor: string): void { - if (this.supportsScrolling()) { - const elSelected = - this.document.getElementById(anchor) || this.document.getElementsByName(anchor)[0]; - if (elSelected) { - this.scrollToElement(elSelected); - } + scrollToAnchor(target: string): void { + if (!this.supportsScrolling()) { + return; } + // TODO(atscott): The correct behavior for `getElementsByName` would be to also verify that the + // element is an anchor. However, this could be considered a breaking change and should be + // done in a major version. + const elSelected: HTMLElement|undefined = + this.document.getElementById(target) ?? this.document.getElementsByName(target)[0]; + if (elSelected === undefined) { + return; + } + + this.scrollToElement(elSelected); + // After scrolling to the element, the spec dictates that we follow the focus steps for the + // target. Rather than following the robust steps, simply attempt focus. + this.attemptFocus(elSelected); } /** @@ -159,7 +147,13 @@ export class BrowserViewportScroller implements ViewportScroller { } } - private scrollToElement(el: any): void { + /** + * Scrolls to an element using the native offset and the specified offset set on this scroller. + * + * The offset can be used when we know that there is a floating header and scrolling naively to an + * element (ex: `scrollIntoView`) leaves the element hidden behind the floating header. + */ + private scrollToElement(el: HTMLElement): void { const rect = el.getBoundingClientRect(); const left = rect.left + this.window.pageXOffset; const top = rect.top + this.window.pageYOffset; @@ -167,6 +161,21 @@ export class BrowserViewportScroller implements ViewportScroller { this.window.scrollTo(left - offset[0], top - offset[1]); } + /** + * Calls `focus` on the `focusTarget` and returns `true` if the element was focused successfully. + * + * If `false`, further steps may be necessary to determine a valid substitute to be focused + * instead. + * + * @see https://html.spec.whatwg.org/#get-the-focusable-area + * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/focus + * @see https://html.spec.whatwg.org/#focusable-area + */ + private attemptFocus(focusTarget: HTMLElement): boolean { + focusTarget.focus(); + return this.document.activeElement === focusTarget; + } + /** * We only support scroll restoration when we can get a hold of window. * This means that we do not support this behavior when running in a web worker. diff --git a/packages/common/test/directives/ng_template_outlet_spec.ts b/packages/common/test/directives/ng_template_outlet_spec.ts index 865f8c40da..ee3132180e 100644 --- a/packages/common/test/directives/ng_template_outlet_spec.ts +++ b/packages/common/test/directives/ng_template_outlet_spec.ts @@ -29,7 +29,7 @@ describe('NgTemplateOutlet', () => { beforeEach(() => { TestBed.configureTestingModule({ - declarations: [TestComponent, CaptureTplRefs, DestroyableCmpt], + declarations: [TestComponent, CaptureTplRefs, DestroyableCmpt, MultiContextComponent], imports: [CommonModule], providers: [DestroyedSpyService] }); @@ -142,7 +142,7 @@ describe('NgTemplateOutlet', () => { expect(spyService.destroyed).toBeFalsy(); }); - it('should recreate embedded view when context shape changes', () => { + it('should update but not destroy embedded view when context shape changes', () => { const template = `:{{foo}}` + ``; @@ -155,7 +155,7 @@ describe('NgTemplateOutlet', () => { fixture.componentInstance.context = {foo: 'baz', other: true}; detectChangesAndExpectText('Content to destroy:baz'); - expect(spyService.destroyed).toBeTruthy(); + expect(spyService.destroyed).toBeFalsy(); }); it('should destroy embedded view when context value changes and templateRef becomes undefined', () => { @@ -241,6 +241,27 @@ describe('NgTemplateOutlet', () => { detectChangesAndExpectText('foo'); }).not.toThrow(); })); + + it('should not mutate context object if two contexts with an identical shape are swapped', () => { + fixture = TestBed.createComponent(MultiContextComponent); + const {componentInstance, nativeElement} = fixture; + componentInstance.context1 = {name: 'one'}; + componentInstance.context2 = {name: 'two'}; + fixture.detectChanges(); + + expect(nativeElement.textContent.trim()).toBe('one | two'); + expect(componentInstance.context1).toEqual({name: 'one'}); + expect(componentInstance.context2).toEqual({name: 'two'}); + + const temp = componentInstance.context1; + componentInstance.context1 = componentInstance.context2; + componentInstance.context2 = temp; + fixture.detectChanges(); + + expect(nativeElement.textContent.trim()).toBe('two | one'); + expect(componentInstance.context1).toEqual({name: 'two'}); + expect(componentInstance.context2).toEqual({name: 'one'}); + }); }); @Injectable() @@ -271,6 +292,19 @@ class TestComponent { value = 'bar'; } +@Component({ + template: ` + {{name}} + + | + + ` +}) +class MultiContextComponent { + context1: {name: string}|undefined; + context2: {name: string}|undefined; +} + function createTestComponent(template: string): ComponentFixture { return TestBed.overrideComponent(TestComponent, {set: {template: template}}) .configureTestingModule({schemas: [NO_ERRORS_SCHEMA]}) diff --git a/packages/common/test/i18n/format_date_spec.ts b/packages/common/test/i18n/format_date_spec.ts index 5931f7222f..c1fda24481 100644 --- a/packages/common/test/i18n/format_date_spec.ts +++ b/packages/common/test/i18n/format_date_spec.ts @@ -9,6 +9,7 @@ import localeAr from '@angular/common/locales/ar'; import localeDe from '@angular/common/locales/de'; import localeEn from '@angular/common/locales/en'; import localeEnExtra from '@angular/common/locales/extra/en'; +import localeFi from '@angular/common/locales/fi'; import localeHu from '@angular/common/locales/hu'; import localeSr from '@angular/common/locales/sr'; import localeTh from '@angular/common/locales/th'; @@ -60,6 +61,8 @@ describe('Format date', () => { describe('formatDate', () => { const isoStringWithoutTime = '2015-01-01'; + const isoStringWithoutTimeOrDate = '2015-01'; + const isoStringWithoutTimeOrDateOrMonth = '2015'; const defaultFormat = 'mediumDate'; let date: Date; @@ -76,6 +79,7 @@ describe('Format date', () => { ɵregisterLocaleData(localeSr); ɵregisterLocaleData(localeTh); ɵregisterLocaleData(localeAr); + ɵregisterLocaleData(localeFi); }); afterAll(() => ɵunregisterLocaleData()); @@ -114,6 +118,12 @@ describe('Format date', () => { W: '3', d: '15', dd: '15', + c: '1', + cc: '1', + ccc: 'Mon', + cccc: 'Monday', + ccccc: 'M', + cccccc: 'Mo', E: 'Mon', EE: 'Mon', EEE: 'Mon', @@ -176,6 +186,12 @@ describe('Format date', () => { W: '1', d: '1', dd: '01', + c: '4', + cc: '4', + ccc: 'Thu', + cccc: 'Thursday', + ccccc: 'T', + cccccc: 'Th', E: 'Thu', EE: 'Thu', EEE: 'Thu', @@ -231,6 +247,16 @@ describe('Format date', () => { expectDateFormatAs(isoStringWithoutTime, pattern, isoStringWithoutTimeFixtures[pattern]); }); + Object.keys(isoStringWithoutTimeFixtures).forEach((pattern: string) => { + expectDateFormatAs( + isoStringWithoutTimeOrDate, pattern, isoStringWithoutTimeFixtures[pattern]); + }); + + Object.keys(isoStringWithoutTimeFixtures).forEach((pattern: string) => { + expectDateFormatAs( + isoStringWithoutTimeOrDateOrMonth, pattern, isoStringWithoutTimeFixtures[pattern]); + }); + const nightTime = new Date(2015, 5, 15, 2, 3, 1, 550); Object.keys(midnightCrossingPeriods).forEach(pattern => { expectDateFormatAs(nightTime, pattern, midnightCrossingPeriods[pattern]); @@ -335,6 +361,32 @@ describe('Format date', () => { .toEqual('10:14 AM'); }); + // The following test is disabled because backwards compatibility requires that date-only ISO + // strings are parsed with the local timezone. + + // it('should create UTC date objects when an ISO string is passed with no time components', + // () => { + // expect(formatDate('2019-09-20', `MMM d, y, h:mm:ss a ZZZZZ`, ɵDEFAULT_LOCALE_ID)) + // .toEqual('Sep 20, 2019, 12:00:00 AM Z'); + // }); + + // This test is to ensure backward compatibility for parsing date-only ISO strings. + it('should create local timezone date objects when an ISO string is passed with no time components', + () => { + // Dates created with individual components are evaluated against the local timezone. See + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date#Individual_date_and_time_component_values + const localDate = new Date(2019, 8, 20, 0, 0, 0, 0); + expect(formatDate('2019-09-20', `MMM d, y, h:mm:ss a ZZZZZ`, ɵDEFAULT_LOCALE_ID)) + .toEqual(formatDate(localDate, `MMM d, y, h:mm:ss a ZZZZZ`, ɵDEFAULT_LOCALE_ID)); + }); + + it('should create local timezone date objects when an ISO string is passed with time components', + () => { + const localDate = new Date(2019, 8, 20, 0, 0, 0, 0); + expect(formatDate('2019-09-20T00:00:00', `MMM d, y, h:mm:ss a ZZZZZ`, ɵDEFAULT_LOCALE_ID)) + .toEqual(formatDate(localDate, `MMM d, y, h:mm:ss a ZZZZZ`, ɵDEFAULT_LOCALE_ID)); + }); + it('should remove bidi control characters', () => expect(formatDate(date, 'MM/dd/yyyy', ɵDEFAULT_LOCALE_ID)!.length).toEqual(10)); @@ -377,6 +429,22 @@ describe('Format date', () => { expect(formatDate('2013-12-29', 'YYYY', 'en')).toEqual('2014'); expect(formatDate('2010-01-02', 'YYYY', 'en')).toEqual('2009'); expect(formatDate('2010-01-04', 'YYYY', 'en')).toEqual('2010'); + expect(formatDate('0049-01-01', 'YYYY', 'en')).toEqual('0048'); + expect(formatDate('0049-01-04', 'YYYY', 'en')).toEqual('0049'); }); + + // https://github.com/angular/angular/issues/40377 + it('should format date with year between 0 and 99 correctly', () => { + expect(formatDate('0098-01-11', 'YYYY', ɵDEFAULT_LOCALE_ID)).toEqual('0098'); + expect(formatDate('0099-01-11', 'YYYY', ɵDEFAULT_LOCALE_ID)).toEqual('0099'); + expect(formatDate('0100-01-11', 'YYYY', ɵDEFAULT_LOCALE_ID)).toEqual('0100'); + expect(formatDate('0001-01-11', 'YYYY', ɵDEFAULT_LOCALE_ID)).toEqual('0001'); + expect(formatDate('0000-01-11', 'YYYY', ɵDEFAULT_LOCALE_ID)).toEqual('0000'); + }); + + // https://github.com/angular/angular/issues/26922 + it('should support fullDate in finnish, which uses standalone week day', () => { + expect(formatDate(date, 'fullDate', 'fi')).toMatch('maanantai 15. kesäkuuta 2015'); + }); }); }); diff --git a/packages/common/test/pipes/async_pipe_spec.ts b/packages/common/test/pipes/async_pipe_spec.ts index 9d57ab5f3c..19cf051ed8 100644 --- a/packages/common/test/pipes/async_pipe_spec.ts +++ b/packages/common/test/pipes/async_pipe_spec.ts @@ -129,6 +129,16 @@ import {SpyChangeDetectorRef} from '../spies'; }); }); + describe('Subscribable', () => { + it('should infer the type from the subscribable', () => { + const ref = new SpyChangeDetectorRef() as any; + const pipe = new AsyncPipe(ref); + const emitter = new EventEmitter<{name: 'T'}>(); + // The following line will fail to compile if the type cannot be inferred. + const name = pipe.transform(emitter)?.name; + }); + }); + describe('Promise', () => { const message = {}; let pipe: AsyncPipe; diff --git a/packages/common/test/viewport_scroller_spec.ts b/packages/common/test/viewport_scroller_spec.ts index 90713e62fd..58ffb5bb1d 100644 --- a/packages/common/test/viewport_scroller_spec.ts +++ b/packages/common/test/viewport_scroller_spec.ts @@ -10,19 +10,17 @@ import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {BrowserViewportScroller, ViewportScroller} from '../src/viewport_scroller'; describe('BrowserViewportScroller', () => { - let scroller: ViewportScroller; - let documentSpy: any; - let windowSpy: any; - - beforeEach(() => { - windowSpy = - jasmine.createSpyObj('window', ['history', 'scrollTo', 'pageXOffset', 'pageYOffset']); - windowSpy.history.scrollRestoration = 'auto'; - documentSpy = jasmine.createSpyObj('document', ['getElementById', 'getElementsByName']); - scroller = new BrowserViewportScroller(documentSpy, windowSpy, null!); - }); - describe('setHistoryScrollRestoration', () => { + let scroller: ViewportScroller; + let windowSpy: any; + + beforeEach(() => { + windowSpy = + jasmine.createSpyObj('window', ['history', 'scrollTo', 'pageXOffset', 'pageYOffset']); + windowSpy.history.scrollRestoration = 'auto'; + scroller = new BrowserViewportScroller(document, windowSpy); + }); + function createNonWritableScrollRestoration() { Object.defineProperty(windowSpy.history, 'scrollRestoration', { value: 'auto', @@ -43,34 +41,47 @@ describe('BrowserViewportScroller', () => { }); describe('scrollToAnchor', () => { + // Testing scroll behavior does not make sense outside a browser + if (isNode) return; const anchor = 'anchor'; - const el = document.createElement('a'); + let tallItem: HTMLDivElement; + let el: HTMLAnchorElement; + let scroller: BrowserViewportScroller; - it('should only call getElementById when an element is found by id', () => { - documentSpy.getElementById.and.returnValue(el); - spyOn(scroller, 'scrollToElement'); - scroller.scrollToAnchor(anchor); - expect(documentSpy.getElementById).toHaveBeenCalledWith(anchor); - expect(documentSpy.getElementsByName).not.toHaveBeenCalled(); - expect((scroller as any).scrollToElement).toHaveBeenCalledWith(el); + beforeEach(() => { + scroller = new BrowserViewportScroller(document, window); + scroller.scrollToPosition([0, 0]); + + tallItem = document.createElement('div'); + tallItem.style.height = '3000px'; + document.body.appendChild(tallItem); + + el = document.createElement('a'); + el.innerText = 'some link'; + el.href = '#'; + document.body.appendChild(el); }); - it('should call getElementById and getElementsByName when an element is found by name', () => { - documentSpy.getElementsByName.and.returnValue([el]); - spyOn(scroller, 'scrollToElement'); - scroller.scrollToAnchor(anchor); - expect(documentSpy.getElementById).toHaveBeenCalledWith(anchor); - expect(documentSpy.getElementsByName).toHaveBeenCalledWith(anchor); - expect((scroller as any).scrollToElement).toHaveBeenCalledWith(el); + afterEach(() => { + document.body.removeChild(tallItem); + document.body.removeChild(el); }); - it('should not call scrollToElement when an element is not found by its id or its name', () => { - documentSpy.getElementsByName.and.returnValue([]); - spyOn(scroller, 'scrollToElement'); + it('should scroll when element with matching id is found', () => { + el.id = anchor; scroller.scrollToAnchor(anchor); - expect(documentSpy.getElementById).toHaveBeenCalledWith(anchor); - expect(documentSpy.getElementsByName).toHaveBeenCalledWith(anchor); - expect((scroller as any).scrollToElement).not.toHaveBeenCalled(); + expect(scroller.getScrollPosition()[1]).not.toEqual(0); + }); + + it('should scroll when anchor with matching name is found', () => { + el.name = anchor; + scroller.scrollToAnchor(anchor); + expect(scroller.getScrollPosition()[1]).not.toEqual(0); + }); + + it('should not scroll when no matching element is found', () => { + scroller.scrollToAnchor(anchor); + expect(scroller.getScrollPosition()[1]).toEqual(0); }); }); }); diff --git a/packages/common/testing/src/location_mock.ts b/packages/common/testing/src/location_mock.ts index 343a4806e3..e7f5595ec9 100644 --- a/packages/common/testing/src/location_mock.ts +++ b/packages/common/testing/src/location_mock.ts @@ -13,8 +13,6 @@ import {SubscriptionLike} from 'rxjs'; /** * A spy for {@link Location} that allows tests to fire simulated location events. * - * {@link Location} 的间谍对象,它允许测试者触发模拟的位置事件。 - * * @publicApi */ @Injectable() diff --git a/packages/common/testing/src/mock_location_strategy.ts b/packages/common/testing/src/mock_location_strategy.ts index bc777d6f93..646534b7af 100644 --- a/packages/common/testing/src/mock_location_strategy.ts +++ b/packages/common/testing/src/mock_location_strategy.ts @@ -15,8 +15,6 @@ import {EventEmitter, Injectable} from '@angular/core'; * A mock implementation of {@link LocationStrategy} that allows tests to fire simulated * location events. * - * {@link LocationStrategy} 的模拟实现,允许测试触发模拟的 location 事件。 - * * @publicApi */ @Injectable() diff --git a/packages/common/testing/src/mock_platform_location.ts b/packages/common/testing/src/mock_platform_location.ts index db21f04d82..e4f3f33607 100644 --- a/packages/common/testing/src/mock_platform_location.ts +++ b/packages/common/testing/src/mock_platform_location.ts @@ -81,8 +81,6 @@ function parseUrl(urlStr: string, baseHref: string) { /** * Mock platform location config * - * 模拟平台的 location 配置 - * * @publicApi */ export interface MockPlatformLocationConfig { @@ -93,8 +91,6 @@ export interface MockPlatformLocationConfig { /** * Provider for mock platform location config * - * 模拟平台 location 配置的提供者 - * * @publicApi */ export const MOCK_PLATFORM_LOCATION_CONFIG = @@ -103,8 +99,6 @@ export const MOCK_PLATFORM_LOCATION_CONFIG = /** * Mock implementation of URL state. * - * URL 状态的模拟实现。 - * * @publicApi */ @Injectable() @@ -159,13 +153,15 @@ export class MockPlatformLocation implements PlatformLocation { return this.baseHref; } - onPopState(fn: LocationChangeListener): void { + onPopState(fn: LocationChangeListener): VoidFunction { // No-op: a state stack is not implemented, so // no events will ever come. + return () => {}; } - onHashChange(fn: LocationChangeListener): void { - this.hashUpdate.subscribe(fn); + onHashChange(fn: LocationChangeListener): VoidFunction { + const subscription = this.hashUpdate.subscribe(fn); + return () => subscription.unsubscribe(); } get href(): string { diff --git a/packages/common/upgrade/src/location_shim.ts b/packages/common/upgrade/src/location_shim.ts index 284caed484..4e46269a06 100644 --- a/packages/common/upgrade/src/location_shim.ts +++ b/packages/common/upgrade/src/location_shim.ts @@ -26,12 +26,8 @@ const DEFAULT_PORTS: {[key: string]: number} = { * Location service that provides a drop-in replacement for the $location service * provided in AngularJS. * - * 位置服务,提供对 AngularJS 中提供的 $location 服务的直接替代品。 - * * @see [Using the Angular Unified Location Service](guide/upgrade#using-the-unified-angular-location-service) * - * [使用 Angular 统一位置服务](guide/upgrade#using-the-unified-angular-location-service) - * * @publicApi */ export class $locationShim { @@ -294,9 +290,6 @@ 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. - * - * 此函数模拟 AngularJS 中的 $browser.state() 函数。除非使用深度相等性检查进行更改,否则它将导致 history.state 被缓存。 - * */ private browserState(): unknown { return this.cachedState; @@ -347,22 +340,12 @@ export class $locationShim { * `$locationChangeSuccess` events which fire when AngularJS updates its internally-referenced * version of the browser URL. * - * 注册对 URL 更改的监听器。该 API 用于捕获 AngularJS 框架执行的更新。`$locationChangeStart` 和 `$locationChangeSuccess` 事件的子集,这些事件在 AngularJS 更新其内部引用的浏览器 URL 版本时触发。 - * * It's possible for `$locationChange` events to happen, but for the browser URL * (window.location) to remain unchanged. This `onChange` callback will fire only when AngularJS * actually updates the browser URL (window.location). * - * `$locationChange` 事件有可能发生,但浏览器的 URL(window.location)保持不变。仅当 AngularJS 实际上更新浏览器 URL(window.location)时,才会触发此 `onChange` - * * @param fn The callback function that is triggered for the listener when the URL changes. - * - * URL 更改时为监听器触发的回调函数。 - * * @param err The callback function that is triggered when an error occurs. - * - * 发生错误时触发的回调函数。 - * */ onChange( fn: (url: string, state: unknown, oldUrl: string, oldState: unknown) => void, @@ -385,12 +368,7 @@ export class $locationShim { /** * Parses the provided URL, and sets the current URL to the parsed result. * - * 解析此 URL,并将当前 URL 设置为解析结果。 - * * @param url The URL string. - * - * URL 字符串。 - * */ $$parse(url: string) { let pathUrl: string|undefined; @@ -415,16 +393,8 @@ export class $locationShim { /** * Parses the provided URL and its relative URL. * - * 解析提供的 URL 及其相对 URL。 - * * @param url The full URL string. - * - * 完整的 URL 字符串。 - * * @param relHref A URL string relative to the full URL string. - * - * 相对于完整 URL 字符串的 URL 字符串。 - * */ $$parseLinkUrl(url: string, relHref?: string|null): boolean { // When relHref is passed, it should be a hash and is handled separately @@ -476,7 +446,6 @@ export class $locationShim { * rules specified in * [RFC 3986](https://tools.ietf.org/html/rfc3986). * - * 检索完整的 URL 表示形式,其中包含根据 [RFC 3986 中](https://tools.ietf.org/html/rfc3986) 指定的规则编码过的所有段。 * * ```js * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo @@ -492,8 +461,6 @@ export class $locationShim { * Retrieves the current URL, or sets a new URL. When setting a URL, * changes the path, search, and hash, and returns a reference to its own instance. * - * 检索当前 URL,或设置新 URL。设置 URL 时,更改路径、搜索和哈希,并返回对其自身实例的引用。 - * * ```js * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo * let url = $location.url(); @@ -524,8 +491,6 @@ export class $locationShim { /** * Retrieves the protocol of the current URL. * - * 检索当前 URL 的协议。 - * * ```js * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo * let protocol = $location.protocol(); @@ -539,12 +504,9 @@ export class $locationShim { /** * Retrieves the protocol of the current URL. * - * 检索当前 URL 的协议。 - * * In contrast to the non-AngularJS version `location.host` which returns `hostname:port`, this * returns the `hostname` portion only. * - * 与非 AngularJS 版本不同,其 `location.host` 会返回 `hostname:port` ,而这里会返回 `hostname` 部分。 * * ```js * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo @@ -565,8 +527,6 @@ export class $locationShim { /** * Retrieves the port of the current URL. * - * 检索当前 URL 的端口。 - * * ```js * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo * let port = $location.port(); @@ -581,13 +541,9 @@ export class $locationShim { * Retrieves the path of the current URL, or changes the path and returns a reference to its own * instance. * - * 检索当前 URL 的路径,或更改路径并返回对其自身实例的引用。 - * * Paths should always begin with forward slash (/). This method adds the forward slash * if it is missing. * - * 路径应始终以正斜杠(/)开头。如果缺少此斜杠,则此方法将添加它。 - * * ```js * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo * let path = $location.path(); @@ -615,7 +571,6 @@ export class $locationShim { * Retrieves a map of the search parameters of the current URL, or changes a search * part and returns a reference to its own instance. * - * 检索当前 URL 的搜索参数的映射,或更改搜索部分并返回对其自身实例的引用。 * * ```js * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo @@ -630,41 +585,25 @@ export class $locationShim { * @param {string|Object.|Object.>} search New search params - string or * hash object. * - * 新的搜索参数-字符串或哈希对象。 - * * When called with a single argument the method acts as a setter, setting the `search` component * of `$location` to the specified value. * - * 当使用单个参数调用它时,该方法会充当设置器,将 `$location` 的 `search` 组件设置为指定值。 - * * If the argument is a hash object containing an array of values, these values will be encoded * as duplicate search parameters in the URL. * - * 如果参数是包含值数组的哈希对象,则这些值将被编码为 URL 中的重复搜索参数。 - * * @param {(string|Number|Array|boolean)=} paramValue If `search` is a string or number, * then `paramValue` * will override only a single search property. * - * 如果 `search` 是字符串或数字,则 `paramValue` 将仅覆盖单个搜索属性。 - * * If `paramValue` is an array, it will override the property of the `search` component of * `$location` specified via the first argument. * - * 如果 `paramValue` 是一个数组,它将覆盖通过第一个参数指定的 `$location` 的 `search` 的部分。 - * * If `paramValue` is `null`, the property specified via the first argument will be deleted. * - * 如果 `paramValue` 为 `null` ,则将删除通过第一个参数指定的属性。 - * * If `paramValue` is `true`, the property specified via the first argument will be added with no * value nor trailing equal sign. * - * 如果 `paramValue` 为 `true` ,则将通过第一个参数指定的属性添加为无值或结尾等号。 - * * @return {Object} The parsed `search` object of the current URL, or the changed `search` object. - * - * 当前 URL 的已解析 `search` 对象,或更改后的 `search` 对象。 */ search(): {[key: string]: unknown}; search(search: string|number|{[key: string]: unknown}): this; @@ -714,8 +653,6 @@ export class $locationShim { * Retrieves the current hash fragment, or changes the hash fragment and returns a reference to * its own instance. * - * 检索当前哈希片段,或更改哈希片段并返回对其自身实例的引用。 - * * ```js * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue * let hash = $location.hash(); @@ -738,9 +675,6 @@ export class $locationShim { /** * Changes to `$location` during the current `$digest` will replace the current * history record, instead of adding a new one. - * - * 当前 `$digest` 期间对 `$location` 更改将替换当前历史记录,而不是添加新的记录。 - * */ replace(): this { this.$$replace = true; @@ -750,18 +684,13 @@ export class $locationShim { /** * Retrieves the history state object when called without any parameter. * - * 当不带任何参数调用时将检索历史状态对象。 - * * Change the history state object when called with one parameter and return `$location`. * The state object is later passed to `pushState` or `replaceState`. * - * 使用一个参数调用时将更改历史状态对象,并返回 `$location` 。状态对象随后传递给 `pushState` 或 `replaceState` 。 - * * This method is supported only in HTML5 mode and only in browsers supporting * the HTML5 History API methods such as `pushState` and `replaceState`. If you need to support * older browsers (like Android < 4.0), don't use this method. * - * 仅在 HTML5 模式下以及在支持 HTML5 History API 方法(例如 `pushState` 和 `replaceState`)的浏览器中才支持此方法。如果你需要支持较旧的浏览器(例如 Android <4.0),请不要使用此方法。 */ state(): unknown; state(state: unknown): this; @@ -779,8 +708,6 @@ export class $locationShim { * The factory function used to create an instance of the `$locationShim` in Angular, * and provides an API-compatiable `$locationProvider` for AngularJS. * - * Angular 中用于创建 `$locationShim` 实例的工厂函数,并为 AngularJS 提供与 API 兼容的 `$locationProvider`。 - * * @publicApi */ export class $locationShimProvider { @@ -791,9 +718,6 @@ export class $locationShimProvider { /** * Factory method that returns an instance of the $locationShim - * - * 返回 $locationShim 实例的工厂方法 - * */ $get() { return new $locationShim( @@ -804,9 +728,6 @@ export class $locationShimProvider { /** * Stub method used to keep API compatible with AngularJS. This setting is configured through * the LocationUpgradeModule's `config` method in your Angular app. - * - * 用于使 API 与 AngularJS 兼容的存根方法。此设置是通过 Angular 应用中 LocationUpgradeModule 的 `config` 方法配置的。 - * */ hashPrefix(prefix?: string) { throw new Error('Configure LocationUpgrade through LocationUpgradeModule.config method.'); @@ -815,9 +736,6 @@ export class $locationShimProvider { /** * Stub method used to keep API compatible with AngularJS. This setting is configured through * the LocationUpgradeModule's `config` method in your Angular app. - * - * 用于使 API 与 AngularJS 兼容的存根方法。此设置是通过 Angular 应用中 LocationUpgradeModule 的 `config` 方法配置的。 - * */ html5Mode(mode?: any) { throw new Error('Configure LocationUpgrade through LocationUpgradeModule.config method.'); diff --git a/packages/common/upgrade/src/location_upgrade_module.ts b/packages/common/upgrade/src/location_upgrade_module.ts index 4c9001eb00..d8405b27c8 100644 --- a/packages/common/upgrade/src/location_upgrade_module.ts +++ b/packages/common/upgrade/src/location_upgrade_module.ts @@ -17,45 +17,28 @@ import {AngularJSUrlCodec, UrlCodec} from './params'; /** * Configuration options for LocationUpgrade. * - * LocationUpgrade 的配置选项。 - * * @publicApi */ export interface LocationUpgradeConfig { /** * Configures whether the location upgrade module should use the `HashLocationStrategy` * or the `PathLocationStrategy` - * - * 配置 location 升级模块应使用 `HashLocationStrategy` 还是 `PathLocationStrategy` - * */ useHash?: boolean; /** * Configures the hash prefix used in the URL when using the `HashLocationStrategy` - * - * 配置使用 `HashLocationStrategy` 时 URL 中要使用的哈希前缀 - * */ hashPrefix?: string; /** * Configures the URL codec for encoding and decoding URLs. Default is the `AngularJSCodec` - * - * 配置 URL 编解码器以对 URL 进行编码和解码。默认为 `AngularJSCodec` - * */ urlCodec?: typeof UrlCodec; /** * Configures the base href when used in server-side rendered applications - * - * 配置在服务器端渲染的应用程序中使用时的 base href - * */ serverBaseHref?: string; /** * Configures the base href when used in client-side rendered applications - * - * 配置在客户端渲染的应用程序中使用时的 base href - * */ appBaseHref?: string; } @@ -63,8 +46,6 @@ export interface LocationUpgradeConfig { /** * A provider token used to configure the location upgrade module. * - * 提供者令牌,用于配置 location 升级模块。 - * * @publicApi */ export const LOCATION_UPGRADE_CONFIGURATION = @@ -75,12 +56,8 @@ const APP_BASE_HREF_RESOLVED = new InjectionToken('APP_BASE_HREF_RESOLVE /** * `NgModule` used for providing and configuring Angular's Unified Location Service for upgrading. * - * `NgModule` 用于提供和配置 Angular 的统一 location 服务以进行升级。 - * * @see [Using the Unified Angular Location Service](guide/upgrade#using-the-unified-angular-location-service) * - * [使用统一的 Angular location 服务](guide/upgrade#using-the-unified-angular-location-service) - * * @publicApi */ @NgModule({imports: [CommonModule]}) diff --git a/packages/common/upgrade/src/params.ts b/packages/common/upgrade/src/params.ts index 5e4a9ebb66..c832d619ff 100644 --- a/packages/common/upgrade/src/params.ts +++ b/packages/common/upgrade/src/params.ts @@ -9,92 +9,55 @@ /** * A codec for encoding and decoding URL parts. * - * 用于编码和解码 URL 部分的编解码器。 - * * @publicApi **/ export abstract class UrlCodec { /** * Encodes the path from the provided string * - * 解码所提供的字符串的路径 - * * @param path The path string - * - * 路径字符串 - * */ abstract encodePath(path: string): string; /** * Decodes the path from the provided string * - * 解码所提供的字符串的路径 - * * @param path The path string - * - * 路径字符串 - * */ abstract decodePath(path: string): string; /** * Encodes the search string from the provided string or object * - * 从所提供的字符串或对象中编码搜索字符串 - * * @param path The path string or object - * - * 路径字符串或对象 - * */ abstract encodeSearch(search: string|{[k: string]: unknown}): string; /** * Decodes the search objects from the provided string * - * 从所提供的字符串中解码搜索对象 - * * @param path The path string - * - * 路径字符串 - * */ abstract decodeSearch(search: string): {[k: string]: unknown}; /** * Encodes the hash from the provided string * - * 对所提供的字符串中的哈希进行编码 - * * @param path The hash string - * - * 哈希字符串 - * */ abstract encodeHash(hash: string): string; /** * Decodes the hash from the provided string * - * 从所提供的字符串中解码哈希 - * * @param path The hash string - * - * 哈希字符串 - * */ abstract decodeHash(hash: string): string; /** * Normalizes the URL from the provided string * - * 从所提供的字符串中标准化 URL - * * @param path The URL string - * - * URL 字符串 - * */ abstract normalize(href: string): string; @@ -102,57 +65,26 @@ export abstract class UrlCodec { /** * Normalizes the URL from the provided string, search, hash, and base URL parameters * - * 根据所提供的字符串、搜索、哈希和基本 URL 参数标准化 URL - * * @param path The URL path - * - * 网址路径 - * * @param search The search object - * - * 搜索对象 - * * @param hash The has string - * - * 哈希字符串 - * * @param baseUrl The base URL for the URL - * - * 此 URL 的基本 URL - * */ abstract normalize(path: string, search: {[k: string]: unknown}, hash: string, baseUrl?: string): string; /** * Checks whether the two strings are equal - * - * 检查两个字符串是否相等 - * * @param valA First string for comparison - * - * 要比较的第一个字符串 - * * @param valB Second string for comparison - * - * 要比较的第二个字符串 - * */ abstract areEqual(valA: string, valB: string): boolean; /** * Parses the URL string based on the base URL * - * 根据基本 URL 解析 URL 字符串 - * * @param url The full URL string - * - * 完整的 URL 字符串 - * * @param base The base for the URL - * - * URL 的 base 部分 - * */ abstract parse(url: string, base?: string): { href: string, @@ -170,8 +102,6 @@ export abstract class UrlCodec { * A `UrlCodec` that uses logic from AngularJS to serialize and parse URLs * and URL parameters. * - * 一个 `UrlCodec`,它使用 AngularJS 中的逻辑来序列化和解析 URL 和 URL 参数。 - * * @publicApi */ export class AngularJSUrlCodec implements UrlCodec { diff --git a/packages/common/upgrade/test/upgrade_location_test_module.ts b/packages/common/upgrade/test/upgrade_location_test_module.ts index 1598f0c587..f154080033 100644 --- a/packages/common/upgrade/test/upgrade_location_test_module.ts +++ b/packages/common/upgrade/test/upgrade_location_test_module.ts @@ -28,8 +28,6 @@ export interface LocationUpgradeTestingConfig { * * Is used in DI to configure the router. * - * 用于在 DI 中配置路由器。 - * * @publicApi */ export const LOC_UPGRADE_TEST_CONFIG = diff --git a/packages/compiler-cli/BUILD.bazel b/packages/compiler-cli/BUILD.bazel index 9942ad35b4..fe4758ee91 100644 --- a/packages/compiler-cli/BUILD.bazel +++ b/packages/compiler-cli/BUILD.bazel @@ -2,6 +2,10 @@ package(default_visibility = ["//visibility:public"]) load("//tools:defaults.bzl", "pkg_npm", "ts_api_guardian_test", "ts_config", "ts_library") +# Load ng_perf_flag explicitly from ng_perf.bzl as it's private API, and not exposed to other +# consumers of @angular/bazel. +load("//packages/bazel/src:ng_perf.bzl", "ng_perf_flag") + ts_config( name = "tsconfig", src = "tsconfig-build.json", @@ -33,6 +37,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/shims", "//packages/compiler-cli/src/ngtsc/translator", "//packages/compiler-cli/src/ngtsc/typecheck", + "//packages/compiler-cli/src/ngtsc/typecheck/api", "@npm//@bazel/typescript", "@npm//@types/node", "@npm//chokidar", @@ -60,6 +65,8 @@ pkg_npm( ], deps = [ ":compiler-cli", + "//packages/compiler-cli/linker", + "//packages/compiler-cli/linker/babel", "//packages/compiler-cli/ngcc", "//packages/compiler-cli/src/ngtsc/file_system/testing", "//packages/compiler-cli/src/ngtsc/logging/testing", @@ -85,3 +92,9 @@ ts_api_guardian_test( ], golden = "angular/goldens/public-api/compiler-cli/compiler_options.d.ts", ) + +# Controls whether the Ivy compiler produces performance traces as part of each build +ng_perf_flag( + name = "ng_perf", + build_setting_default = False, +) diff --git a/packages/compiler-cli/index.ts b/packages/compiler-cli/index.ts index e74cedd96b..1247ebc2f4 100644 --- a/packages/compiler-cli/index.ts +++ b/packages/compiler-cli/index.ts @@ -22,5 +22,6 @@ export {CompilerOptions as AngularCompilerOptions} from './src/transformers/api' export {ngToTsDiagnostic} from './src/transformers/util'; export {NgTscPlugin} from './src/ngtsc/tsc_plugin'; +export {NgtscProgram} from './src/ngtsc/program'; setFileSystem(new NodeJSFileSystem()); 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 24ff406205..8ac13126d0 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 @@ -63,9 +63,11 @@ describe('Ivy NgModule', () => { } const errorCode = ivyEnabled ? 'NG0200: ' : ''; + const errorLink = ivyEnabled ? '. Find more at https://angular.io/errors/NG0200' : ''; expect(() => createInjector(AModule)) .toThrowError(`${ - errorCode}Circular dependency in DI detected for AModule. Dependency path: AModule > BModule > AModule`); + errorCode}Circular dependency in DI detected for AModule. Dependency path: AModule > BModule > AModule${ + errorLink}`); }); it('merges imports and exports', () => { diff --git a/packages/compiler-cli/integrationtest/test/init.ts b/packages/compiler-cli/integrationtest/test/init.ts index 869005a110..658b708e69 100644 --- a/packages/compiler-cli/integrationtest/test/init.ts +++ b/packages/compiler-cli/integrationtest/test/init.ts @@ -8,8 +8,8 @@ // import zone.js from npm here because integration test will load zone.js // from built npm_package instead of source -import 'zone.js/dist/zone-node'; -import 'zone.js/dist/zone-testing'; +import 'zone.js/node'; +import 'zone.js/testing'; // Only needed to satisfy the check in core/src/util/decorators.ts // TODO(alexeagle): maybe remove that check? require('reflect-metadata'); diff --git a/packages/compiler-cli/linker/BUILD.bazel b/packages/compiler-cli/linker/BUILD.bazel index 0c9fb1309d..f114a02c97 100644 --- a/packages/compiler-cli/linker/BUILD.bazel +++ b/packages/compiler-cli/linker/BUILD.bazel @@ -9,7 +9,12 @@ ts_library( ]), deps = [ "//packages/compiler", + "//packages/compiler-cli/src/ngtsc/file_system", + "//packages/compiler-cli/src/ngtsc/logging", + "//packages/compiler-cli/src/ngtsc/sourcemaps", "//packages/compiler-cli/src/ngtsc/translator", + "@npm//@types/semver", + "@npm//semver", "@npm//typescript", ], ) diff --git a/packages/compiler-cli/linker/babel/BUILD.bazel b/packages/compiler-cli/linker/babel/BUILD.bazel index b5a15beef3..b8798073d9 100644 --- a/packages/compiler-cli/linker/babel/BUILD.bazel +++ b/packages/compiler-cli/linker/babel/BUILD.bazel @@ -10,6 +10,8 @@ ts_library( deps = [ "//packages/compiler", "//packages/compiler-cli/linker", + "//packages/compiler-cli/src/ngtsc/file_system", + "//packages/compiler-cli/src/ngtsc/logging", "//packages/compiler-cli/src/ngtsc/translator", "@npm//@babel/core", "@npm//@babel/types", diff --git a/packages/compiler-cli/linker/babel/src/ast/babel_ast_factory.ts b/packages/compiler-cli/linker/babel/src/ast/babel_ast_factory.ts index 811f8a9bac..70f4b408af 100644 --- a/packages/compiler-cli/linker/babel/src/ast/babel_ast_factory.ts +++ b/packages/compiler-cli/linker/babel/src/ast/babel_ast_factory.ts @@ -14,6 +14,10 @@ import {AstFactory, BinaryOperator, LeadingComment, ObjectLiteralProperty, Sourc * A Babel flavored implementation of the AstFactory. */ export class BabelAstFactory implements AstFactory { + constructor( + /** The absolute path to the source file being compiled. */ + private sourceUrl: string) {} + attachComments(statement: t.Statement, leadingComments: LeadingComment[]): void { // We must process the comments in reverse because `t.addComment()` will add new ones in front. for (let i = leadingComments.length - 1; i >= 0; i--) { @@ -138,9 +142,11 @@ export class BabelAstFactory implements AstFactory { if (sourceMapRange === null) { return node; } - // Note that the linker only works on a single file at a time, so there is no need to track the - // filename. Babel will just use the current filename in the source-map. node.loc = { + // Add in the filename so that we can map to external template files. + // Note that Babel gets confused if you specify a filename when it is the original source + // file. This happens when the template is inline, in which case just use `undefined`. + filename: sourceMapRange.url !== this.sourceUrl ? sourceMapRange.url : undefined, start: { line: sourceMapRange.start.line + 1, // lines are 1-based in Babel. column: sourceMapRange.start.column, @@ -149,7 +155,7 @@ export class BabelAstFactory implements AstFactory { line: sourceMapRange.end.line + 1, // lines are 1-based in Babel. column: sourceMapRange.end.column, }, - }; + } as any; // Needed because the Babel typings for `loc` don't include `filename`. node.start = sourceMapRange.start.offset; node.end = sourceMapRange.end.offset; diff --git a/packages/compiler-cli/linker/babel/src/ast/babel_ast_host.ts b/packages/compiler-cli/linker/babel/src/ast/babel_ast_host.ts index 4e826d8028..5f9426847a 100644 --- a/packages/compiler-cli/linker/babel/src/ast/babel_ast_host.ts +++ b/packages/compiler-cli/linker/babel/src/ast/babel_ast_host.ts @@ -100,6 +100,21 @@ export class BabelAstHost implements AstHost { return stmt.argument; } + isCallExpression = t.isCallExpression; + parseCallee(call: t.Expression): t.Expression { + assert(call, t.isCallExpression, 'a call expression'); + assert(call.callee, t.isExpression, 'an expression'); + return call.callee; + } + parseArguments(call: t.Expression): t.Expression[] { + assert(call, t.isCallExpression, 'a call expression'); + return call.arguments.map(arg => { + assert(arg, isNotSpreadArgument, 'argument not to use spread syntax'); + assert(arg, t.isExpression, 'argument to be an expression'); + return arg; + }); + } + getRange(node: t.Expression): Range { if (node.loc == null || node.start === null || node.end === null) { throw new FatalLinkerError( @@ -138,3 +153,15 @@ function isNotSpreadElement(e: t.Expression|t.SpreadElement): e is t.Expression function isPropertyName(e: t.Expression): e is t.Identifier|t.StringLiteral|t.NumericLiteral { return t.isIdentifier(e) || t.isStringLiteral(e) || t.isNumericLiteral(e); } + +/** + * The declared type of an argument to a call expression. + */ +type ArgumentType = t.CallExpression['arguments'][number]; + +/** + * Return true if the argument is not a spread element. + */ +function isNotSpreadArgument(arg: ArgumentType): arg is Exclude { + return !t.isSpreadElement(arg); +} diff --git a/packages/compiler-cli/linker/babel/src/es2015_linker_plugin.ts b/packages/compiler-cli/linker/babel/src/es2015_linker_plugin.ts index 0ebe7b5db5..17abf1ad33 100644 --- a/packages/compiler-cli/linker/babel/src/es2015_linker_plugin.ts +++ b/packages/compiler-cli/linker/babel/src/es2015_linker_plugin.ts @@ -9,11 +9,13 @@ import {PluginObj} from '@babel/core'; import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; -import {FileLinker, isFatalLinkerError, LinkerEnvironment, LinkerOptions} from '../../../linker'; +import {FileLinker, isFatalLinkerError, LinkerEnvironment} from '../../../linker'; import {BabelAstFactory} from './ast/babel_ast_factory'; import {BabelAstHost} from './ast/babel_ast_host'; import {BabelDeclarationScope, ConstantScopePath} from './babel_declaration_scope'; +import {LinkerPluginOptions} from './linker_plugin_options'; + /** * Create a Babel plugin that visits the program, identifying and linking partial declarations. @@ -21,12 +23,10 @@ import {BabelDeclarationScope, ConstantScopePath} from './babel_declaration_scop * The plugin delegates most of its work to a generic `FileLinker` for each file (`t.Program` in * Babel) that is visited. */ -export function createEs2015LinkerPlugin(options: Partial = {}): PluginObj { +export function createEs2015LinkerPlugin({fileSystem, logger, ...options}: LinkerPluginOptions): + PluginObj { let fileLinker: FileLinker|null = null; - const linkerEnvironment = LinkerEnvironment.create( - new BabelAstHost(), new BabelAstFactory(), options); - return { visitor: { Program: { @@ -36,8 +36,19 @@ export function createEs2015LinkerPlugin(options: Partial = {}): */ enter(path: NodePath): void { assertNull(fileLinker); + // Babel can be configured with a `filename` or `relativeFilename` (or both, or neither) - + // possibly relative to the optional `cwd` path. const file: BabelFile = path.hub.file; - fileLinker = new FileLinker(linkerEnvironment, file.opts.filename ?? '', file.code); + const filename = file.opts.filename ?? file.opts.filenameRelative; + if (!filename) { + throw new Error( + 'No filename (nor filenameRelative) provided by Babel. This is required for the linking of partially compiled directives and components.'); + } + const sourceUrl = fileSystem.resolve(file.opts.cwd ?? '.', filename); + + const linkerEnvironment = LinkerEnvironment.create( + fileSystem, logger, new BabelAstHost(), new BabelAstFactory(sourceUrl), options); + fileLinker = new FileLinker(linkerEnvironment, sourceUrl, file.code); }, /** @@ -66,11 +77,7 @@ export function createEs2015LinkerPlugin(options: Partial = {}): } try { - const callee = call.node.callee; - if (!t.isExpression(callee)) { - return; - } - const calleeName = linkerEnvironment.host.getSymbolName(callee); + const calleeName = getCalleeName(call); if (calleeName === null) { return; } @@ -126,6 +133,17 @@ function insertIntoProgram(program: NodePath, statements: t.Statement } } +function getCalleeName(call: NodePath): string|null { + const callee = call.node.callee; + if (t.isIdentifier(callee)) { + return callee.name; + } else if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) { + return callee.property.name; + } else { + return null; + } +} + /** * Return true if all the `nodes` are Babel expressions. */ @@ -166,7 +184,11 @@ function buildCodeFrameError(file: BabelFile, message: string, node: t.Node): st */ interface BabelFile { code: string; - opts: {filename?: string;}; + opts: { + filename?: string, + filenameRelative?: string, + cwd?: string, + }; buildCodeFrameError(node: t.Node, message: string): Error; } diff --git a/packages/compiler-cli/linker/babel/test/BUILD.bazel b/packages/compiler-cli/linker/babel/test/BUILD.bazel index f2a426de88..f1a48537c3 100644 --- a/packages/compiler-cli/linker/babel/test/BUILD.bazel +++ b/packages/compiler-cli/linker/babel/test/BUILD.bazel @@ -13,6 +13,8 @@ ts_library( "//packages/compiler", "//packages/compiler-cli/linker", "//packages/compiler-cli/linker/babel", + "//packages/compiler-cli/src/ngtsc/file_system/testing", + "//packages/compiler-cli/src/ngtsc/logging/testing", "//packages/compiler-cli/src/ngtsc/translator", "@npm//@babel/core", "@npm//@babel/generator", diff --git a/packages/compiler-cli/linker/babel/test/ast/babel_ast_factory_spec.ts b/packages/compiler-cli/linker/babel/test/ast/babel_ast_factory_spec.ts index c1f874d7f6..950eafc40c 100644 --- a/packages/compiler-cli/linker/babel/test/ast/babel_ast_factory_spec.ts +++ b/packages/compiler-cli/linker/babel/test/ast/babel_ast_factory_spec.ts @@ -14,7 +14,7 @@ import {BabelAstFactory} from '../../src/ast/babel_ast_factory'; describe('BabelAstFactory', () => { let factory: BabelAstFactory; - beforeEach(() => factory = new BabelAstFactory()); + beforeEach(() => factory = new BabelAstFactory('/original.ts')); describe('attachComments()', () => { it('should add the comments to the given statement', () => { @@ -367,16 +367,34 @@ describe('BabelAstFactory', () => { start: {line: 0, column: 1, offset: 1}, end: {line: 2, column: 3, offset: 15}, content: '-****\n*****\n****', - url: 'original.ts' + url: 'other.ts' }); // Lines are 1-based in Babel. expect(expr.loc).toEqual({ + filename: 'other.ts', start: {line: 1, column: 1}, end: {line: 3, column: 3}, - }); + } as any); // The typings for `loc` do not include `filename`. expect(expr.start).toEqual(1); expect(expr.end).toEqual(15); }); + + it('should use undefined if the url is the same as the one passed to the constructor', () => { + const expr = expression.ast`42`; + factory.setSourceMapRange(expr, { + start: {line: 0, column: 1, offset: 1}, + end: {line: 2, column: 3, offset: 15}, + content: '-****\n*****\n****', + url: '/original.ts' + }); + + // Lines are 1-based in Babel. + expect(expr.loc).toEqual({ + filename: undefined, + start: {line: 1, column: 1}, + end: {line: 3, column: 3}, + } as any); // The typings for `loc` do not include `filename`. + }); }); }); diff --git a/packages/compiler-cli/linker/babel/test/ast/babel_ast_host_spec.ts b/packages/compiler-cli/linker/babel/test/ast/babel_ast_host_spec.ts index 746f6cd3ae..81b4571e93 100644 --- a/packages/compiler-cli/linker/babel/test/ast/babel_ast_host_spec.ts +++ b/packages/compiler-cli/linker/babel/test/ast/babel_ast_host_spec.ts @@ -263,6 +263,55 @@ describe('BabelAstHost', () => { }); }); + describe('isCallExpression()', () => { + it('should return true if the expression is a call expression', () => { + expect(host.isCallExpression(expr('foo()'))).toBe(true); + expect(host.isCallExpression(expr('foo.bar()'))).toBe(true); + expect(host.isCallExpression(expr('(foo)(1)'))).toBe(true); + }); + + it('should return false if the expression is not a call expression', () => { + expect(host.isCallExpression(expr('[]'))).toBe(false); + expect(host.isCallExpression(expr('"moo"'))).toBe(false); + expect(host.isCallExpression(expr('\'moo\''))).toBe(false); + expect(host.isCallExpression(expr('someIdentifier'))).toBe(false); + expect(host.isCallExpression(expr('42'))).toBe(false); + expect(host.isCallExpression(expr('{}'))).toBe(false); + expect(host.isCallExpression(expr('null'))).toBe(false); + expect(host.isCallExpression(expr('\'a\' + \'b\''))).toBe(false); + expect(host.isCallExpression(expr('\`moo\`'))).toBe(false); + }); + }); + + describe('parseCallee()', () => { + it('should return the callee expression', () => { + expect(host.parseCallee(expr('foo()'))).toEqual(expr('foo')); + expect(host.parseCallee(expr('foo.bar()'))).toEqual(expr('foo.bar')); + }); + + it('should error if the node is not a call expression', () => { + expect(() => host.parseCallee(expr('[]'))) + .toThrowError('Unsupported syntax, expected a call expression.'); + }); + }); + + describe('parseArguments()', () => { + it('should return the arguments as an array of expressions', () => { + expect(host.parseArguments(expr('foo(12, [])'))).toEqual([expr('12'), expr('[]')]); + expect(host.parseArguments(expr('foo.bar()'))).toEqual([]); + }); + + it('should error if the node is not a call expression', () => { + expect(() => host.parseArguments(expr('[]'))) + .toThrowError('Unsupported syntax, expected a call expression.'); + }); + + it('should error if an argument uses spread syntax', () => { + expect(() => host.parseArguments(expr('foo(1, ...[])'))) + .toThrowError('Unsupported syntax, expected argument not to use spread syntax.'); + }); + }); + describe('getRange()', () => { it('should extract the range from the expression', () => { const file = parse('// preamble\nx = \'moo\';'); diff --git a/packages/compiler-cli/linker/babel/test/es2015_linker_plugin_spec.ts b/packages/compiler-cli/linker/babel/test/es2015_linker_plugin_spec.ts index da4b2bc8b8..21a8b63402 100644 --- a/packages/compiler-cli/linker/babel/test/es2015_linker_plugin_spec.ts +++ b/packages/compiler-cli/linker/babel/test/es2015_linker_plugin_spec.ts @@ -11,13 +11,17 @@ import generate from '@babel/generator'; import * as t from '@babel/types'; import {FileLinker} from '../../../linker'; +import {MockFileSystemNative} from '../../../src/ngtsc/file_system/testing'; +import {MockLogger} from '../../../src/ngtsc/logging/testing'; import {PartialDirectiveLinkerVersion1} from '../../src/file_linker/partial_linkers/partial_directive_linker_1'; import {createEs2015LinkerPlugin} from '../src/es2015_linker_plugin'; describe('createEs2015LinkerPlugin()', () => { it('should return a Babel plugin visitor that handles Program (enter/exit) and CallExpression nodes', () => { - const plugin = createEs2015LinkerPlugin(); + const fileSystem = new MockFileSystemNative(); + const logger = new MockLogger(); + const plugin = createEs2015LinkerPlugin({fileSystem, logger}); expect(plugin.visitor).toEqual({ Program: { enter: jasmine.any(Function), @@ -31,13 +35,16 @@ describe('createEs2015LinkerPlugin()', () => { () => { const isPartialDeclarationSpy = spyOn(FileLinker.prototype, 'isPartialDeclaration'); + const fileSystem = new MockFileSystemNative(); + const logger = new MockLogger(); + const plugin = createEs2015LinkerPlugin({fileSystem, logger}); transformSync( [ 'var core;', `fn1()`, 'fn2({prop: () => fn3({})});', `x.method(() => fn4());`, 'spread(...x);' ].join('\n'), { - plugins: [createEs2015LinkerPlugin()], + plugins: [plugin], filename: '/test.js', parserOpts: {sourceType: 'unambiguous'}, }); @@ -55,29 +62,32 @@ describe('createEs2015LinkerPlugin()', () => { () => { const linkSpy = spyOn(FileLinker.prototype, 'linkPartialDeclaration') .and.returnValue(t.identifier('REPLACEMENT')); + const fileSystem = new MockFileSystemNative(); + const logger = new MockLogger(); + const plugin = createEs2015LinkerPlugin({fileSystem, logger}); transformSync( [ 'var core;', - `ɵɵngDeclareDirective({version: 1, ngImport: core, x: 1});`, - `ɵɵngDeclareComponent({version: 1, ngImport: core, foo: () => ɵɵngDeclareDirective({version: 1, ngImport: core, x: 2})});`, - `x.qux(() => ɵɵngDeclareDirective({version: 1, ngImport: core, x: 3}));`, + `ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core, x: 1});`, + `ɵɵngDeclareComponent({version: '0.0.0-PLACEHOLDER', ngImport: core, foo: () => ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core, x: 2})});`, + `x.qux(() => ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core, x: 3}));`, 'spread(...x);', ].join('\n'), { - plugins: [createEs2015LinkerPlugin()], + plugins: [createEs2015LinkerPlugin({fileSystem, logger})], filename: '/test.js', parserOpts: {sourceType: 'unambiguous'}, }); expect(humanizeLinkerCalls(linkSpy.calls)).toEqual([ - ['ɵɵngDeclareDirective', '{version:1,ngImport:core,x:1}'], + ['ɵɵngDeclareDirective', '{version:\'0.0.0-PLACEHOLDER\',ngImport:core,x:1}'], [ 'ɵɵngDeclareComponent', - '{version:1,ngImport:core,foo:()=>ɵɵngDeclareDirective({version:1,ngImport:core,x:2})}' + '{version:\'0.0.0-PLACEHOLDER\',ngImport:core,foo:()=>ɵɵngDeclareDirective({version:\'0.0.0-PLACEHOLDER\',ngImport:core,x:2})}' ], // Note we do not process `x:2` declaration since it is nested within another declaration - ['ɵɵngDeclareDirective', '{version:1,ngImport:core,x:3}'] + ['ɵɵngDeclareDirective', '{version:\'0.0.0-PLACEHOLDER\',ngImport:core,x:3}'] ]); }); @@ -86,16 +96,19 @@ describe('createEs2015LinkerPlugin()', () => { let replaceCount = 0; spyOn(FileLinker.prototype, 'linkPartialDeclaration') .and.callFake(() => t.identifier('REPLACEMENT_' + ++replaceCount)); + const fileSystem = new MockFileSystemNative(); + const logger = new MockLogger(); + const plugin = createEs2015LinkerPlugin({fileSystem, logger}); const result = transformSync( [ 'var core;', - 'ɵɵngDeclareDirective({version: 1, ngImport: core});', - 'ɵɵngDeclareDirective({version: 1, ngImport: core, foo: () => bar({})});', + 'ɵɵngDeclareDirective({version: \'0.0.0-PLACEHOLDER\', ngImport: core});', + 'ɵɵngDeclareDirective({version: \'0.0.0-PLACEHOLDER\', ngImport: core, foo: () => bar({})});', 'x.qux();', 'spread(...x);', ].join('\n'), { - plugins: [createEs2015LinkerPlugin()], + plugins: [createEs2015LinkerPlugin({fileSystem, logger})], filename: '/test.js', parserOpts: {sourceType: 'unambiguous'}, generatorOpts: {compact: true}, @@ -105,16 +118,19 @@ describe('createEs2015LinkerPlugin()', () => { it('should return a Babel plugin that adds shared statements after any imports', () => { spyOnLinkPartialDeclarationWithConstants(o.literal('REPLACEMENT')); + const fileSystem = new MockFileSystemNative(); + const logger = new MockLogger(); + const plugin = createEs2015LinkerPlugin({fileSystem, logger}); const result = transformSync( [ 'import * as core from \'some-module\';', 'import {id} from \'other-module\';', - `ɵɵngDeclareDirective({version: 1, ngImport: core})`, - `ɵɵngDeclareDirective({version: 1, ngImport: core})`, - `ɵɵngDeclareDirective({version: 1, ngImport: core})`, + `ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core})`, + `ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core})`, + `ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core})`, ].join('\n'), { - plugins: [createEs2015LinkerPlugin()], + plugins: [createEs2015LinkerPlugin({fileSystem, logger})], filename: '/test.js', parserOpts: {sourceType: 'unambiguous'}, generatorOpts: {compact: true}, @@ -127,15 +143,18 @@ describe('createEs2015LinkerPlugin()', () => { it('should return a Babel plugin that adds shared statements at the start of the program if it is an ECMAScript Module and there are no imports', () => { spyOnLinkPartialDeclarationWithConstants(o.literal('REPLACEMENT')); + const fileSystem = new MockFileSystemNative(); + const logger = new MockLogger(); + const plugin = createEs2015LinkerPlugin({fileSystem, logger}); const result = transformSync( [ 'var core;', - `ɵɵngDeclareDirective({version: 1, ngImport: core})`, - `ɵɵngDeclareDirective({version: 1, ngImport: core})`, - `ɵɵngDeclareDirective({version: 1, ngImport: core})`, + `ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core})`, + `ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core})`, + `ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core})`, ].join('\n'), { - plugins: [createEs2015LinkerPlugin()], + plugins: [createEs2015LinkerPlugin({fileSystem, logger})], filename: '/test.js', // We declare the file as a module because this cannot be inferred from the source parserOpts: {sourceType: 'module'}, @@ -149,14 +168,18 @@ describe('createEs2015LinkerPlugin()', () => { it('should return a Babel plugin that adds shared statements at the start of the function body if the ngImport is from a function parameter', () => { spyOnLinkPartialDeclarationWithConstants(o.literal('REPLACEMENT')); + const fileSystem = new MockFileSystemNative(); + const logger = new MockLogger(); + const plugin = createEs2015LinkerPlugin({fileSystem, logger}); const result = transformSync( [ - 'function run(core) {', ` ɵɵngDeclareDirective({version: 1, ngImport: core})`, - ` ɵɵngDeclareDirective({version: 1, ngImport: core})`, - ` ɵɵngDeclareDirective({version: 1, ngImport: core})`, '}' + 'function run(core) {', + ` ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core})`, + ` ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core})`, + ` ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core})`, '}' ].join('\n'), { - plugins: [createEs2015LinkerPlugin()], + plugins: [createEs2015LinkerPlugin({fileSystem, logger})], filename: '/test.js', parserOpts: {sourceType: 'unambiguous'}, generatorOpts: {compact: true}, @@ -169,16 +192,19 @@ describe('createEs2015LinkerPlugin()', () => { it('should return a Babel plugin that adds shared statements into an IIFE if no scope could not be derived for the ngImport', () => { spyOnLinkPartialDeclarationWithConstants(o.literal('REPLACEMENT')); + const fileSystem = new MockFileSystemNative(); + const logger = new MockLogger(); + const plugin = createEs2015LinkerPlugin({fileSystem, logger}); const result = transformSync( [ 'function run() {', - ` ɵɵngDeclareDirective({version: 1, ngImport: core})`, - ` ɵɵngDeclareDirective({version: 1, ngImport: core})`, - ` ɵɵngDeclareDirective({version: 1, ngImport: core})`, + ` ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core})`, + ` ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core})`, + ` ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core})`, '}', ].join('\n'), { - plugins: [createEs2015LinkerPlugin()], + plugins: [createEs2015LinkerPlugin({fileSystem, logger})], filename: '/test.js', parserOpts: {sourceType: 'unambiguous'}, generatorOpts: {compact: true}, @@ -195,13 +221,16 @@ describe('createEs2015LinkerPlugin()', () => { it('should still execute other plugins that match AST nodes inside the result of the replacement', () => { spyOnLinkPartialDeclarationWithConstants(o.fn([], [], null, null, 'FOO')); + const fileSystem = new MockFileSystemNative(); + const logger = new MockLogger(); + const plugin = createEs2015LinkerPlugin({fileSystem, logger}); const result = transformSync( [ - `ɵɵngDeclareDirective({version: 1, ngImport: core}); FOO;`, + `ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core}); FOO;`, ].join('\n'), { plugins: [ - createEs2015LinkerPlugin(), + createEs2015LinkerPlugin({fileSystem, logger}), createIdentifierMapperPlugin('FOO', 'BAR'), createIdentifierMapperPlugin('_c0', 'x1'), ], @@ -216,7 +245,7 @@ describe('createEs2015LinkerPlugin()', () => { it('should not process call expressions within inserted functions', () => { spyOn(PartialDirectiveLinkerVersion1.prototype, 'linkPartialDeclaration') - .and.callFake(((sourceUrl, code, constantPool) => { + .and.callFake((constantPool => { // Insert a call expression into the constant pool. This is inserted into // Babel's AST upon program exit, and will therefore be visited by Babel // outside of an active linker context. @@ -232,13 +261,16 @@ describe('createEs2015LinkerPlugin()', () => { const isPartialDeclarationSpy = spyOn(FileLinker.prototype, 'isPartialDeclaration').and.callThrough(); + const fileSystem = new MockFileSystemNative(); + const logger = new MockLogger(); + const plugin = createEs2015LinkerPlugin({fileSystem, logger}); const result = transformSync( [ 'import * as core from \'some-module\';', - `ɵɵngDeclareDirective({version: 1, ngImport: core})`, + `ɵɵngDeclareDirective({version: '0.0.0-PLACEHOLDER', ngImport: core})`, ].join('\n'), { - plugins: [createEs2015LinkerPlugin()], + plugins: [createEs2015LinkerPlugin({fileSystem, logger})], filename: '/test.js', parserOpts: {sourceType: 'unambiguous'}, generatorOpts: {compact: true}, @@ -265,7 +297,7 @@ function humanizeLinkerCalls( function spyOnLinkPartialDeclarationWithConstants(replacement: o.Expression) { let callCount = 0; spyOn(PartialDirectiveLinkerVersion1.prototype, 'linkPartialDeclaration') - .and.callFake(((sourceUrl, code, constantPool) => { + .and.callFake((constantPool => { const constArray = o.literalArr([o.literal(++callCount)]); // We have to add the constant twice or it will not create a shared statement constantPool.getConstLiteral(constArray); diff --git a/packages/compiler-cli/linker/index.ts b/packages/compiler-cli/linker/index.ts index 96256b7b43..30504f1d5d 100644 --- a/packages/compiler-cli/linker/index.ts +++ b/packages/compiler-cli/linker/index.ts @@ -11,4 +11,5 @@ export {FatalLinkerError, isFatalLinkerError} from './src/fatal_linker_error'; export {DeclarationScope} from './src/file_linker/declaration_scope'; export {FileLinker} from './src/file_linker/file_linker'; export {LinkerEnvironment} from './src/file_linker/linker_environment'; -export {LinkerOptions} from './src/file_linker/linker_options'; +export {DEFAULT_LINKER_OPTIONS, LinkerOptions} from './src/file_linker/linker_options'; +export {needsLinking} from './src/file_linker/needs_linking'; diff --git a/packages/compiler-cli/linker/src/ast/ast_host.ts b/packages/compiler-cli/linker/src/ast/ast_host.ts index 70f55bb5d7..11907f6115 100644 --- a/packages/compiler-cli/linker/src/ast/ast_host.ts +++ b/packages/compiler-cli/linker/src/ast/ast_host.ts @@ -74,6 +74,21 @@ export interface AstHost { */ parseReturnValue(fn: TExpression): TExpression; + /** + * Return true if the given expression is a call expression, or false otherwise. + */ + isCallExpression(node: TExpression): boolean; + /** + * Returns the expression that is called in the provided call expression, or throw if it is not + * a call expression. + */ + parseCallee(call: TExpression): TExpression; + /** + * Returns the argument expressions for the provided call expression, or throw if it is not + * a call expression. + */ + parseArguments(call: TExpression): TExpression[]; + /** * Compute the location range of the expression in the source file, to be used for source-mapping. */ diff --git a/packages/compiler-cli/linker/src/ast/ast_value.ts b/packages/compiler-cli/linker/src/ast/ast_value.ts index 838e2ac637..3e3de6943c 100644 --- a/packages/compiler-cli/linker/src/ast/ast_value.ts +++ b/packages/compiler-cli/linker/src/ast/ast_value.ts @@ -9,18 +9,60 @@ import * as o from '@angular/compiler'; import {FatalLinkerError} from '../fatal_linker_error'; import {AstHost, Range} from './ast_host'; +/** + * Represents only those types in `T` that are object types. + */ +type ObjectType = Extract; + +/** + * Represents the value type of an object literal. + */ +type ObjectValueType = T extends Record? R : never; + +/** + * Represents the value type of an array literal. + */ +type ArrayValueType = T extends Array? R : never; + +/** + * Ensures that `This` has its generic type `Actual` conform to the expected generic type in + * `Expected`, to disallow calling a method if the generic type does not conform. + */ +type ConformsTo = Actual extends Expected ? This : never; + +/** + * Ensures that `This` is an `AstValue` whose generic type conforms to `Expected`, to disallow + * calling a method if the value's type does not conform. + */ +type HasValueType = + This extends AstValue? ConformsTo: never; + +/** + * Represents only the string keys of type `T`. + */ +type PropertyKey = keyof T&string; + /** * This helper class wraps an object expression along with an `AstHost` object, exposing helper * methods that make it easier to extract the properties of the object. + * + * The generic `T` is used as reference type of the expected structure that is represented by this + * object. It does not achieve full type-safety for the provided operations in correspondence with + * `T`; its main goal is to provide references to a documented type and ensure that the properties + * that are read from the object are present. + * + * Unfortunately, the generic types are unable to prevent reading an optional property from the + * object without first having called `has` to ensure that the property exists. This is one example + * of where full type-safety is not achieved. */ -export class AstObject { +export class AstObject { /** * Create a new `AstObject` from the given `expression` and `host`. */ - static parse(expression: TExpression, host: AstHost): - AstObject { + static parse(expression: TExpression, host: AstHost): + AstObject { const obj = host.parseObjectLiteral(expression); - return new AstObject(expression, obj, host); + return new AstObject(expression, obj, host); } private constructor( @@ -30,7 +72,7 @@ export class AstObject { /** * Returns true if the object has a property called `propertyName`. */ - has(propertyName: string): boolean { + has(propertyName: PropertyKey): boolean { return this.obj.has(propertyName); } @@ -39,7 +81,8 @@ export class AstObject { * * Throws an error if there is no such property or the property is not a number. */ - getNumber(propertyName: string): number { + getNumber>(this: ConformsTo, propertyName: K): + number { return this.host.parseNumericLiteral(this.getRequiredProperty(propertyName)); } @@ -48,7 +91,8 @@ export class AstObject { * * Throws an error if there is no such property or the property is not a string. */ - getString(propertyName: string): string { + getString>(this: ConformsTo, propertyName: K): + string { return this.host.parseStringLiteral(this.getRequiredProperty(propertyName)); } @@ -57,8 +101,9 @@ export class AstObject { * * Throws an error if there is no such property or the property is not a boolean. */ - getBoolean(propertyName: string): boolean { - return this.host.parseBooleanLiteral(this.getRequiredProperty(propertyName)); + getBoolean>(this: ConformsTo, propertyName: K): + boolean { + return this.host.parseBooleanLiteral(this.getRequiredProperty(propertyName)) as any; } /** @@ -66,7 +111,8 @@ export class AstObject { * * Throws an error if there is no such property or the property is not an object. */ - getObject(propertyName: string): AstObject { + getObject>(this: ConformsTo, propertyName: K): + AstObject, TExpression> { const expr = this.getRequiredProperty(propertyName); const obj = this.host.parseObjectLiteral(expr); return new AstObject(expr, obj, this.host); @@ -77,7 +123,8 @@ export class AstObject { * * Throws an error if there is no such property or the property is not an array. */ - getArray(propertyName: string): AstValue[] { + getArray>(this: ConformsTo, propertyName: K): + AstValue, TExpression>[] { const arr = this.host.parseArrayLiteral(this.getRequiredProperty(propertyName)); return arr.map(entry => new AstValue(entry, this.host)); } @@ -88,7 +135,7 @@ export class AstObject { * * Throws an error if there is no such property. */ - getOpaque(propertyName: string): o.WrappedNodeExpr { + getOpaque(propertyName: PropertyKey): o.WrappedNodeExpr { return new o.WrappedNodeExpr(this.getRequiredProperty(propertyName)); } @@ -97,7 +144,7 @@ export class AstObject { * * Throws an error if there is no such property. */ - getNode(propertyName: string): TExpression { + getNode(propertyName: PropertyKey): TExpression { return this.getRequiredProperty(propertyName); } @@ -106,7 +153,7 @@ export class AstObject { * * Throws an error if there is no such property. */ - getValue(propertyName: string): AstValue { + getValue>(propertyName: K): AstValue { return new AstValue(this.getRequiredProperty(propertyName), this.host); } @@ -114,8 +161,8 @@ export class AstObject { * Converts the AstObject to a raw JavaScript object, mapping each property value (as an * `AstValue`) to the generic type (`T`) via the `mapper` function. */ - toLiteral(mapper: (value: AstValue) => T): {[key: string]: T} { - const result: {[key: string]: T} = {}; + toLiteral(mapper: (value: AstValue, TExpression>) => V): Record { + const result: Record = {}; for (const [key, expression] of this.obj) { result[key] = mapper(new AstValue(expression, this.host)); } @@ -126,15 +173,15 @@ export class AstObject { * Converts the AstObject to a JavaScript Map, mapping each property value (as an * `AstValue`) to the generic type (`T`) via the `mapper` function. */ - toMap(mapper: (value: AstValue) => T): Map { - const result = new Map(); + toMap(mapper: (value: AstValue, TExpression>) => V): Map { + const result = new Map(); for (const [key, expression] of this.obj) { result.set(key, mapper(new AstValue(expression, this.host))); } return result; } - private getRequiredProperty(propertyName: string): TExpression { + private getRequiredProperty(propertyName: PropertyKey): TExpression { if (!this.obj.has(propertyName)) { throw new FatalLinkerError( this.expression, `Expected property '${propertyName}' to be present.`); @@ -146,8 +193,12 @@ export class AstObject { /** * This helper class wraps an `expression`, exposing methods that use the `host` to give * access to the underlying value of the wrapped expression. + * + * The generic `T` is used as reference type of the expected type that is represented by this value. + * It does not achieve full type-safety for the provided operations in correspondence with `T`; its + * main goal is to provide references to a documented type. */ -export class AstValue { +export class AstValue { constructor(readonly expression: TExpression, private host: AstHost) {} /** @@ -168,7 +219,7 @@ export class AstValue { /** * Parse the number from this value, or error if it is not a number. */ - getNumber(): number { + getNumber(this: HasValueType): number { return this.host.parseNumericLiteral(this.expression); } @@ -182,7 +233,7 @@ export class AstValue { /** * Parse the string from this value, or error if it is not a string. */ - getString(): string { + getString(this: HasValueType): string { return this.host.parseStringLiteral(this.expression); } @@ -196,7 +247,7 @@ export class AstValue { /** * Parse the boolean from this value, or error if it is not a boolean. */ - getBoolean(): boolean { + getBoolean(this: HasValueType): boolean { return this.host.parseBooleanLiteral(this.expression); } @@ -210,7 +261,7 @@ export class AstValue { /** * Parse this value into an `AstObject`, or error if it is not an object literal. */ - getObject(): AstObject { + getObject(this: HasValueType): AstObject, TExpression> { return AstObject.parse(this.expression, this.host); } @@ -224,7 +275,7 @@ export class AstValue { /** * Parse this value into an array of `AstValue` objects, or error if it is not an array literal. */ - getArray(): AstValue[] { + getArray(this: HasValueType): AstValue, TExpression>[] { const arr = this.host.parseArrayLiteral(this.expression); return arr.map(entry => new AstValue(entry, this.host)); } @@ -240,10 +291,23 @@ export class AstValue { * Extract the return value as an `AstValue` from this value as a function expression, or error if * it is not a function expression. */ - getFunctionReturnValue(): AstValue { + getFunctionReturnValue(this: HasValueType): AstValue { return new AstValue(this.host.parseReturnValue(this.expression), this.host); } + isCallExpression(): boolean { + return this.host.isCallExpression(this.expression); + } + + getCallee(): AstValue { + return new AstValue(this.host.parseCallee(this.expression), this.host); + } + + getArguments(): AstValue[] { + const args = this.host.parseArguments(this.expression); + return args.map(arg => new AstValue(arg, this.host)); + } + /** * Return the `TExpression` of this value wrapped in a `WrappedNodeExpr`. */ diff --git a/packages/compiler-cli/linker/src/ast/typescript/typescript_ast_host.ts b/packages/compiler-cli/linker/src/ast/typescript/typescript_ast_host.ts index 582f5823a0..f23223d0b8 100644 --- a/packages/compiler-cli/linker/src/ast/typescript/typescript_ast_host.ts +++ b/packages/compiler-cli/linker/src/ast/typescript/typescript_ast_host.ts @@ -109,6 +109,21 @@ export class TypeScriptAstHost implements AstHost { return stmt.expression; } + isCallExpression = ts.isCallExpression; + + parseCallee(call: ts.Expression): ts.Expression { + assert(call, ts.isCallExpression, 'a call expression'); + return call.expression; + } + + parseArguments(call: ts.Expression): ts.Expression[] { + assert(call, ts.isCallExpression, 'a call expression'); + return call.arguments.map(arg => { + assert(arg, isNotSpreadElement, 'argument not to use spread syntax'); + return arg; + }); + } + getRange(node: ts.Expression): Range { const file = node.getSourceFile(); if (file === undefined) { diff --git a/packages/compiler-cli/linker/src/file_linker/emit_scopes/emit_scope.ts b/packages/compiler-cli/linker/src/file_linker/emit_scopes/emit_scope.ts index 75f47067ce..4402e160df 100644 --- a/packages/compiler-cli/linker/src/file_linker/emit_scopes/emit_scope.ts +++ b/packages/compiler-cli/linker/src/file_linker/emit_scopes/emit_scope.ts @@ -8,7 +8,7 @@ import {ConstantPool} from '@angular/compiler'; import * as o from '@angular/compiler/src/output/output_ast'; import {LinkerImportGenerator} from '../../linker_import_generator'; -import {LinkerEnvironment} from '../linker_environment'; +import {Translator} from '../translator'; /** * This class represents (from the point of view of the `FileLinker`) the scope in which @@ -24,7 +24,7 @@ export class EmitScope { constructor( protected readonly ngImport: TExpression, - protected readonly linkerEnvironment: LinkerEnvironment) {} + protected readonly translator: Translator) {} /** * Translate the given Output AST definition expression into a generic `TExpression`. @@ -32,7 +32,7 @@ export class EmitScope { * Use a `LinkerImportGenerator` to handle any imports in the definition. */ translateDefinition(definition: o.Expression): TExpression { - return this.linkerEnvironment.translator.translateExpression( + return this.translator.translateExpression( definition, new LinkerImportGenerator(this.ngImport)); } @@ -40,9 +40,8 @@ export class EmitScope { * Return any constant statements that are shared between all uses of this `EmitScope`. */ getConstantStatements(): TStatement[] { - const {translator} = this.linkerEnvironment; const importGenerator = new LinkerImportGenerator(this.ngImport); return this.constantPool.statements.map( - statement => translator.translateStatement(statement, importGenerator)); + statement => this.translator.translateStatement(statement, importGenerator)); } } diff --git a/packages/compiler-cli/linker/src/file_linker/emit_scopes/iife_emit_scope.ts b/packages/compiler-cli/linker/src/file_linker/emit_scopes/iife_emit_scope.ts index 18d0982d2d..d98484453c 100644 --- a/packages/compiler-cli/linker/src/file_linker/emit_scopes/iife_emit_scope.ts +++ b/packages/compiler-cli/linker/src/file_linker/emit_scopes/iife_emit_scope.ts @@ -6,6 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ import * as o from '@angular/compiler/src/output/output_ast'; + +import {AstFactory} from '../../../../src/ngtsc/translator'; +import {Translator} from '../translator'; + import {EmitScope} from './emit_scope'; /** @@ -14,6 +18,12 @@ import {EmitScope} from './emit_scope'; * translated definition inside an IIFE. */ export class IifeEmitScope extends EmitScope { + constructor( + ngImport: TExpression, translator: Translator, + private readonly factory: AstFactory) { + super(ngImport, translator); + } + /** * Translate the given Output AST definition expression into a generic `TExpression`. * @@ -21,13 +31,13 @@ export class IifeEmitScope extends EmitScope = [] as const; * This class is responsible for linking all the partial declarations found in a single file. */ export class FileLinker { - private linkerSelector = new PartialLinkerSelector(this.linkerEnvironment.options); + private linkerSelector: PartialLinkerSelector; private emitScopes = new Map>(); constructor( private linkerEnvironment: LinkerEnvironment, - private sourceUrl: string, readonly code: string) {} + sourceUrl: AbsoluteFsPath, code: string) { + this.linkerSelector = + new PartialLinkerSelector(this.linkerEnvironment, sourceUrl, code); + } /** * Return true if the given callee name matches a partial declaration that can be linked. @@ -40,7 +45,8 @@ export class FileLinker { * * @param declarationFn the name of the function used to declare the partial declaration - e.g. * `ɵɵngDeclareDirective`. - * @param args the arguments passed to the declaration function. + * @param args the arguments passed to the declaration function, should be a single object that + * corresponds to the `R3DeclareDirectiveMetadata` or `R3DeclareComponentMetadata` interfaces. * @param declarationScope the scope that contains this call to the declaration function. */ linkPartialDeclaration( @@ -52,14 +58,14 @@ export class FileLinker { args.length}.`); } - const metaObj = AstObject.parse(args[0], this.linkerEnvironment.host); + const metaObj = + AstObject.parse(args[0], this.linkerEnvironment.host); const ngImport = metaObj.getNode('ngImport'); const emitScope = this.getEmitScope(ngImport, declarationScope); - const version = metaObj.getNumber('version'); + const version = metaObj.getString('version'); const linker = this.linkerSelector.getLinker(declarationFn, version); - const definition = - linker.linkPartialDeclaration(this.sourceUrl, this.code, emitScope.constantPool, metaObj); + const definition = linker.linkPartialDeclaration(emitScope.constantPool, metaObj); return emitScope.translateDefinition(definition); } @@ -83,11 +89,13 @@ export class FileLinker { const constantScope = declarationScope.getConstantScopeRef(ngImport); if (constantScope === null) { // There is no constant scope so we will emit extra statements into the definition IIFE. - return new IifeEmitScope(ngImport, this.linkerEnvironment); + return new IifeEmitScope( + ngImport, this.linkerEnvironment.translator, this.linkerEnvironment.factory); } if (!this.emitScopes.has(constantScope)) { - this.emitScopes.set(constantScope, new EmitScope(ngImport, this.linkerEnvironment)); + this.emitScopes.set( + constantScope, new EmitScope(ngImport, this.linkerEnvironment.translator)); } return this.emitScopes.get(constantScope)!; } diff --git a/packages/compiler-cli/linker/src/file_linker/linker_environment.ts b/packages/compiler-cli/linker/src/file_linker/linker_environment.ts index ff1401b028..06c4a86d6e 100644 --- a/packages/compiler-cli/linker/src/file_linker/linker_environment.ts +++ b/packages/compiler-cli/linker/src/file_linker/linker_environment.ts @@ -5,7 +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 {AstFactory} from '@angular/compiler-cli/src/ngtsc/translator'; +import {ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; +import {Logger} from '../../../src/ngtsc/logging'; +import {SourceFileLoader} from '../../../src/ngtsc/sourcemaps'; +import {AstFactory} from '../../../src/ngtsc/translator'; import {AstHost} from '../ast/ast_host'; import {DEFAULT_LINKER_OPTIONS, LinkerOptions} from './linker_options'; @@ -13,19 +16,26 @@ import {Translator} from './translator'; export class LinkerEnvironment { readonly translator = new Translator(this.factory); + readonly sourceFileLoader = + this.options.sourceMapping ? new SourceFileLoader(this.fileSystem, this.logger, {}) : null; + private constructor( + readonly fileSystem: ReadonlyFileSystem, readonly logger: Logger, readonly host: AstHost, readonly factory: AstFactory, readonly options: LinkerOptions) {} static create( - host: AstHost, factory: AstFactory, + fileSystem: ReadonlyFileSystem, logger: Logger, host: AstHost, + factory: AstFactory, options: Partial): LinkerEnvironment { - return new LinkerEnvironment(host, factory, { + return new LinkerEnvironment(fileSystem, logger, host, factory, { enableI18nLegacyMessageIdFormat: options.enableI18nLegacyMessageIdFormat ?? DEFAULT_LINKER_OPTIONS.enableI18nLegacyMessageIdFormat, i18nNormalizeLineEndingsInICUs: options.i18nNormalizeLineEndingsInICUs ?? DEFAULT_LINKER_OPTIONS.i18nNormalizeLineEndingsInICUs, i18nUseExternalIds: options.i18nUseExternalIds ?? DEFAULT_LINKER_OPTIONS.i18nUseExternalIds, + sourceMapping: options.sourceMapping ?? DEFAULT_LINKER_OPTIONS.sourceMapping, + linkerJitMode: options.linkerJitMode ?? DEFAULT_LINKER_OPTIONS.linkerJitMode, }); } } diff --git a/packages/compiler-cli/linker/src/file_linker/linker_options.ts b/packages/compiler-cli/linker/src/file_linker/linker_options.ts index 9c4d8feed0..a11112291e 100644 --- a/packages/compiler-cli/linker/src/file_linker/linker_options.ts +++ b/packages/compiler-cli/linker/src/file_linker/linker_options.ts @@ -27,6 +27,20 @@ export interface LinkerOptions { * The default is `false`. */ i18nUseExternalIds: boolean; + + /** + * Whether to use source-mapping to compute the original source for external templates. + * The default is `true`. + */ + sourceMapping: boolean; + + /** + * This option tells the linker to generate information used by a downstream JIT compiler. + * + * Specifically, in JIT mode, NgModule definitions must describe the `declarations`, `imports`, + * `exports`, etc, which are otherwise not needed. + */ + linkerJitMode: boolean; } /** @@ -36,4 +50,6 @@ export const DEFAULT_LINKER_OPTIONS: LinkerOptions = { enableI18nLegacyMessageIdFormat: true, i18nNormalizeLineEndingsInICUs: false, i18nUseExternalIds: false, + sourceMapping: true, + linkerJitMode: false, }; diff --git a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_component_linker_1.ts b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_component_linker_1.ts index 3a7f4cbdf4..d2d0d09f00 100644 --- a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_component_linker_1.ts +++ b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_component_linker_1.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 {compileComponentFromMetadata, ConstantPool, DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig, makeBindingParser, parseTemplate, R3ComponentMetadata, R3UsedDirectiveMetadata} from '@angular/compiler'; +import {compileComponentFromMetadata, ConstantPool, DeclarationListEmitMode, DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig, makeBindingParser, parseTemplate, R3ComponentMetadata, R3DeclareComponentMetadata, R3DeclareUsedDirectiveMetadata, R3PartialDeclaration, R3UsedDirectiveMetadata} from '@angular/compiler'; import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/compiler/src/core'; import * as o from '@angular/compiler/src/output/output_ast'; +import {AbsoluteFsPath} from '../../../../src/ngtsc/file_system'; import {Range} from '../../ast/ast_host'; import {AstObject, AstValue} from '../../ast/ast_value'; import {FatalLinkerError} from '../../fatal_linker_error'; -import {LinkerOptions} from '../linker_options'; +import {GetSourceFileFn} from '../get_source_file'; +import {LinkerEnvironment} from '../linker_environment'; import {toR3DirectiveMeta} from './partial_directive_linker_1'; import {PartialLinker} from './partial_linker'; @@ -20,120 +22,230 @@ import {PartialLinker} from './partial_linker'; /** * A `PartialLinker` that is designed to process `ɵɵngDeclareComponent()` call expressions. */ -export class PartialComponentLinkerVersion1 implements PartialLinker { - constructor(private readonly options: LinkerOptions) {} +export class PartialComponentLinkerVersion1 implements + PartialLinker { + private readonly i18nNormalizeLineEndingsInICUs = + this.environment.options.i18nNormalizeLineEndingsInICUs; + private readonly enableI18nLegacyMessageIdFormat = + this.environment.options.enableI18nLegacyMessageIdFormat; + private readonly i18nUseExternalIds = this.environment.options.i18nUseExternalIds; + + constructor( + private readonly environment: LinkerEnvironment, + private readonly getSourceFile: GetSourceFileFn, private sourceUrl: AbsoluteFsPath, + private code: string) {} linkPartialDeclaration( - sourceUrl: string, code: string, constantPool: ConstantPool, - metaObj: AstObject): o.Expression { - const meta = toR3ComponentMeta(metaObj, code, sourceUrl, this.options); + constantPool: ConstantPool, + metaObj: AstObject): o.Expression { + const meta = this.toR3ComponentMeta(metaObj); const def = compileComponentFromMetadata(meta, constantPool, makeBindingParser()); return def.expression; } + + /** + * This function derives the `R3ComponentMetadata` from the provided AST object. + */ + private toR3ComponentMeta(metaObj: AstObject): + R3ComponentMetadata { + const interpolation = parseInterpolationConfig(metaObj); + const templateSource = metaObj.getValue('template'); + const isInline = metaObj.has('isInline') ? metaObj.getBoolean('isInline') : false; + const templateInfo = this.getTemplateInfo(templateSource, isInline); + + // We always normalize line endings if the template is inline. + const i18nNormalizeLineEndingsInICUs = isInline || this.i18nNormalizeLineEndingsInICUs; + + const template = parseTemplate(templateInfo.code, templateInfo.sourceUrl, { + escapedString: templateInfo.isEscaped, + interpolationConfig: interpolation, + range: templateInfo.range, + enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat, + preserveWhitespaces: + metaObj.has('preserveWhitespaces') ? metaObj.getBoolean('preserveWhitespaces') : false, + i18nNormalizeLineEndingsInICUs, + isInline, + }); + if (template.errors !== null) { + const errors = template.errors.map(err => err.toString()).join('\n'); + throw new FatalLinkerError( + templateSource.expression, `Errors found in the template:\n${errors}`); + } + + let declarationListEmitMode = DeclarationListEmitMode.Direct; + + const collectUsedDirectives = + (directives: AstValue[]) => { + return directives.map(directive => { + const directiveExpr = directive.getObject(); + const type = directiveExpr.getValue('type'); + const selector = directiveExpr.getString('selector'); + + let typeExpr = type.getOpaque(); + const forwardRefType = extractForwardRef(type); + if (forwardRefType !== null) { + typeExpr = forwardRefType; + declarationListEmitMode = DeclarationListEmitMode.Closure; + } + + return { + type: typeExpr, + selector: selector, + inputs: directiveExpr.has('inputs') ? + directiveExpr.getArray('inputs').map(input => input.getString()) : + [], + outputs: directiveExpr.has('outputs') ? + directiveExpr.getArray('outputs').map(input => input.getString()) : + [], + exportAs: directiveExpr.has('exportAs') ? + directiveExpr.getArray('exportAs').map(exportAs => exportAs.getString()) : + null, + }; + }); + }; + + let directives: R3UsedDirectiveMetadata[] = []; + if (metaObj.has('components')) { + directives.push(...collectUsedDirectives(metaObj.getArray('components'))); + } + if (metaObj.has('directives')) { + directives.push(...collectUsedDirectives(metaObj.getArray('directives'))); + } + + let pipes = new Map(); + if (metaObj.has('pipes')) { + pipes = metaObj.getObject('pipes').toMap(pipe => { + const forwardRefType = extractForwardRef(pipe); + if (forwardRefType !== null) { + declarationListEmitMode = DeclarationListEmitMode.Closure; + return forwardRefType; + } else { + return pipe.getOpaque(); + } + }); + } + + return { + ...toR3DirectiveMeta(metaObj, this.code, this.sourceUrl), + viewProviders: metaObj.has('viewProviders') ? metaObj.getOpaque('viewProviders') : null, + template: { + nodes: template.nodes, + ngContentSelectors: template.ngContentSelectors, + }, + declarationListEmitMode, + styles: metaObj.has('styles') ? metaObj.getArray('styles').map(entry => entry.getString()) : + [], + encapsulation: metaObj.has('encapsulation') ? + parseEncapsulation(metaObj.getValue('encapsulation')) : + ViewEncapsulation.Emulated, + interpolation, + changeDetection: metaObj.has('changeDetection') ? + parseChangeDetectionStrategy(metaObj.getValue('changeDetection')) : + ChangeDetectionStrategy.Default, + animations: metaObj.has('animations') ? metaObj.getOpaque('animations') : null, + relativeContextFilePath: this.sourceUrl, + i18nUseExternalIds: this.i18nUseExternalIds, + pipes, + directives, + }; + } + + /** + * Update the range to remove the start and end chars, which should be quotes around the template. + */ + private getTemplateInfo(templateNode: AstValue, isInline: boolean): + TemplateInfo { + const range = templateNode.getRange(); + + if (!isInline) { + // If not marked as inline, then we try to get the template info from the original external + // template file, via source-mapping. + const externalTemplate = this.tryExternalTemplate(range); + if (externalTemplate !== null) { + return externalTemplate; + } + } + + // Either the template is marked inline or we failed to find the original external template. + // So just use the literal string from the partially compiled component declaration. + return this.templateFromPartialCode(templateNode, range); + } + + private tryExternalTemplate(range: Range): TemplateInfo|null { + const sourceFile = this.getSourceFile(); + if (sourceFile === null) { + return null; + } + + const pos = sourceFile.getOriginalLocation(range.startLine, range.startCol); + // Only interested if the original location is in an "external" template file: + // * the file is different to the current file + // * the file does not end in `.js` or `.ts` (we expect it to be something like `.html`). + // * the range starts at the beginning of the file + if (pos === null || pos.file === this.sourceUrl || /\.[jt]s$/.test(pos.file) || + pos.line !== 0 || pos.column !== 0) { + return null; + } + + const templateContents = sourceFile.sources.find(src => src?.sourcePath === pos.file)!.contents; + + return { + code: templateContents, + sourceUrl: pos.file, + range: {startPos: 0, startLine: 0, startCol: 0, endPos: templateContents.length}, + isEscaped: false, + }; + } + + private templateFromPartialCode( + templateNode: AstValue, + {startPos, endPos, startLine, startCol}: Range): TemplateInfo { + if (!/["'`]/.test(this.code[startPos]) || this.code[startPos] !== this.code[endPos - 1]) { + throw new FatalLinkerError( + templateNode.expression, + `Expected the template string to be wrapped in quotes but got: ${ + this.code.substring(startPos, endPos)}`); + } + return { + code: this.code, + sourceUrl: this.sourceUrl, + range: {startPos: startPos + 1, endPos: endPos - 1, startLine, startCol: startCol + 1}, + isEscaped: true, + }; + } +} + +interface TemplateInfo { + code: string; + sourceUrl: string; + range: Range; + isEscaped: boolean; } /** - * This function derives the `R3ComponentMetadata` from the provided AST object. + * Extract an `InterpolationConfig` from the component declaration. */ -export function toR3ComponentMeta( - metaObj: AstObject, code: string, sourceUrl: string, - options: LinkerOptions): R3ComponentMetadata { - let interpolation = DEFAULT_INTERPOLATION_CONFIG; - if (metaObj.has('interpolation')) { - interpolation = InterpolationConfig.fromArray( - metaObj.getArray('interpolation').map(entry => entry.getString()) as [string, string]); +function parseInterpolationConfig( + metaObj: AstObject): InterpolationConfig { + if (!metaObj.has('interpolation')) { + return DEFAULT_INTERPOLATION_CONFIG; } - const templateObj = metaObj.getObject('template'); - const templateSource = templateObj.getValue('source'); - const range = getTemplateRange(templateSource, code); - const isInline = templateObj.getBoolean('isInline'); - // We always normalize line endings if the template is inline. - const i18nNormalizeLineEndingsInICUs = isInline || options.i18nNormalizeLineEndingsInICUs; - - const template = parseTemplate(code, sourceUrl, { - escapedString: true, - interpolationConfig: interpolation, - range, - enableI18nLegacyMessageIdFormat: options.enableI18nLegacyMessageIdFormat, - preserveWhitespaces: - metaObj.has('preserveWhitespaces') ? metaObj.getBoolean('preserveWhitespaces') : false, - i18nNormalizeLineEndingsInICUs, - isInline, - }); - if (template.errors !== null) { - const errors = template.errors.map(err => err.toString()).join('\n'); + const interpolationExpr = metaObj.getValue('interpolation'); + const values = interpolationExpr.getArray().map(entry => entry.getString()); + if (values.length !== 2) { throw new FatalLinkerError( - templateSource.expression, `Errors found in the template:\n${errors}`); + interpolationExpr.expression, + 'Unsupported interpolation config, expected an array containing exactly two strings'); } - - let wrapDirectivesAndPipesInClosure = false; - - const directives: R3UsedDirectiveMetadata[] = metaObj.has('directives') ? - metaObj.getArray('directives').map(directive => { - const directiveExpr = directive.getObject(); - const type = directiveExpr.getValue('type'); - const selector = directiveExpr.getString('selector'); - - let typeExpr = type.getOpaque(); - if (type.isFunction()) { - typeExpr = type.getFunctionReturnValue().getOpaque(); - wrapDirectivesAndPipesInClosure = true; - } - return { - type: typeExpr, - selector: selector, - inputs: directiveExpr.has('inputs') ? - directiveExpr.getArray('inputs').map(input => input.getString()) : - [], - outputs: directiveExpr.has('outputs') ? - directiveExpr.getArray('outputs').map(input => input.getString()) : - [], - exportAs: directiveExpr.has('exportAs') ? - directiveExpr.getArray('exportAs').map(exportAs => exportAs.getString()) : - null, - }; - }) : - []; - - const pipes = metaObj.has('pipes') ? metaObj.getObject('pipes').toMap(value => { - if (value.isFunction()) { - wrapDirectivesAndPipesInClosure = true; - return value.getFunctionReturnValue().getOpaque(); - } else { - return value.getOpaque(); - } - }) : - new Map(); - - return { - ...toR3DirectiveMeta(metaObj, code, sourceUrl), - viewProviders: metaObj.has('viewProviders') ? metaObj.getOpaque('viewProviders') : null, - template: { - nodes: template.nodes, - ngContentSelectors: template.ngContentSelectors, - }, - wrapDirectivesAndPipesInClosure, - styles: metaObj.has('styles') ? metaObj.getArray('styles').map(entry => entry.getString()) : [], - encapsulation: metaObj.has('encapsulation') ? - parseEncapsulation(metaObj.getValue('encapsulation')) : - ViewEncapsulation.Emulated, - interpolation, - changeDetection: metaObj.has('changeDetection') ? - parseChangeDetectionStrategy(metaObj.getValue('changeDetection')) : - ChangeDetectionStrategy.Default, - animations: metaObj.has('animations') ? metaObj.getOpaque('animations') : null, - relativeContextFilePath: sourceUrl, - i18nUseExternalIds: options.i18nUseExternalIds, - pipes, - directives, - }; + return InterpolationConfig.fromArray(values as [string, string]); } /** * Determines the `ViewEncapsulation` mode from the AST value's symbol name. */ -function parseEncapsulation(encapsulation: AstValue): ViewEncapsulation { +function parseEncapsulation(encapsulation: AstValue): + ViewEncapsulation { const symbolName = encapsulation.getSymbolName(); if (symbolName === null) { throw new FatalLinkerError( @@ -149,7 +261,8 @@ function parseEncapsulation(encapsulation: AstValue): /** * Determines the `ChangeDetectionStrategy` from the AST value's symbol name. */ -function parseChangeDetectionStrategy(changeDetectionStrategy: AstValue): +function parseChangeDetectionStrategy( + changeDetectionStrategy: AstValue): ChangeDetectionStrategy { const symbolName = changeDetectionStrategy.getSymbolName(); if (symbolName === null) { @@ -166,21 +279,33 @@ function parseChangeDetectionStrategy(changeDetectionStrategy: AstV } /** - * Update the range to remove the start and end chars, which should be quotes around the template. + * Extract the type reference expression from a `forwardRef` function call. For example, the + * expression `forwardRef(function() { return FooDir; })` returns `FooDir`. Note that this + * expression is required to be wrapped in a closure, as otherwise the forward reference would be + * resolved before initialization. */ -function getTemplateRange(templateNode: AstValue, code: string): Range { - const {startPos, endPos, startLine, startCol} = templateNode.getRange(); - - if (!/["'`]/.test(code[startPos]) || code[startPos] !== code[endPos - 1]) { - throw new FatalLinkerError( - templateNode.expression, - `Expected the template string to be wrapped in quotes but got: ${ - code.substring(startPos, endPos)}`); +function extractForwardRef(expr: AstValue): + o.WrappedNodeExpr|null { + if (!expr.isCallExpression()) { + return null; } - return { - startPos: startPos + 1, - endPos: endPos - 1, - startLine, - startCol: startCol + 1, - }; + + const callee = expr.getCallee(); + if (callee.getSymbolName() !== 'forwardRef') { + throw new FatalLinkerError( + callee.expression, 'Unsupported directive type, expected forwardRef or a type reference'); + } + + const args = expr.getArguments(); + if (args.length !== 1) { + throw new FatalLinkerError(expr, 'Unsupported forwardRef call, expected a single argument'); + } + + const wrapperFn = args[0] as AstValue; + if (!wrapperFn.isFunction()) { + throw new FatalLinkerError( + wrapperFn, 'Unsupported forwardRef call, expected a function argument'); + } + + return wrapperFn.getFunctionReturnValue().getOpaque(); } diff --git a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_directive_linker_1.ts b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_directive_linker_1.ts index 10229bb695..16b75c530d 100644 --- a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_directive_linker_1.ts +++ b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_directive_linker_1.ts @@ -5,23 +5,27 @@ * 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 {compileDirectiveFromMetadata, ConstantPool, makeBindingParser, ParseLocation, ParseSourceFile, ParseSourceSpan, R3DirectiveMetadata, R3HostMetadata, R3QueryMetadata, R3Reference} from '@angular/compiler'; +import {compileDirectiveFromMetadata, ConstantPool, makeBindingParser, ParseLocation, ParseSourceFile, ParseSourceSpan, R3DeclareDirectiveMetadata, R3DeclareQueryMetadata, R3DirectiveMetadata, R3HostMetadata, R3PartialDeclaration, R3QueryMetadata} from '@angular/compiler'; import * as o from '@angular/compiler/src/output/output_ast'; +import {AbsoluteFsPath} from '../../../../src/ngtsc/file_system'; import {Range} from '../../ast/ast_host'; import {AstObject, AstValue} from '../../ast/ast_value'; import {FatalLinkerError} from '../../fatal_linker_error'; import {PartialLinker} from './partial_linker'; +import {wrapReference} from './util'; /** * A `PartialLinker` that is designed to process `ɵɵngDeclareDirective()` call expressions. */ export class PartialDirectiveLinkerVersion1 implements PartialLinker { + constructor(private sourceUrl: AbsoluteFsPath, private code: string) {} + linkPartialDeclaration( - sourceUrl: string, code: string, constantPool: ConstantPool, - metaObj: AstObject): o.Expression { - const meta = toR3DirectiveMeta(metaObj, code, sourceUrl); + constantPool: ConstantPool, + metaObj: AstObject): o.Expression { + const meta = toR3DirectiveMeta(metaObj, this.code, this.sourceUrl); const def = compileDirectiveFromMetadata(meta, constantPool, makeBindingParser()); return def.expression; } @@ -31,7 +35,8 @@ export class PartialDirectiveLinkerVersion1 implements PartialLinke * Derives the `R3DirectiveMetadata` structure from the AST object. */ export function toR3DirectiveMeta( - metaObj: AstObject, code: string, sourceUrl: string): R3DirectiveMetadata { + metaObj: AstObject, code: string, + sourceUrl: AbsoluteFsPath): R3DirectiveMetadata { const typeExpr = metaObj.getValue('type'); const typeName = typeExpr.getSymbolName(); if (typeName === null) { @@ -73,7 +78,8 @@ export function toR3DirectiveMeta( /** * Decodes the AST value for a single input to its representation as used in the metadata. */ -function toInputMapping(value: AstValue): string|[string, string] { +function toInputMapping(value: AstValue): + string|[string, string] { if (value.isString()) { return value.getString(); } @@ -90,7 +96,8 @@ function toInputMapping(value: AstValue): string|[stri /** * Extracts the host metadata configuration from the AST metadata object. */ -function toHostMetadata(metaObj: AstObject): R3HostMetadata { +function toHostMetadata(metaObj: AstObject): + R3HostMetadata { if (!metaObj.has('host')) { return { attributes: {}, @@ -127,7 +134,8 @@ function toHostMetadata(metaObj: AstObject): R3HostMet /** * Extracts the metadata for a single query from an AST object. */ -function toQueryMetadata(obj: AstObject): R3QueryMetadata { +function toQueryMetadata(obj: AstObject): + R3QueryMetadata { let predicate: R3QueryMetadata['predicate']; const predicateExpr = obj.getValue('predicate'); if (predicateExpr.isArray()) { @@ -140,15 +148,13 @@ function toQueryMetadata(obj: AstObject): R3QueryMetad first: obj.has('first') ? obj.getBoolean('first') : false, predicate, descendants: obj.has('descendants') ? obj.getBoolean('descendants') : false, + emitDistinctChangesOnly: + obj.has('emitDistinctChangesOnly') ? obj.getBoolean('emitDistinctChangesOnly') : true, read: obj.has('read') ? obj.getOpaque('read') : null, static: obj.has('static') ? obj.getBoolean('static') : false, }; } -function wrapReference(wrapped: o.WrappedNodeExpr): R3Reference { - return {value: wrapped, type: wrapped}; -} - export function createSourceSpan(range: Range, code: string, sourceUrl: string): ParseSourceSpan { const sourceFile = new ParseSourceFile(code, sourceUrl); const startLocation = diff --git a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker.ts b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker.ts index e9d0119793..c75dbc2d93 100644 --- a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker.ts +++ b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker.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 {ConstantPool} from '@angular/compiler'; +import {ConstantPool, R3PartialDeclaration} from '@angular/compiler'; import * as o from '@angular/compiler/src/output/output_ast'; + import {AstObject} from '../../ast/ast_value'; /** @@ -15,8 +16,11 @@ import {AstObject} from '../../ast/ast_value'; export interface PartialLinker { /** * Link the partial declaration `metaObj` information to generate a full definition expression. + * + * @param metaObj An object that fits one of the `R3DeclareDirectiveMetadata` or + * `R3DeclareComponentMetadata` interfaces. */ linkPartialDeclaration( - sourceUrl: string, code: string, constantPool: ConstantPool, - metaObj: AstObject): o.Expression; + constantPool: ConstantPool, + metaObj: AstObject): o.Expression; } diff --git a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker_selector.ts b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker_selector.ts index 4002f285d0..3238d99bbf 100644 --- a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker_selector.ts +++ b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker_selector.ts @@ -5,44 +5,124 @@ * 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 {LinkerOptions} from '../linker_options'; +import {satisfies} from 'semver'; + +import {AbsoluteFsPath} from '../../../../src/ngtsc/file_system'; +import {createGetSourceFile} from '../get_source_file'; +import {LinkerEnvironment} from '../linker_environment'; import {PartialComponentLinkerVersion1} from './partial_component_linker_1'; import {PartialDirectiveLinkerVersion1} from './partial_directive_linker_1'; +import {PartialFactoryLinkerVersion1} from './partial_factory_linker_1'; +import {PartialInjectorLinkerVersion1} from './partial_injector_linker_1'; import {PartialLinker} from './partial_linker'; +import {PartialNgModuleLinkerVersion1} from './partial_ng_module_linker_1'; +import {PartialPipeLinkerVersion1} from './partial_pipe_linker_1'; -export class PartialLinkerSelector { - private linkers: Record>> = { - 'ɵɵngDeclareDirective': { - 1: new PartialDirectiveLinkerVersion1(), - }, - 'ɵɵngDeclareComponent': { - 1: new PartialComponentLinkerVersion1(this.options), - }, - }; +export const ɵɵngDeclareDirective = 'ɵɵngDeclareDirective'; +export const ɵɵngDeclareComponent = 'ɵɵngDeclareComponent'; +export const ɵɵngDeclareFactory = 'ɵɵngDeclareFactory'; +export const ɵɵngDeclareInjector = 'ɵɵngDeclareInjector'; +export const ɵɵngDeclareNgModule = 'ɵɵngDeclareNgModule'; +export const ɵɵngDeclarePipe = 'ɵɵngDeclarePipe'; +export const declarationFunctions = [ + ɵɵngDeclareDirective, ɵɵngDeclareComponent, ɵɵngDeclareFactory, ɵɵngDeclareInjector, + ɵɵngDeclareNgModule, ɵɵngDeclarePipe +]; - constructor(private options: LinkerOptions) {} +interface LinkerRange { + range: string; + linker: PartialLinker; +} + +/** + * A helper that selects the appropriate `PartialLinker` for a given declaration. + * + * The selection is made from a database of linker instances, chosen if their given semver range + * satisfies the version found in the code to be linked. + * + * Note that the ranges are checked in order, and the first matching range will be selected, so + * ranges should be most restrictive first. + * + * Also, ranges are matched to include "pre-releases", therefore if the range is `>=11.1.0-next.1` + * then this includes `11.1.0-next.2` and also `12.0.0-next.1`. + * + * Finally, note that we always start with the current version (i.e. `0.0.0-PLACEHOLDER`). This + * allows the linker to work on local builds effectively. + */ +export class PartialLinkerSelector { + private readonly linkers: Map[]>; + + constructor( + environment: LinkerEnvironment, sourceUrl: AbsoluteFsPath, + code: string) { + this.linkers = this.createLinkerMap(environment, sourceUrl, code); + } /** * Returns true if there are `PartialLinker` classes that can handle functions with this name. */ supportsDeclaration(functionName: string): boolean { - return this.linkers[functionName] !== undefined; + return this.linkers.has(functionName); } /** * Returns the `PartialLinker` that can handle functions with the given name and version. * Throws an error if there is none. */ - getLinker(functionName: string, version: number): PartialLinker { - const versions = this.linkers[functionName]; - if (versions === undefined) { + getLinker(functionName: string, version: string): PartialLinker { + if (!this.linkers.has(functionName)) { throw new Error(`Unknown partial declaration function ${functionName}.`); } - const linker = versions[version]; - if (linker === undefined) { - throw new Error(`Unsupported partial declaration version ${version} for ${functionName}.`); + const versions = this.linkers.get(functionName)!; + for (const {range, linker} of versions) { + if (satisfies(version, range, {includePrerelease: true})) { + return linker; + } } - return linker; + throw new Error( + `Unsupported partial declaration version ${version} for ${functionName}.\n` + + 'Valid version ranges are:\n' + versions.map(v => ` - ${v.range}`).join('\n')); + } + + private createLinkerMap( + environment: LinkerEnvironment, sourceUrl: AbsoluteFsPath, + code: string): Map[]> { + const partialDirectiveLinkerVersion1 = new PartialDirectiveLinkerVersion1(sourceUrl, code); + const partialComponentLinkerVersion1 = new PartialComponentLinkerVersion1( + environment, createGetSourceFile(sourceUrl, code, environment.sourceFileLoader), sourceUrl, + code); + const partialFactoryLinkerVersion1 = new PartialFactoryLinkerVersion1(); + const partialInjectorLinkerVersion1 = new PartialInjectorLinkerVersion1(); + const partialNgModuleLinkerVersion1 = + new PartialNgModuleLinkerVersion1(environment.options.linkerJitMode); + const partialPipeLinkerVersion1 = new PartialPipeLinkerVersion1(); + + const linkers = new Map[]>(); + linkers.set(ɵɵngDeclareDirective, [ + {range: '0.0.0-PLACEHOLDER', linker: partialDirectiveLinkerVersion1}, + {range: '>=11.1.0-next.1', linker: partialDirectiveLinkerVersion1}, + ]); + linkers.set(ɵɵngDeclareComponent, [ + {range: '0.0.0-PLACEHOLDER', linker: partialComponentLinkerVersion1}, + {range: '>=11.1.0-next.1', linker: partialComponentLinkerVersion1}, + ]); + linkers.set(ɵɵngDeclareFactory, [ + {range: '0.0.0-PLACEHOLDER', linker: partialFactoryLinkerVersion1}, + {range: '>=11.1.0-next.1', linker: partialFactoryLinkerVersion1}, + ]); + linkers.set(ɵɵngDeclareInjector, [ + {range: '0.0.0-PLACEHOLDER', linker: partialInjectorLinkerVersion1}, + {range: '>=11.1.0-next.1', linker: partialInjectorLinkerVersion1}, + ]); + linkers.set(ɵɵngDeclareNgModule, [ + {range: '0.0.0-PLACEHOLDER', linker: partialNgModuleLinkerVersion1}, + {range: '>=11.1.0-next.1', linker: partialNgModuleLinkerVersion1}, + ]); + linkers.set(ɵɵngDeclarePipe, [ + {range: '0.0.0-PLACEHOLDER', linker: partialPipeLinkerVersion1}, + {range: '>=11.1.0-next.1', linker: partialPipeLinkerVersion1}, + ]); + return linkers; } } diff --git a/packages/compiler-cli/linker/test/BUILD.bazel b/packages/compiler-cli/linker/test/BUILD.bazel index 5a3008dcc0..e917f02c74 100644 --- a/packages/compiler-cli/linker/test/BUILD.bazel +++ b/packages/compiler-cli/linker/test/BUILD.bazel @@ -12,6 +12,9 @@ ts_library( "//packages:types", "//packages/compiler", "//packages/compiler-cli/linker", + "//packages/compiler-cli/src/ngtsc/file_system", + "//packages/compiler-cli/src/ngtsc/file_system/testing", + "//packages/compiler-cli/src/ngtsc/logging/testing", "//packages/compiler-cli/src/ngtsc/translator", "@npm//typescript", ], diff --git a/packages/compiler-cli/linker/test/ast/ast_value_spec.ts b/packages/compiler-cli/linker/test/ast/ast_value_spec.ts index cdc61f1cd8..2893e80722 100644 --- a/packages/compiler-cli/linker/test/ast/ast_value_spec.ts +++ b/packages/compiler-cli/linker/test/ast/ast_value_spec.ts @@ -9,18 +9,28 @@ import {WrappedNodeExpr} from '@angular/compiler'; import {TypeScriptAstFactory} from '@angular/compiler-cli/src/ngtsc/translator'; import * as ts from 'typescript'; +import {AstHost} from '../../src/ast/ast_host'; import {AstObject, AstValue} from '../../src/ast/ast_value'; import {TypeScriptAstHost} from '../../src/ast/typescript/typescript_ast_host'; -const host = new TypeScriptAstHost(); -const factory = new TypeScriptAstFactory(); +interface TestObject { + a: number; + b: string; + c: boolean; + d: {x: number; y: string}; + e: number[]; + missing: unknown; +} + +const host: AstHost = new TypeScriptAstHost(); +const factory = new TypeScriptAstFactory(/* annotateForClosureCompiler */ false); const nestedObj = factory.createObjectLiteral([ {propertyName: 'x', quoted: false, value: factory.createLiteral(42)}, {propertyName: 'y', quoted: false, value: factory.createLiteral('X')}, ]); const nestedArray = factory.createArrayLiteral([factory.createLiteral(1), factory.createLiteral(2)]); -const obj = AstObject.parse( +const obj = AstObject.parse( factory.createObjectLiteral([ {propertyName: 'a', quoted: false, value: factory.createLiteral(42)}, {propertyName: 'b', quoted: false, value: factory.createLiteral('X')}, @@ -35,7 +45,10 @@ describe('AstObject', () => { it('should return true if the property exists on the object', () => { expect(obj.has('a')).toBe(true); expect(obj.has('b')).toBe(true); - expect(obj.has('z')).toBe(false); + expect(obj.has('missing')).toBe(false); + + // @ts-expect-error + expect(obj.has('x')).toBe(false); }); }); @@ -45,6 +58,7 @@ describe('AstObject', () => { }); it('should throw an error if the property is not a number', () => { + // @ts-expect-error expect(() => obj.getNumber('b')) .toThrowError('Unsupported syntax, expected a numeric literal.'); }); @@ -56,6 +70,7 @@ describe('AstObject', () => { }); it('should throw an error if the property is not a string', () => { + // @ts-expect-error expect(() => obj.getString('a')) .toThrowError('Unsupported syntax, expected a string literal.'); }); @@ -67,6 +82,7 @@ describe('AstObject', () => { }); it('should throw an error if the property is not a boolean', () => { + // @ts-expect-error expect(() => obj.getBoolean('b')) .toThrowError('Unsupported syntax, expected a boolean literal.'); }); @@ -78,6 +94,7 @@ describe('AstObject', () => { }); it('should throw an error if the property is not an object expression', () => { + // @ts-expect-error expect(() => obj.getObject('b')) .toThrowError('Unsupported syntax, expected an object literal.'); }); @@ -93,6 +110,7 @@ describe('AstObject', () => { }); it('should throw an error if the property is not an array of expressions', () => { + // @ts-expect-error expect(() => obj.getArray('b')) .toThrowError('Unsupported syntax, expected an array literal.'); }); @@ -105,7 +123,11 @@ describe('AstObject', () => { }); it('should throw an error if the property does not exist', () => { - expect(() => obj.getOpaque('x')).toThrowError('Expected property \'x\' to be present.'); + expect(() => obj.getOpaque('missing')) + .toThrowError(`Expected property 'missing' to be present.`); + + // @ts-expect-error + expect(() => obj.getOpaque('x')).toThrowError(`Expected property 'x' to be present.`); }); }); @@ -115,7 +137,11 @@ describe('AstObject', () => { }); it('should throw an error if the property does not exist', () => { - expect(() => obj.getNode('x')).toThrowError('Expected property \'x\' to be present.'); + expect(() => obj.getNode('missing')) + .toThrowError(`Expected property 'missing' to be present.`); + + // @ts-expect-error + expect(() => obj.getNode('x')).toThrowError(`Expected property 'x' to be present.`); }); }); @@ -126,7 +152,11 @@ describe('AstObject', () => { }); it('should throw an error if the property does not exist', () => { - expect(() => obj.getValue('x')).toThrowError('Expected property \'x\' to be present.'); + expect(() => obj.getValue('missing')) + .toThrowError(`Expected property 'missing' to be present.`); + + // @ts-expect-error + expect(() => obj.getValue('x')).toThrowError(`Expected property 'x' to be present.`); }); }); @@ -156,126 +186,136 @@ describe('AstObject', () => { }); describe('AstValue', () => { + function createAstValue(node: ts.Expression): AstValue { + return new AstValue(node, host); + } + describe('getSymbolName', () => { it('should return the name of an identifier', () => { - expect(new AstValue(factory.createIdentifier('Foo'), host).getSymbolName()).toEqual('Foo'); + expect(createAstValue(factory.createIdentifier('Foo')).getSymbolName()).toEqual('Foo'); }); it('should return the name of a property access', () => { const propertyAccess = factory.createPropertyAccess( factory.createIdentifier('Foo'), factory.createIdentifier('Bar')); - expect(new AstValue(propertyAccess, host).getSymbolName()).toEqual('Bar'); + expect(createAstValue(propertyAccess).getSymbolName()).toEqual('Bar'); }); it('should return null if no symbol name is available', () => { - expect(new AstValue(factory.createLiteral('a'), host).getSymbolName()).toBeNull(); + expect(createAstValue(factory.createLiteral('a')).getSymbolName()).toBeNull(); }); }); describe('isNumber', () => { it('should return true if the value is a number', () => { - expect(new AstValue(factory.createLiteral(42), host).isNumber()).toEqual(true); + expect(createAstValue(factory.createLiteral(42)).isNumber()).toEqual(true); }); it('should return false if the value is not a number', () => { - expect(new AstValue(factory.createLiteral('a'), host).isNumber()).toEqual(false); + expect(createAstValue(factory.createLiteral('a')).isNumber()).toEqual(false); }); }); describe('getNumber', () => { it('should return the number value of the AstValue', () => { - expect(new AstValue(factory.createLiteral(42), host).getNumber()).toEqual(42); + expect(createAstValue(factory.createLiteral(42)).getNumber()).toEqual(42); }); it('should throw an error if the property is not a number', () => { - expect(() => new AstValue(factory.createLiteral('a'), host).getNumber()) + // @ts-expect-error + expect(() => createAstValue(factory.createLiteral('a')).getNumber()) .toThrowError('Unsupported syntax, expected a numeric literal.'); }); }); describe('isString', () => { it('should return true if the value is a string', () => { - expect(new AstValue(factory.createLiteral('a'), host).isString()).toEqual(true); + expect(createAstValue(factory.createLiteral('a')).isString()).toEqual(true); }); it('should return false if the value is not a string', () => { - expect(new AstValue(factory.createLiteral(42), host).isString()).toEqual(false); + expect(createAstValue(factory.createLiteral(42)).isString()).toEqual(false); }); }); describe('getString', () => { it('should return the string value of the AstValue', () => { - expect(new AstValue(factory.createLiteral('X'), host).getString()).toEqual('X'); + expect(createAstValue(factory.createLiteral('X')).getString()).toEqual('X'); }); it('should throw an error if the property is not a string', () => { - expect(() => new AstValue(factory.createLiteral(42), host).getString()) + // @ts-expect-error + expect(() => createAstValue(factory.createLiteral(42)).getString()) .toThrowError('Unsupported syntax, expected a string literal.'); }); }); describe('isBoolean', () => { it('should return true if the value is a boolean', () => { - expect(new AstValue(factory.createLiteral(true), host).isBoolean()).toEqual(true); + expect(createAstValue(factory.createLiteral(true)).isBoolean()).toEqual(true); }); it('should return false if the value is not a boolean', () => { - expect(new AstValue(factory.createLiteral(42), host).isBoolean()).toEqual(false); + expect(createAstValue(factory.createLiteral(42)).isBoolean()).toEqual(false); }); }); describe('getBoolean', () => { it('should return the boolean value of the AstValue', () => { - expect(new AstValue(factory.createLiteral(true), host).getBoolean()).toEqual(true); + expect(createAstValue(factory.createLiteral(true)).getBoolean()).toEqual(true); }); it('should throw an error if the property is not a boolean', () => { - expect(() => new AstValue(factory.createLiteral(42), host).getBoolean()) + // @ts-expect-error + expect(() => createAstValue(factory.createLiteral(42)).getBoolean()) .toThrowError('Unsupported syntax, expected a boolean literal.'); }); }); describe('isObject', () => { it('should return true if the value is an object literal', () => { - expect(new AstValue(nestedObj, host).isObject()).toEqual(true); + expect(createAstValue(nestedObj).isObject()).toEqual(true); }); it('should return false if the value is not an object literal', () => { - expect(new AstValue(factory.createLiteral(42), host).isObject()).toEqual(false); + expect(createAstValue(factory.createLiteral(42)).isObject()).toEqual(false); }); }); describe('getObject', () => { it('should return the AstObject value of the AstValue', () => { - expect(new AstValue(nestedObj, host).getObject()).toEqual(AstObject.parse(nestedObj, host)); + expect(createAstValue(nestedObj).getObject()) + .toEqual(AstObject.parse(nestedObj, host)); }); it('should throw an error if the property is not an object literal', () => { - expect(() => new AstValue(factory.createLiteral(42), host).getObject()) + // @ts-expect-error + expect(() => createAstValue(factory.createLiteral(42)).getObject()) .toThrowError('Unsupported syntax, expected an object literal.'); }); }); describe('isArray', () => { it('should return true if the value is an array literal', () => { - expect(new AstValue(nestedArray, host).isArray()).toEqual(true); + expect(createAstValue(nestedArray).isArray()).toEqual(true); }); it('should return false if the value is not an object literal', () => { - expect(new AstValue(factory.createLiteral(42), host).isArray()).toEqual(false); + expect(createAstValue(factory.createLiteral(42)).isArray()).toEqual(false); }); }); describe('getArray', () => { it('should return an array of AstValue objects from the AstValue', () => { - expect(new AstValue(nestedArray, host).getArray()).toEqual([ - new AstValue(factory.createLiteral(1), host), - new AstValue(factory.createLiteral(2), host), + expect(createAstValue(nestedArray).getArray()).toEqual([ + createAstValue(factory.createLiteral(1)), + createAstValue(factory.createLiteral(2)), ]); }); it('should throw an error if the property is not an array', () => { - expect(() => new AstValue(factory.createLiteral(42), host).getArray()) + // @ts-expect-error + expect(() => createAstValue(factory.createLiteral(42)).getArray()) .toThrowError('Unsupported syntax, expected an array literal.'); }); }); @@ -285,11 +325,11 @@ describe('AstValue', () => { const funcExpr = factory.createFunctionExpression( 'foo', [], factory.createBlock([factory.createReturnStatement(factory.createLiteral(42))])); - expect(new AstValue(funcExpr, host).isFunction()).toEqual(true); + expect(createAstValue(funcExpr).isFunction()).toEqual(true); }); it('should return false if the value is not a function expression', () => { - expect(new AstValue(factory.createLiteral(42), host).isFunction()).toEqual(false); + expect(createAstValue(factory.createLiteral(42)).isFunction()).toEqual(false); }); }); @@ -298,12 +338,13 @@ describe('AstValue', () => { const funcExpr = factory.createFunctionExpression( 'foo', [], factory.createBlock([factory.createReturnStatement(factory.createLiteral(42))])); - expect(new AstValue(funcExpr, host).getFunctionReturnValue()) - .toEqual(new AstValue(factory.createLiteral(42), host)); + expect(createAstValue(funcExpr).getFunctionReturnValue()) + .toEqual(createAstValue(factory.createLiteral(42))); }); it('should throw an error if the property is not a function expression', () => { - expect(() => new AstValue(factory.createLiteral(42), host).getFunctionReturnValue()) + // @ts-expect-error + expect(() => createAstValue(factory.createLiteral(42)).getFunctionReturnValue()) .toThrowError('Unsupported syntax, expected a function.'); }); @@ -312,17 +353,63 @@ describe('AstValue', () => { const funcExpr = factory.createFunctionExpression( 'foo', [], factory.createBlock([factory.createExpressionStatement( factory.createLiteral('do nothing'))])); - expect(() => new AstValue(funcExpr, host).getFunctionReturnValue()) + expect(() => createAstValue(funcExpr).getFunctionReturnValue()) .toThrowError( 'Unsupported syntax, expected a function body with a single return statement.'); }); }); + describe('isCallExpression', () => { + it('should return true if the value represents a call expression', () => { + const callExpr = factory.createCallExpression(factory.createIdentifier('foo'), [], false); + expect(createAstValue(callExpr).isCallExpression()).toBe(true); + }); + + it('should return false if the value does not represent a call expression', () => { + const fooExpr = factory.createIdentifier('foo'); + expect(createAstValue(fooExpr).isCallExpression()).toBe(false); + }); + }); + + describe('getCallee', () => { + it('should return the callee expression as a value', () => { + const callExpr = factory.createCallExpression(factory.createIdentifier('foo'), [], false); + expect(createAstValue(callExpr).getCallee()) + .toEqual(createAstValue(factory.createIdentifier('foo'))); + }); + + it('should throw an error if the value is not a call expression', () => { + expect(() => createAstValue(factory.createLiteral(42)).getCallee()) + .toThrowError('Unsupported syntax, expected a call expression.'); + }); + }); + + describe('getArguments', () => { + it('should return the arguments as an array of values', () => { + const callExpr = factory.createCallExpression( + factory.createIdentifier('foo'), + [ + factory.createLiteral(1), + factory.createLiteral(2), + ], + false); + expect(createAstValue(callExpr).getArguments()).toEqual([ + createAstValue(factory.createLiteral(1)), + createAstValue(factory.createLiteral(2)), + ]); + }); + + it('should throw an error if the value is not a call expression', () => { + expect(() => createAstValue(factory.createLiteral(42)).getArguments()) + .toThrowError('Unsupported syntax, expected a call expression.'); + }); + }); + describe('getOpaque()', () => { it('should return the value wrapped in a `WrappedNodeExpr`', () => { - expect(new AstValue(factory.createLiteral(42), host).getOpaque()) + expect(createAstValue(factory.createLiteral(42)).getOpaque()) .toEqual(jasmine.any(WrappedNodeExpr)); - expect(new AstValue(factory.createLiteral(42), host).getOpaque().node) + expect(createAstValue(factory.createLiteral(42)).getOpaque().node) .toEqual(factory.createLiteral(42)); }); }); @@ -339,7 +426,7 @@ describe('AstValue', () => { (stmt.expression as ts.AssignmentExpression>).right; // Check that this string literal has the expected range. - expect(new AstValue(mooString, host).getRange()) + expect(createAstValue(mooString).getRange()) .toEqual({startLine: 1, startCol: 4, startPos: 16, endPos: 21}); }); }); diff --git a/packages/compiler-cli/linker/test/ast/typescript/typescript_ast_host_spec.ts b/packages/compiler-cli/linker/test/ast/typescript/typescript_ast_host_spec.ts index 68dd6ad9b4..c904b50a4c 100644 --- a/packages/compiler-cli/linker/test/ast/typescript/typescript_ast_host_spec.ts +++ b/packages/compiler-cli/linker/test/ast/typescript/typescript_ast_host_spec.ts @@ -257,6 +257,59 @@ describe('TypeScriptAstHost', () => { }); }); + describe('isCallExpression()', () => { + it('should return true if the expression is a call expression', () => { + expect(host.isCallExpression(expr('foo()'))).toBe(true); + expect(host.isCallExpression(expr('foo.bar()'))).toBe(true); + expect(host.isCallExpression(expr('(foo)(1)'))).toBe(true); + }); + + it('should return false if the expression is not a call expression', () => { + expect(host.isCallExpression(expr('[]'))).toBe(false); + expect(host.isCallExpression(expr('"moo"'))).toBe(false); + expect(host.isCallExpression(expr('\'moo\''))).toBe(false); + expect(host.isCallExpression(expr('someIdentifier'))).toBe(false); + expect(host.isCallExpression(expr('42'))).toBe(false); + expect(host.isCallExpression(rhs('x = {}'))).toBe(false); + expect(host.isCallExpression(expr('null'))).toBe(false); + expect(host.isCallExpression(expr('\'a\' + \'b\''))).toBe(false); + expect(host.isCallExpression(expr('\`moo\`'))).toBe(false); + }); + }); + + describe('parseCallee()', () => { + it('should return the callee expression', () => { + const foo = jasmine.objectContaining({text: 'foo', kind: ts.SyntaxKind.Identifier}); + const bar = jasmine.objectContaining({kind: ts.SyntaxKind.PropertyAccessExpression}); + expect(host.parseCallee(expr('foo()'))).toEqual(foo); + expect(host.parseCallee(expr('foo.bar()'))).toEqual(bar); + }); + + it('should error if the node is not a call expression', () => { + expect(() => host.parseCallee(expr('[]'))) + .toThrowError('Unsupported syntax, expected a call expression.'); + }); + }); + + describe('parseArguments()', () => { + it('should return the arguments as an array of expressions', () => { + const arg1 = jasmine.objectContaining({text: '12', kind: ts.SyntaxKind.NumericLiteral}); + const arg2 = jasmine.objectContaining({kind: ts.SyntaxKind.ArrayLiteralExpression}); + expect(host.parseArguments(expr('foo(12, [])'))).toEqual([arg1, arg2]); + expect(host.parseArguments(expr('foo.bar()'))).toEqual([]); + }); + + it('should error if the node is not a call expression', () => { + expect(() => host.parseArguments(expr('[]'))) + .toThrowError('Unsupported syntax, expected a call expression.'); + }); + + it('should error if an argument uses spread syntax', () => { + expect(() => host.parseArguments(expr('foo(1, ...[])'))) + .toThrowError('Unsupported syntax, expected argument not to use spread syntax.'); + }); + }); + describe('getRange()', () => { it('should extract the range from the expression', () => { const moo = rhs('// preamble\nx = \'moo\';'); diff --git a/packages/compiler-cli/linker/test/file_linker/emit_scopes/emit_scope_spec.ts b/packages/compiler-cli/linker/test/file_linker/emit_scopes/emit_scope_spec.ts index 3484f3978d..3e0568a740 100644 --- a/packages/compiler-cli/linker/test/file_linker/emit_scopes/emit_scope_spec.ts +++ b/packages/compiler-cli/linker/test/file_linker/emit_scopes/emit_scope_spec.ts @@ -9,31 +9,27 @@ import * as o from '@angular/compiler/src/output/output_ast'; import * as ts from 'typescript'; import {TypeScriptAstFactory} from '../../../../src/ngtsc/translator'; -import {TypeScriptAstHost} from '../../../src/ast/typescript/typescript_ast_host'; import {EmitScope} from '../../../src/file_linker/emit_scopes/emit_scope'; -import {LinkerEnvironment} from '../../../src/file_linker/linker_environment'; -import {DEFAULT_LINKER_OPTIONS} from '../../../src/file_linker/linker_options'; +import {Translator} from '../../../src/file_linker/translator'; import {generate} from '../helpers'; describe('EmitScope', () => { describe('translateDefinition()', () => { it('should translate the given output AST into a TExpression', () => { - const factory = new TypeScriptAstFactory(); - const linkerEnvironment = LinkerEnvironment.create( - new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS); + const factory = new TypeScriptAstFactory(/* annotateForClosureCompiler */ false); + const translator = new Translator(factory); const ngImport = factory.createIdentifier('core'); - const emitScope = new EmitScope(ngImport, linkerEnvironment); + const emitScope = new EmitScope(ngImport, translator); const def = emitScope.translateDefinition(o.fn([], [], null, null, 'foo')); expect(generate(def)).toEqual('function foo() { }'); }); it('should use the `ngImport` idenfifier for imports when translating', () => { - const factory = new TypeScriptAstFactory(); - const linkerEnvironment = LinkerEnvironment.create( - new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS); + const factory = new TypeScriptAstFactory(/* annotateForClosureCompiler */ false); + const translator = new Translator(factory); const ngImport = factory.createIdentifier('core'); - const emitScope = new EmitScope(ngImport, linkerEnvironment); + const emitScope = new EmitScope(ngImport, translator); const coreImportRef = new o.ExternalReference('@angular/core', 'foo'); const def = emitScope.translateDefinition(o.importExpr(coreImportRef).callMethod('bar', [])); @@ -41,11 +37,10 @@ describe('EmitScope', () => { }); it('should not emit any shared constants in the replacement expression', () => { - const factory = new TypeScriptAstFactory(); - const linkerEnvironment = LinkerEnvironment.create( - new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS); + const factory = new TypeScriptAstFactory(/* annotateForClosureCompiler */ false); + const translator = new Translator(factory); const ngImport = factory.createIdentifier('core'); - const emitScope = new EmitScope(ngImport, linkerEnvironment); + const emitScope = new EmitScope(ngImport, translator); const constArray = o.literalArr([o.literal('CONST')]); // We have to add the constant twice or it will not create a shared statement @@ -59,11 +54,10 @@ describe('EmitScope', () => { describe('getConstantStatements()', () => { it('should return any constant statements that were added to the `constantPool`', () => { - const factory = new TypeScriptAstFactory(); - const linkerEnvironment = LinkerEnvironment.create( - new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS); + const factory = new TypeScriptAstFactory(/* annotateForClosureCompiler */ false); + const translator = new Translator(factory); const ngImport = factory.createIdentifier('core'); - const emitScope = new EmitScope(ngImport, linkerEnvironment); + const emitScope = new EmitScope(ngImport, translator); const constArray = o.literalArr([o.literal('CONST')]); // We have to add the constant twice or it will not create a shared statement diff --git a/packages/compiler-cli/linker/test/file_linker/emit_scopes/iief_emit_scope_spec.ts b/packages/compiler-cli/linker/test/file_linker/emit_scopes/iief_emit_scope_spec.ts index b4d12f8269..408d396dea 100644 --- a/packages/compiler-cli/linker/test/file_linker/emit_scopes/iief_emit_scope_spec.ts +++ b/packages/compiler-cli/linker/test/file_linker/emit_scopes/iief_emit_scope_spec.ts @@ -9,31 +9,29 @@ import * as o from '@angular/compiler/src/output/output_ast'; import * as ts from 'typescript'; import {TypeScriptAstFactory} from '../../../../src/ngtsc/translator'; -import {TypeScriptAstHost} from '../../../src/ast/typescript/typescript_ast_host'; import {IifeEmitScope} from '../../../src/file_linker/emit_scopes/iife_emit_scope'; -import {LinkerEnvironment} from '../../../src/file_linker/linker_environment'; -import {DEFAULT_LINKER_OPTIONS} from '../../../src/file_linker/linker_options'; +import {Translator} from '../../../src/file_linker/translator'; import {generate} from '../helpers'; describe('IifeEmitScope', () => { describe('translateDefinition()', () => { it('should translate the given output AST into a TExpression, wrapped in an IIFE', () => { - const factory = new TypeScriptAstFactory(); - const linkerEnvironment = LinkerEnvironment.create( - new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS); + const factory = new TypeScriptAstFactory(/* annotateForClosureCompiler */ false); + const translator = new Translator(factory); const ngImport = factory.createIdentifier('core'); - const emitScope = new IifeEmitScope(ngImport, linkerEnvironment); + const emitScope = + new IifeEmitScope(ngImport, translator, factory); const def = emitScope.translateDefinition(o.fn([], [], null, null, 'foo')); expect(generate(def)).toEqual('function () { return function foo() { }; }()'); }); it('should use the `ngImport` idenfifier for imports when translating', () => { - const factory = new TypeScriptAstFactory(); - const linkerEnvironment = LinkerEnvironment.create( - new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS); + const factory = new TypeScriptAstFactory(/* annotateForClosureCompiler */ false); + const translator = new Translator(factory); const ngImport = factory.createIdentifier('core'); - const emitScope = new IifeEmitScope(ngImport, linkerEnvironment); + const emitScope = + new IifeEmitScope(ngImport, translator, factory); const coreImportRef = new o.ExternalReference('@angular/core', 'foo'); const def = emitScope.translateDefinition(o.importExpr(coreImportRef).callMethod('bar', [])); @@ -41,11 +39,11 @@ describe('IifeEmitScope', () => { }); it('should emit any shared constants in the replacement expression IIFE', () => { - const factory = new TypeScriptAstFactory(); - const linkerEnvironment = LinkerEnvironment.create( - new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS); + const factory = new TypeScriptAstFactory(/* annotateForClosureCompiler */ false); + const translator = new Translator(factory); const ngImport = factory.createIdentifier('core'); - const emitScope = new IifeEmitScope(ngImport, linkerEnvironment); + const emitScope = + new IifeEmitScope(ngImport, translator, factory); const constArray = o.literalArr([o.literal('CONST')]); // We have to add the constant twice or it will not create a shared statement @@ -60,11 +58,11 @@ describe('IifeEmitScope', () => { describe('getConstantStatements()', () => { it('should throw an error', () => { - const factory = new TypeScriptAstFactory(); - const linkerEnvironment = LinkerEnvironment.create( - new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS); + const factory = new TypeScriptAstFactory(/* annotateForClosureCompiler */ false); + const translator = new Translator(factory); const ngImport = factory.createIdentifier('core'); - const emitScope = new IifeEmitScope(ngImport, linkerEnvironment); + const emitScope = + new IifeEmitScope(ngImport, translator, factory); expect(() => emitScope.getConstantStatements()).toThrowError(); }); }); diff --git a/packages/compiler-cli/linker/test/file_linker/file_linker_spec.ts b/packages/compiler-cli/linker/test/file_linker/file_linker_spec.ts index 583868992b..cb684a8edc 100644 --- a/packages/compiler-cli/linker/test/file_linker/file_linker_spec.ts +++ b/packages/compiler-cli/linker/test/file_linker/file_linker_spec.ts @@ -8,6 +8,8 @@ import * as o from '@angular/compiler/src/output/output_ast'; import * as ts from 'typescript'; +import {MockFileSystemNative} from '../../../src/ngtsc/file_system/testing'; +import {MockLogger} from '../../../src/ngtsc/logging/testing'; import {TypeScriptAstFactory} from '../../../src/ngtsc/translator'; import {AstHost} from '../../src/ast/ast_host'; import {TypeScriptAstHost} from '../../src/ast/typescript/typescript_ast_host'; @@ -16,11 +18,12 @@ import {FileLinker} from '../../src/file_linker/file_linker'; import {LinkerEnvironment} from '../../src/file_linker/linker_environment'; import {DEFAULT_LINKER_OPTIONS} from '../../src/file_linker/linker_options'; import {PartialDirectiveLinkerVersion1} from '../../src/file_linker/partial_linkers/partial_directive_linker_1'; + import {generate} from './helpers'; describe('FileLinker', () => { let factory: TypeScriptAstFactory; - beforeEach(() => factory = new TypeScriptAstFactory()); + beforeEach(() => factory = new TypeScriptAstFactory(/* annotateForClosureCompiler */ false)); describe('isPartialDeclaration()', () => { it('should return true if the callee is recognized', () => { @@ -38,7 +41,7 @@ describe('FileLinker', () => { describe('linkPartialDeclaration()', () => { it('should throw an error if the function name is not recognised', () => { const {fileLinker} = createFileLinker(); - const version = factory.createLiteral(1); + const version = factory.createLiteral('0.0.0-PLACEHOLDER'); const ngImport = factory.createIdentifier('core'); const declarationArg = factory.createObjectLiteral([ {propertyName: 'version', quoted: false, value: version}, @@ -80,7 +83,7 @@ describe('FileLinker', () => { .and.returnValue(o.literal('compilation result')); const ngImport = factory.createIdentifier('core'); - const version = factory.createLiteral(1); + const version = factory.createLiteral('0.0.0-PLACEHOLDER'); const declarationArg = factory.createObjectLiteral([ {propertyName: 'ngImport', quoted: false, value: ngImport}, {propertyName: 'version', quoted: false, value: version}, @@ -91,7 +94,7 @@ describe('FileLinker', () => { expect(compilationResult).toEqual(factory.createLiteral('compilation result')); expect(compileSpy).toHaveBeenCalled(); - expect(compileSpy.calls.mostRecent().args[3].getNode('ngImport')).toBe(ngImport); + expect(compileSpy.calls.mostRecent().args[1].getNode('ngImport')).toBe(ngImport); }); }); @@ -104,7 +107,7 @@ describe('FileLinker', () => { // constant statements. const declarationArg = factory.createObjectLiteral([ {propertyName: 'ngImport', quoted: false, value: factory.createIdentifier('core')}, - {propertyName: 'version', quoted: false, value: factory.createLiteral(1)}, + {propertyName: 'version', quoted: false, value: factory.createLiteral('0.0.0-PLACEHOLDER')}, ]); const replacement = fileLinker.linkPartialDeclaration( @@ -127,7 +130,11 @@ describe('FileLinker', () => { // statements to be emitted in an IIFE rather than added to the shared constant scope. const declarationArg = factory.createObjectLiteral([ {propertyName: 'ngImport', quoted: false, value: factory.createLiteral('not-a-module')}, - {propertyName: 'version', quoted: false, value: factory.createLiteral(1)}, + { + propertyName: 'version', + quoted: false, + value: factory.createLiteral('0.0.0-PLACEHOLDER') + }, ]); const replacement = fileLinker.linkPartialDeclaration( @@ -144,10 +151,13 @@ describe('FileLinker', () => { host: AstHost, fileLinker: FileLinker } { + const fs = new MockFileSystemNative(); + const logger = new MockLogger(); const linkerEnvironment = LinkerEnvironment.create( - new TypeScriptAstHost(), new TypeScriptAstFactory(), DEFAULT_LINKER_OPTIONS); + fs, logger, new TypeScriptAstHost(), + new TypeScriptAstFactory(/* annotateForClosureCompiler */ false), DEFAULT_LINKER_OPTIONS); const fileLinker = new FileLinker( - linkerEnvironment, 'test.js', '// test code'); + linkerEnvironment, fs.resolve('/test.js'), '// test code'); return {host: linkerEnvironment.host, fileLinker}; } }); @@ -181,7 +191,7 @@ class MockConstantScopeRef { function spyOnLinkPartialDeclarationWithConstants(replacement: o.Expression) { let callCount = 0; spyOn(PartialDirectiveLinkerVersion1.prototype, 'linkPartialDeclaration') - .and.callFake(((sourceUrl, code, constantPool) => { + .and.callFake((constantPool => { const constArray = o.literalArr([o.literal(++callCount)]); // We have to add the constant twice or it will not create a shared statement constantPool.getConstLiteral(constArray); diff --git a/packages/compiler-cli/linker/test/file_linker/partial_linkers/partial_linker_selector_spec.ts b/packages/compiler-cli/linker/test/file_linker/partial_linkers/partial_linker_selector_spec.ts index 002f77bf46..ca10c691c8 100644 --- a/packages/compiler-cli/linker/test/file_linker/partial_linkers/partial_linker_selector_spec.ts +++ b/packages/compiler-cli/linker/test/file_linker/partial_linkers/partial_linker_selector_spec.ts @@ -5,44 +5,126 @@ * 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 {LinkerOptions} from '../../..'; +import {DEFAULT_LINKER_OPTIONS, LinkerOptions} from '../../..'; +import {FileSystem} from '../../../../src/ngtsc/file_system'; +import {MockFileSystemNative} from '../../../../src/ngtsc/file_system/testing'; +import {MockLogger} from '../../../../src/ngtsc/logging/testing'; +import {TypeScriptAstFactory} from '../../../../src/ngtsc/translator'; +import {TypeScriptAstHost} from '../../../src/ast/typescript/typescript_ast_host'; +import {LinkerEnvironment} from '../../../src/file_linker/linker_environment'; import {PartialComponentLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_component_linker_1'; import {PartialDirectiveLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_directive_linker_1'; +import {PartialFactoryLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_factory_linker_1'; +import {PartialInjectorLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_injector_linker_1'; import {PartialLinkerSelector} from '../../../src/file_linker/partial_linkers/partial_linker_selector'; +import {PartialNgModuleLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_ng_module_linker_1'; +import {PartialPipeLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_pipe_linker_1'; describe('PartialLinkerSelector', () => { - const options: LinkerOptions = { - i18nNormalizeLineEndingsInICUs: true, - enableI18nLegacyMessageIdFormat: false, - i18nUseExternalIds: false, - }; + const options: LinkerOptions = DEFAULT_LINKER_OPTIONS; + let environment: LinkerEnvironment; + let fs: FileSystem; + + beforeEach(() => { + fs = new MockFileSystemNative(); + const logger = new MockLogger(); + environment = LinkerEnvironment.create( + fs, logger, new TypeScriptAstHost(), + new TypeScriptAstFactory(/* annotateForClosureCompiler */ false), options); + }); describe('supportsDeclaration()', () => { it('should return true if there is at least one linker that matches the given function name', () => { - const selector = new PartialLinkerSelector(options); + const selector = new PartialLinkerSelector( + environment, fs.resolve('/some/path/to/file.js'), 'some file contents'); expect(selector.supportsDeclaration('ɵɵngDeclareDirective')).toBe(true); expect(selector.supportsDeclaration('ɵɵngDeclareComponent')).toBe(true); + expect(selector.supportsDeclaration('ɵɵngDeclareFactory')).toBe(true); + expect(selector.supportsDeclaration('ɵɵngDeclareInjector')).toBe(true); + expect(selector.supportsDeclaration('ɵɵngDeclareNgModule')).toBe(true); + expect(selector.supportsDeclaration('ɵɵngDeclarePipe')).toBe(true); expect(selector.supportsDeclaration('$foo')).toBe(false); }); + + it('should return false for methods on `Object`', () => { + const selector = new PartialLinkerSelector( + environment, fs.resolve('/some/path/to/file.js'), 'some file contents'); + expect(selector.supportsDeclaration('toString')).toBe(false); + }); }); describe('getLinker()', () => { - it('should return the linker that matches the name and version number', () => { - const selector = new PartialLinkerSelector(options); - expect(selector.getLinker('ɵɵngDeclareDirective', 1)) + it('should return the latest linker if the version is "0.0.0-PLACEHOLDER"', () => { + const selector = new PartialLinkerSelector( + environment, fs.resolve('/some/path/to/file.js'), 'some file contents'); + expect(selector.getLinker('ɵɵngDeclareDirective', '0.0.0-PLACEHOLDER')) .toBeInstanceOf(PartialDirectiveLinkerVersion1); - expect(selector.getLinker('ɵɵngDeclareComponent', 1)) + expect(selector.getLinker('ɵɵngDeclareComponent', '0.0.0-PLACEHOLDER')) .toBeInstanceOf(PartialComponentLinkerVersion1); + expect(selector.getLinker('ɵɵngDeclareFactory', '0.0.0-PLACEHOLDER')) + .toBeInstanceOf(PartialFactoryLinkerVersion1); + expect(selector.getLinker('ɵɵngDeclareInjector', '0.0.0-PLACEHOLDER')) + .toBeInstanceOf(PartialInjectorLinkerVersion1); + expect(selector.getLinker('ɵɵngDeclareNgModule', '0.0.0-PLACEHOLDER')) + .toBeInstanceOf(PartialNgModuleLinkerVersion1); + expect(selector.getLinker('ɵɵngDeclarePipe', '0.0.0-PLACEHOLDER')) + .toBeInstanceOf(PartialPipeLinkerVersion1); + }); + + it('should return the linker that matches the name and valid full version', () => { + const selector = new PartialLinkerSelector( + environment, fs.resolve('/some/path/to/file.js'), 'some file contents'); + expect(selector.getLinker('ɵɵngDeclareDirective', '11.1.2')) + .toBeInstanceOf(PartialDirectiveLinkerVersion1); + expect(selector.getLinker('ɵɵngDeclareDirective', '11.2.5')) + .toBeInstanceOf(PartialDirectiveLinkerVersion1); + expect(selector.getLinker('ɵɵngDeclareDirective', '12.0.0')) + .toBeInstanceOf(PartialDirectiveLinkerVersion1); + }); + + it('should return the linker that matches the name and valid pre-release versions', () => { + const selector = new PartialLinkerSelector( + environment, fs.resolve('/some/path/to/file.js'), 'some file contents'); + expect(selector.getLinker('ɵɵngDeclareDirective', '11.1.0-next.1')) + .toBeInstanceOf(PartialDirectiveLinkerVersion1); + expect(selector.getLinker('ɵɵngDeclareDirective', '11.1.0-next.7')) + .toBeInstanceOf(PartialDirectiveLinkerVersion1); + expect(selector.getLinker('ɵɵngDeclareDirective', '12.0.0-next.7')) + .toBeInstanceOf(PartialDirectiveLinkerVersion1); }); it('should throw an error if there is no linker that matches the given name or version', () => { - const selector = new PartialLinkerSelector(options); - expect(() => selector.getLinker('$foo', 1)) + const selector = new PartialLinkerSelector( + environment, fs.resolve('/some/path/to/file.js'), 'some file contents'); + // `$foo` is not a valid name, even though `0.0.0-PLACEHOLDER` is a valid version + expect(() => selector.getLinker('$foo', '0.0.0-PLACEHOLDER')) .toThrowError('Unknown partial declaration function $foo.'); - expect(() => selector.getLinker('ɵɵngDeclareDirective', 2)) - .toThrowError('Unsupported partial declaration version 2 for ɵɵngDeclareDirective.'); + // `$foo` is not a valid name, even though `11.1.0` is a valid version + expect(() => selector.getLinker('$foo', '11.1.0')) + .toThrowError('Unknown partial declaration function $foo.'); + + // There are no linkers earlier than 11.1.0-next.1 + expect(() => selector.getLinker('ɵɵngDeclareDirective', '10.2.1')) + .toThrowError( + 'Unsupported partial declaration version 10.2.1 for ɵɵngDeclareDirective.\n' + + 'Valid version ranges are:\n' + + ' - 0.0.0-PLACEHOLDER\n' + + ' - >=11.1.0-next.1'); + expect(() => selector.getLinker('ɵɵngDeclareDirective', '11.0.2')) + .toThrowError( + 'Unsupported partial declaration version 11.0.2 for ɵɵngDeclareDirective.\n' + + 'Valid version ranges are:\n' + + ' - 0.0.0-PLACEHOLDER\n' + + ' - >=11.1.0-next.1'); + expect(() => selector.getLinker('ɵɵngDeclareDirective', '11.1.0-next.0')) + .toThrowError( + 'Unsupported partial declaration version 11.1.0-next.0 for ɵɵngDeclareDirective.\n' + + 'Valid version ranges are:\n' + + ' - 0.0.0-PLACEHOLDER\n' + + ' - >=11.1.0-next.1'); }); }); }); diff --git a/packages/compiler-cli/linker/test/file_linker/translator_spec.ts b/packages/compiler-cli/linker/test/file_linker/translator_spec.ts index 31d0401f0b..233cd38e9a 100644 --- a/packages/compiler-cli/linker/test/file_linker/translator_spec.ts +++ b/packages/compiler-cli/linker/test/file_linker/translator_spec.ts @@ -14,7 +14,7 @@ import {generate} from './helpers'; describe('Translator', () => { let factory: TypeScriptAstFactory; - beforeEach(() => factory = new TypeScriptAstFactory()); + beforeEach(() => factory = new TypeScriptAstFactory(/* annotateForClosureCompiler */ false)); describe('translateExpression()', () => { it('should generate expression specific output', () => { diff --git a/packages/compiler-cli/ngcc/BUILD.bazel b/packages/compiler-cli/ngcc/BUILD.bazel index 6241361c09..6502721b91 100644 --- a/packages/compiler-cli/ngcc/BUILD.bazel +++ b/packages/compiler-cli/ngcc/BUILD.bazel @@ -19,6 +19,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/file_system", "//packages/compiler-cli/src/ngtsc/imports", "//packages/compiler-cli/src/ngtsc/incremental:api", + "//packages/compiler-cli/src/ngtsc/incremental/semantic_graph", "//packages/compiler-cli/src/ngtsc/logging", "//packages/compiler-cli/src/ngtsc/metadata", "//packages/compiler-cli/src/ngtsc/partial_evaluator", diff --git a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts index b6cd153955..ce1a5a154b 100644 --- a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts +++ b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts @@ -6,17 +6,19 @@ * found in the LICENSE file at https://angular.io/license */ import {ConstantPool} from '@angular/compiler'; +import {NOOP_PERF_RECORDER} from '@angular/compiler-cli/src/ngtsc/perf'; 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 {CycleAnalyzer, CycleHandlingStrategy, ImportGraph} from '../../../src/ngtsc/cycles'; import {isFatalDiagnosticError} from '../../../src/ngtsc/diagnostics'; -import {absoluteFrom, absoluteFromSourceFile, dirname, FileSystem, LogicalFileSystem, resolve} from '../../../src/ngtsc/file_system'; +import {absoluteFromSourceFile, LogicalFileSystem, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, PrivateExportAliasingHost, Reexport, ReferenceEmitter} from '../../../src/ngtsc/imports'; +import {SemanticSymbol} from '../../../src/ngtsc/incremental/semantic_graph'; import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, ResourceRegistry} from '../../../src/ngtsc/metadata'; import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator'; -import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../../src/ngtsc/scope'; +import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver, TypeCheckScopeRegistry} from '../../../src/ngtsc/scope'; import {DecoratorHandler} from '../../../src/ngtsc/transform'; import {NgccReflectionHost} from '../host/ngcc_host'; import {Migration} from '../migrations/migration'; @@ -36,16 +38,20 @@ import {isWithinPackage, NOOP_DEPENDENCY_TRACKER} from './util'; * Simple class that resolves and loads files directly from the filesystem. */ class NgccResourceLoader implements ResourceLoader { - constructor(private fs: FileSystem) {} + constructor(private fs: ReadonlyFileSystem) {} canPreload = false; + canPreprocess = false; preload(): undefined|Promise { throw new Error('Not implemented.'); } + preprocessInline(): Promise { + throw new Error('Not implemented.'); + } load(url: string): string { - return this.fs.readFile(resolve(url)); + return this.fs.readFile(this.fs.resolve(url)); } resolve(url: string, containingFile: string): string { - return resolve(dirname(absoluteFrom(containingFile)), url); + return this.fs.resolve(this.fs.dirname(containingFile), url); } } @@ -88,45 +94,55 @@ export class DecorationAnalyzer { fullRegistry = new CompoundMetadataRegistry([this.metaRegistry, this.scopeRegistry]); evaluator = new PartialEvaluator(this.reflectionHost, this.typeChecker, /* dependencyTracker */ null); - importGraph = new ImportGraph(this.moduleResolver); + importGraph = new ImportGraph(this.typeChecker, NOOP_PERF_RECORDER); cycleAnalyzer = new CycleAnalyzer(this.importGraph); injectableRegistry = new InjectableClassRegistry(this.reflectionHost); - handlers: DecoratorHandler[] = [ + typeCheckScopeRegistry = new TypeCheckScopeRegistry(this.scopeRegistry, this.fullMetaReader); + handlers: DecoratorHandler[] = [ new ComponentDecoratorHandler( this.reflectionHost, this.evaluator, this.fullRegistry, this.fullMetaReader, - this.scopeRegistry, this.scopeRegistry, new ResourceRegistry(), this.isCore, - this.resourceManager, this.rootDirs, !!this.compilerOptions.preserveWhitespaces, + this.scopeRegistry, this.scopeRegistry, this.typeCheckScopeRegistry, new ResourceRegistry(), + this.isCore, this.resourceManager, this.rootDirs, + !!this.compilerOptions.preserveWhitespaces, /* i18nUseExternalIds */ true, this.bundle.enableI18nLegacyMessageIdFormat, + /* usePoisonedData */ false, /* i18nNormalizeLineEndingsInICUs */ false, this.moduleResolver, this.cycleAnalyzer, - this.refEmitter, NOOP_DEFAULT_IMPORT_RECORDER, NOOP_DEPENDENCY_TRACKER, - this.injectableRegistry, !!this.compilerOptions.annotateForClosureCompiler), + CycleHandlingStrategy.UseRemoteScoping, this.refEmitter, NOOP_DEFAULT_IMPORT_RECORDER, + NOOP_DEPENDENCY_TRACKER, this.injectableRegistry, + /* semanticDepGraphUpdater */ null, !!this.compilerOptions.annotateForClosureCompiler, + NOOP_PERF_RECORDER), + // See the note in ngtsc about why this cast is needed. // clang-format off new DirectiveDecoratorHandler( this.reflectionHost, this.evaluator, this.fullRegistry, this.scopeRegistry, this.fullMetaReader, NOOP_DEFAULT_IMPORT_RECORDER, this.injectableRegistry, this.isCore, + /* semanticDepGraphUpdater */ null, !!this.compilerOptions.annotateForClosureCompiler, // In ngcc we want to compile undecorated classes with Angular features. As of // version 10, undecorated classes that use Angular features are no longer handled // in ngtsc, but we want to ensure compatibility in ngcc for outdated libraries that // have not migrated to explicit decorators. See: https://hackmd.io/@alx/ryfYYuvzH. - /* compileUndecoratedClassesWithAngularFeatures */ true - ) as DecoratorHandler, + /* compileUndecoratedClassesWithAngularFeatures */ true, + NOOP_PERF_RECORDER + ) as DecoratorHandler, // clang-format on // Pipe handler must be before injectable handler in list so pipe factories are printed // before injectable factories (so injectable factories can delegate to them) new PipeDecoratorHandler( this.reflectionHost, this.evaluator, this.metaRegistry, this.scopeRegistry, - NOOP_DEFAULT_IMPORT_RECORDER, this.injectableRegistry, this.isCore), + NOOP_DEFAULT_IMPORT_RECORDER, this.injectableRegistry, this.isCore, NOOP_PERF_RECORDER), new InjectableDecoratorHandler( this.reflectionHost, NOOP_DEFAULT_IMPORT_RECORDER, this.isCore, - /* strictCtorDeps */ false, this.injectableRegistry, /* errorOnDuplicateProv */ false), + /* strictCtorDeps */ false, this.injectableRegistry, NOOP_PERF_RECORDER, + /* errorOnDuplicateProv */ false), new NgModuleDecoratorHandler( this.reflectionHost, this.evaluator, this.fullMetaReader, this.fullRegistry, this.scopeRegistry, this.referencesRegistry, this.isCore, /* routeAnalyzer */ null, this.refEmitter, /* factoryTracker */ null, NOOP_DEFAULT_IMPORT_RECORDER, - !!this.compilerOptions.annotateForClosureCompiler, this.injectableRegistry), + !!this.compilerOptions.annotateForClosureCompiler, this.injectableRegistry, + NOOP_PERF_RECORDER), ]; compiler = new NgccTraitCompiler(this.handlers, this.reflectionHost); migrations: Migration[] = [ @@ -136,7 +152,7 @@ export class DecorationAnalyzer { ]; constructor( - private fs: FileSystem, private bundle: EntryPointBundle, + private fs: ReadonlyFileSystem, private bundle: EntryPointBundle, private reflectionHost: NgccReflectionHost, private referencesRegistry: ReferencesRegistry, private diagnosticHandler: (error: ts.Diagnostic) => void = () => {}, private tsConfig: ParsedConfiguration|null = null) {} diff --git a/packages/compiler-cli/ngcc/src/analysis/migration_host.ts b/packages/compiler-cli/ngcc/src/analysis/migration_host.ts index e1e486e54a..60b6f89bd6 100644 --- a/packages/compiler-cli/ngcc/src/analysis/migration_host.ts +++ b/packages/compiler-cli/ngcc/src/analysis/migration_host.ts @@ -32,9 +32,14 @@ export class DefaultMigrationHost implements MigrationHost { const migratedTraits = this.compiler.injectSyntheticDecorator(clazz, decorator, flags); for (const trait of migratedTraits) { - if (trait.state === TraitState.ERRORED) { - trait.diagnostics = - trait.diagnostics.map(diag => createMigrationDiagnostic(diag, clazz, decorator)); + if ((trait.state === TraitState.Analyzed || trait.state === TraitState.Resolved) && + trait.analysisDiagnostics !== null) { + trait.analysisDiagnostics = trait.analysisDiagnostics.map( + diag => createMigrationDiagnostic(diag, clazz, decorator)); + } + if (trait.state === TraitState.Resolved && trait.resolveDiagnostics !== null) { + trait.resolveDiagnostics = + trait.resolveDiagnostics.map(diag => createMigrationDiagnostic(diag, clazz, decorator)); } } } 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 bfda1dbeff..e5dde6cbf5 100644 --- a/packages/compiler-cli/ngcc/src/analysis/ngcc_trait_compiler.ts +++ b/packages/compiler-cli/ngcc/src/analysis/ngcc_trait_compiler.ts @@ -8,6 +8,7 @@ import * as ts from 'typescript'; import {IncrementalBuild} from '../../../src/ngtsc/incremental/api'; +import {SemanticSymbol} from '../../../src/ngtsc/incremental/semantic_graph'; import {NOOP_PERF_RECORDER} from '../../../src/ngtsc/perf'; import {ClassDeclaration, Decorator} from '../../../src/ngtsc/reflection'; import {CompilationMode, DecoratorHandler, DtsTransformRegistry, HandlerFlags, Trait, TraitCompiler} from '../../../src/ngtsc/transform'; @@ -22,11 +23,12 @@ import {isDefined} from '../utils'; */ export class NgccTraitCompiler extends TraitCompiler { constructor( - handlers: DecoratorHandler[], + handlers: DecoratorHandler[], private ngccReflector: NgccReflectionHost) { super( handlers, ngccReflector, NOOP_PERF_RECORDER, new NoIncrementalBuild(), - /* compileNonExportedClasses */ true, CompilationMode.FULL, new DtsTransformRegistry()); + /* compileNonExportedClasses */ true, CompilationMode.FULL, new DtsTransformRegistry(), + /* semanticDepGraphUpdater */ null); } get analyzedFiles(): ts.SourceFile[] { @@ -54,7 +56,7 @@ export class NgccTraitCompiler extends TraitCompiler { * @param flags optional bitwise flag to influence the compilation of the decorator. */ injectSyntheticDecorator(clazz: ClassDeclaration, decorator: Decorator, flags?: HandlerFlags): - Trait[] { + Trait[] { const migratedTraits = this.detectTraits(clazz, [decorator]); if (migratedTraits === null) { return []; diff --git a/packages/compiler-cli/ngcc/src/analysis/util.ts b/packages/compiler-cli/ngcc/src/analysis/util.ts index dfe2b6188b..f5f5ee6e15 100644 --- a/packages/compiler-cli/ngcc/src/analysis/util.ts +++ b/packages/compiler-cli/ngcc/src/analysis/util.ts @@ -16,8 +16,7 @@ export function isWithinPackage(packagePath: AbsoluteFsPath, filePath: AbsoluteF class NoopDependencyTracker implements DependencyTracker { addDependency(): void {} addResourceDependency(): void {} - addTransitiveDependency(): void {} - addTransitiveResources(): void {} + recordDependencyAnalysisFailure(): void {} } export const NOOP_DEPENDENCY_TRACKER: DependencyTracker = new NoopDependencyTracker(); diff --git a/packages/compiler-cli/ngcc/src/command_line_options.ts b/packages/compiler-cli/ngcc/src/command_line_options.ts index 12f06df3b7..b423d127a8 100644 --- a/packages/compiler-cli/ngcc/src/command_line_options.ts +++ b/packages/compiler-cli/ngcc/src/command_line_options.ts @@ -8,7 +8,7 @@ */ import * as yargs from 'yargs'; -import {resolve, setFileSystem, NodeJSFileSystem} from '../../src/ngtsc/file_system'; +import {setFileSystem, NodeJSFileSystem} from '../../src/ngtsc/file_system'; import {ConsoleLogger, LogLevel} from '../../src/ngtsc/logging'; import {NgccOptions} from './ngcc_options'; @@ -49,7 +49,14 @@ export function parseCommandLineOptions(args: string[]): NgccOptions { }) .option('first-only', { describe: - 'If specified then only the first matching package.json property will be compiled.', + 'If specified then only the first matching package.json property will be compiled.\n' + + 'This option is overridden by `--typings-only`.', + type: 'boolean', + }) + .option('typings-only', { + describe: + 'If specified then only the typings files are processed, and no JS source files will be modified.\n' + + 'Setting this option will force `--first-only` to be set, since only one format is needed to process the typings', type: 'boolean', }) .option('create-ivy-entry-points', { @@ -115,12 +122,14 @@ export function parseCommandLineOptions(args: string[]): NgccOptions { process.exit(1); } - setFileSystem(new NodeJSFileSystem()); + const fs = new NodeJSFileSystem(); + setFileSystem(fs); - const baseSourcePath = resolve(options.s || './node_modules'); + const baseSourcePath = fs.resolve(options.s || './node_modules'); const propertiesToConsider = options.p; const targetEntryPointPath = options.t; const compileAllFormats = !options['first-only']; + const typingsOnly = options['typings-only']; const createNewEntryPointFormats = options['create-ivy-entry-points']; const logLevel = options.l as keyof typeof LogLevel | undefined; const enableI18nLegacyMessageIdFormat = options['legacy-message-ids']; @@ -138,6 +147,7 @@ export function parseCommandLineOptions(args: string[]): NgccOptions { basePath: baseSourcePath, propertiesToConsider, targetEntryPointPath, + typingsOnly, compileAllFormats, createNewEntryPointFormats, logger, diff --git a/packages/compiler-cli/ngcc/src/dependencies/dependency_host.ts b/packages/compiler-cli/ngcc/src/dependencies/dependency_host.ts index 1913bf871f..8fdbb64169 100644 --- a/packages/compiler-cli/ngcc/src/dependencies/dependency_host.ts +++ b/packages/compiler-cli/ngcc/src/dependencies/dependency_host.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} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, PathSegment, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {EntryPoint} from '../packages/entry_point'; import {resolveFileWithPostfixes} from '../utils'; @@ -32,7 +32,7 @@ export function createDependencyInfo(): DependencyInfo { } export abstract class DependencyHostBase implements DependencyHost { - constructor(protected fs: FileSystem, protected moduleResolver: ModuleResolver) {} + constructor(protected fs: ReadonlyFileSystem, protected moduleResolver: ModuleResolver) {} /** * Find all the dependencies for the entry-point at the given path. diff --git a/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts b/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts index 5833af645f..7d6d7f3219 100644 --- a/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts +++ b/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts @@ -8,7 +8,7 @@ import {DepGraph} from 'dependency-graph'; -import {AbsoluteFsPath, FileSystem, resolve} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; import {NgccConfiguration} from '../packages/configuration'; import {EntryPoint, EntryPointFormat, getEntryPointFormat, SUPPORTED_FORMAT_PROPERTIES} from '../packages/entry_point'; @@ -84,7 +84,7 @@ export interface SortedEntryPointsInfo extends DependencyDiagnostics { */ export class DependencyResolver { constructor( - private fs: FileSystem, private logger: Logger, private config: NgccConfiguration, + private fs: ReadonlyFileSystem, private logger: Logger, private config: NgccConfiguration, private hosts: Partial>, private typingsHost: DependencyHost) {} /** @@ -212,7 +212,7 @@ export class DependencyResolver { const format = getEntryPointFormat(this.fs, entryPoint, property); if (format === undefined) continue; - return {format, path: resolve(entryPoint.path, formatPath)}; + return {format, path: this.fs.resolve(entryPoint.path, formatPath)}; } throw new Error( 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 c651c562b3..9d66446ac6 100644 --- a/packages/compiler-cli/ngcc/src/dependencies/dts_dependency_host.ts +++ b/packages/compiler-cli/ngcc/src/dependencies/dts_dependency_host.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} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {PathMappings} from '../path_mappings'; import {EsmDependencyHost} from './esm_dependency_host'; import {ModuleResolver} from './module_resolver'; @@ -14,7 +14,7 @@ import {ModuleResolver} from './module_resolver'; * Helper functions for computing dependencies via typings files. */ export class DtsDependencyHost extends EsmDependencyHost { - constructor(fs: FileSystem, pathMappings?: PathMappings) { + constructor(fs: ReadonlyFileSystem, pathMappings?: PathMappings) { super( fs, new ModuleResolver(fs, pathMappings, ['', '.d.ts', '/index.d.ts', '.js', '/index.js']), false); diff --git a/packages/compiler-cli/ngcc/src/dependencies/esm_dependency_host.ts b/packages/compiler-cli/ngcc/src/dependencies/esm_dependency_host.ts index 7348463c14..77b8242733 100644 --- a/packages/compiler-cli/ngcc/src/dependencies/esm_dependency_host.ts +++ b/packages/compiler-cli/ngcc/src/dependencies/esm_dependency_host.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import * as ts from 'typescript'; -import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {DependencyHostBase} from './dependency_host'; import {ModuleResolver} from './module_resolver'; @@ -15,7 +15,8 @@ import {ModuleResolver} from './module_resolver'; */ export class EsmDependencyHost extends DependencyHostBase { constructor( - fs: FileSystem, moduleResolver: ModuleResolver, private scanImportExpressions = true) { + fs: ReadonlyFileSystem, moduleResolver: ModuleResolver, + private scanImportExpressions = true) { super(fs, moduleResolver); } // By skipping trivia here we don't have to account for it in the processing below diff --git a/packages/compiler-cli/ngcc/src/dependencies/module_resolver.ts b/packages/compiler-cli/ngcc/src/dependencies/module_resolver.ts index fafa3e95e2..35c854a982 100644 --- a/packages/compiler-cli/ngcc/src/dependencies/module_resolver.ts +++ b/packages/compiler-cli/ngcc/src/dependencies/module_resolver.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 {absoluteFrom, AbsoluteFsPath, dirname, FileSystem, isRoot, join, resolve} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {PathMappings} from '../path_mappings'; import {isRelativePath, resolveFileWithPostfixes} from '../utils'; @@ -25,9 +25,9 @@ import {isRelativePath, resolveFileWithPostfixes} from '../utils'; export class ModuleResolver { private pathMappings: ProcessedPathMapping[]; - constructor(private fs: FileSystem, pathMappings?: PathMappings, readonly relativeExtensions = [ - '', '.js', '/index.js' - ]) { + constructor( + private fs: ReadonlyFileSystem, pathMappings?: PathMappings, + readonly relativeExtensions = ['', '.js', '/index.js']) { this.pathMappings = pathMappings ? this.processPathMappings(pathMappings) : []; } @@ -54,7 +54,7 @@ export class ModuleResolver { * Convert the `pathMappings` into a collection of `PathMapper` functions. */ private processPathMappings(pathMappings: PathMappings): ProcessedPathMapping[] { - const baseUrl = absoluteFrom(pathMappings.baseUrl); + const baseUrl = this.fs.resolve(pathMappings.baseUrl); return Object.keys(pathMappings.paths).map(pathPattern => { const matcher = splitOnStar(pathPattern); const templates = pathMappings.paths[pathPattern].map(splitOnStar); @@ -71,7 +71,7 @@ export class ModuleResolver { */ private resolveAsRelativePath(moduleName: string, fromPath: AbsoluteFsPath): ResolvedModule|null { const resolvedPath = resolveFileWithPostfixes( - this.fs, resolve(dirname(fromPath), moduleName), this.relativeExtensions); + this.fs, this.fs.resolve(this.fs.dirname(fromPath), moduleName), this.relativeExtensions); return resolvedPath && new ResolvedRelativeModule(resolvedPath); } @@ -115,13 +115,13 @@ export class ModuleResolver { */ private resolveAsEntryPoint(moduleName: string, fromPath: AbsoluteFsPath): ResolvedModule|null { let folder = fromPath; - while (!isRoot(folder)) { - folder = dirname(folder); + while (!this.fs.isRoot(folder)) { + folder = this.fs.dirname(folder); if (folder.endsWith('node_modules')) { // Skip up if the folder already ends in node_modules - folder = dirname(folder); + folder = this.fs.dirname(folder); } - const modulePath = resolve(folder, 'node_modules', moduleName); + const modulePath = this.fs.resolve(folder, 'node_modules', moduleName); if (this.isEntryPoint(modulePath)) { return new ResolvedExternalModule(modulePath); } else if (this.resolveAsRelativePath(modulePath, fromPath)) { @@ -138,7 +138,7 @@ export class ModuleResolver { * This is achieved by checking for the existence of `${modulePath}/package.json`. */ private isEntryPoint(modulePath: AbsoluteFsPath): boolean { - return this.fs.exists(join(modulePath, 'package.json')); + return this.fs.exists(this.fs.join(modulePath, 'package.json')); } /** @@ -203,7 +203,7 @@ export class ModuleResolver { */ private computeMappedTemplates(mapping: ProcessedPathMapping, match: string) { return mapping.templates.map( - template => resolve(mapping.baseUrl, template.prefix + match + template.postfix)); + template => this.fs.resolve(mapping.baseUrl, template.prefix + match + template.postfix)); } /** @@ -212,9 +212,9 @@ export class ModuleResolver { */ private findPackagePath(path: AbsoluteFsPath): AbsoluteFsPath|null { let folder = path; - while (!isRoot(folder)) { - folder = dirname(folder); - if (this.fs.exists(join(folder, 'package.json'))) { + while (!this.fs.isRoot(folder)) { + folder = this.fs.dirname(folder); + if (this.fs.exists(this.fs.join(folder, 'package.json'))) { return folder; } } diff --git a/packages/compiler-cli/ngcc/src/entry_point_finder/entry_point_collector.ts b/packages/compiler-cli/ngcc/src/entry_point_finder/entry_point_collector.ts index 638c9f9249..1d29a95c22 100644 --- a/packages/compiler-cli/ngcc/src/entry_point_finder/entry_point_collector.ts +++ b/packages/compiler-cli/ngcc/src/entry_point_finder/entry_point_collector.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 */ -import {AbsoluteFsPath, FileSystem, PathSegment} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, PathSegment, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; - import {EntryPointWithDependencies} from '../dependencies/dependency_host'; import {DependencyResolver} from '../dependencies/dependency_resolver'; import {NgccConfiguration} from '../packages/configuration'; @@ -20,7 +19,7 @@ import {NGCC_DIRECTORY} from '../writing/new_entry_point_file_writer'; */ export class EntryPointCollector { constructor( - private fs: FileSystem, private config: NgccConfiguration, private logger: Logger, + private fs: ReadonlyFileSystem, private config: NgccConfiguration, private logger: Logger, private resolver: DependencyResolver) {} /** diff --git a/packages/compiler-cli/ngcc/src/entry_point_finder/program_based_entry_point_finder.ts b/packages/compiler-cli/ngcc/src/entry_point_finder/program_based_entry_point_finder.ts index 7c9f1b67c2..01f9330989 100644 --- a/packages/compiler-cli/ngcc/src/entry_point_finder/program_based_entry_point_finder.ts +++ b/packages/compiler-cli/ngcc/src/entry_point_finder/program_based_entry_point_finder.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} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; import {ParsedConfiguration} from '../../../src/perform_compile'; @@ -32,12 +32,13 @@ export class ProgramBasedEntryPointFinder extends TracingEntryPointFinder { private entryPointsWithDependencies: Map|null = null; constructor( - fs: FileSystem, config: NgccConfiguration, logger: Logger, resolver: DependencyResolver, - private entryPointCollector: EntryPointCollector, + fs: ReadonlyFileSystem, config: NgccConfiguration, logger: Logger, + resolver: DependencyResolver, private entryPointCollector: EntryPointCollector, private entryPointManifest: EntryPointManifest, basePath: AbsoluteFsPath, private tsConfig: ParsedConfiguration, projectPath: AbsoluteFsPath) { super( - fs, config, logger, resolver, basePath, getPathMappingsFromTsConfig(tsConfig, projectPath)); + fs, config, logger, resolver, basePath, + getPathMappingsFromTsConfig(fs, tsConfig, projectPath)); } /** 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 7b24af8835..9b729e2183 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,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, join, PathSegment, relative, relativeFrom} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, PathSegment, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; import {EntryPointWithDependencies} from '../dependencies/dependency_host'; import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver'; @@ -25,8 +25,8 @@ import {TracingEntryPointFinder} from './tracing_entry_point_finder'; */ export class TargetedEntryPointFinder extends TracingEntryPointFinder { constructor( - fs: FileSystem, config: NgccConfiguration, logger: Logger, resolver: DependencyResolver, - basePath: AbsoluteFsPath, pathMappings: PathMappings|undefined, + fs: ReadonlyFileSystem, config: NgccConfiguration, logger: Logger, + resolver: DependencyResolver, basePath: AbsoluteFsPath, pathMappings: PathMappings|undefined, private targetPath: AbsoluteFsPath) { super(fs, config, logger, resolver, basePath, pathMappings); } @@ -119,7 +119,7 @@ export class TargetedEntryPointFinder extends TracingEntryPointFinder { private computePackagePath(entryPointPath: AbsoluteFsPath): AbsoluteFsPath { // First try the main basePath, to avoid having to compute the other basePaths from the paths // mappings, which can be computationally intensive. - if (entryPointPath.startsWith(this.basePath)) { + if (this.isPathContainedBy(this.basePath, entryPointPath)) { const packagePath = this.computePackagePathFromContainingPath(entryPointPath, this.basePath); if (packagePath !== null) { return packagePath; @@ -129,7 +129,7 @@ export class TargetedEntryPointFinder extends TracingEntryPointFinder { // The main `basePath` didn't work out so now we try the `basePaths` computed from the paths // mappings in `tsconfig.json`. for (const basePath of this.getBasePaths()) { - if (entryPointPath.startsWith(basePath)) { + if (this.isPathContainedBy(basePath, entryPointPath)) { const packagePath = this.computePackagePathFromContainingPath(entryPointPath, basePath); if (packagePath !== null) { return packagePath; @@ -147,6 +147,18 @@ export class TargetedEntryPointFinder extends TracingEntryPointFinder { return this.computePackagePathFromNearestNodeModules(entryPointPath); } + /** + * Compute whether the `test` path is contained within the `base` path. + * + * Note that this doesn't use a simple `startsWith()` since that would result in a false positive + * for `test` paths such as `a/b/c-x` when the `base` path is `a/b/c`. + * + * Since `fs.relative()` can be quite expensive we check the fast possibilities first. + */ + private isPathContainedBy(base: AbsoluteFsPath, test: AbsoluteFsPath): boolean { + return test === base || + (test.startsWith(base) && !this.fs.relative(base, test).startsWith('..')); + } /** * Search down to the `entryPointPath` from the `containingPath` for the first `package.json` that @@ -162,14 +174,14 @@ export class TargetedEntryPointFinder extends TracingEntryPointFinder { private computePackagePathFromContainingPath( entryPointPath: AbsoluteFsPath, containingPath: AbsoluteFsPath): AbsoluteFsPath|null { let packagePath = containingPath; - const segments = this.splitPath(relative(containingPath, entryPointPath)); - let nodeModulesIndex = segments.lastIndexOf(relativeFrom('node_modules')); + const segments = this.splitPath(this.fs.relative(containingPath, entryPointPath)); + let nodeModulesIndex = segments.lastIndexOf('node_modules' as PathSegment); // If there are no `node_modules` in the relative path between the `basePath` and the // `entryPointPath` then just try the `basePath` as the `packagePath`. // (This can be the case with path-mapped entry-points.) if (nodeModulesIndex === -1) { - if (this.fs.exists(join(packagePath, 'package.json'))) { + if (this.fs.exists(this.fs.join(packagePath, 'package.json'))) { return packagePath; } } @@ -177,7 +189,7 @@ export class TargetedEntryPointFinder extends TracingEntryPointFinder { // 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 = this.fs.join(packagePath, segments.shift()!); nodeModulesIndex--; } @@ -185,8 +197,8 @@ export class TargetedEntryPointFinder extends TracingEntryPointFinder { // initial candidate `packagePath` is either a `node_modules` folder or the `basePath` with // no `package.json`. for (const segment of segments) { - packagePath = join(packagePath, segment); - if (this.fs.exists(join(packagePath, 'package.json'))) { + packagePath = this.fs.join(packagePath, segment); + if (this.fs.exists(this.fs.join(packagePath, 'package.json'))) { return packagePath; } } @@ -207,12 +219,12 @@ export class TargetedEntryPointFinder extends TracingEntryPointFinder { containerPath = this.fs.dirname(containerPath); } - if (this.fs.exists(join(packagePath, 'package.json'))) { + if (this.fs.exists(this.fs.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'))) { + this.fs.exists(this.fs.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; diff --git a/packages/compiler-cli/ngcc/src/entry_point_finder/tracing_entry_point_finder.ts b/packages/compiler-cli/ngcc/src/entry_point_finder/tracing_entry_point_finder.ts index 578b184247..a39d131638 100644 --- a/packages/compiler-cli/ngcc/src/entry_point_finder/tracing_entry_point_finder.ts +++ b/packages/compiler-cli/ngcc/src/entry_point_finder/tracing_entry_point_finder.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 */ -import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, PathManipulation, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; - import {EntryPointWithDependencies} from '../dependencies/dependency_host'; import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver'; import {NgccConfiguration} from '../packages/configuration'; @@ -36,9 +35,9 @@ export abstract class TracingEntryPointFinder implements EntryPointFinder { private basePaths: AbsoluteFsPath[]|null = null; constructor( - protected fs: FileSystem, protected config: NgccConfiguration, protected logger: Logger, - protected resolver: DependencyResolver, protected basePath: AbsoluteFsPath, - protected pathMappings: PathMappings|undefined) {} + protected fs: ReadonlyFileSystem, protected config: NgccConfiguration, + protected logger: Logger, protected resolver: DependencyResolver, + protected basePath: AbsoluteFsPath, protected pathMappings: PathMappings|undefined) {} /** * Search for Angular package entry-points. 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 37099f1e98..c74f5ce1de 100644 --- a/packages/compiler-cli/ngcc/src/entry_point_finder/utils.ts +++ b/packages/compiler-cli/ngcc/src/entry_point_finder/utils.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, getFileSystem, isRoot, resolve} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, getFileSystem, PathManipulation, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; import {PathMappings} from '../path_mappings'; @@ -34,31 +34,62 @@ export function getBasePaths( const fs = getFileSystem(); const basePaths = [sourceDirectory]; if (pathMappings) { - const baseUrl = resolve(pathMappings.baseUrl); + const baseUrl = fs.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 = fs.resolve(baseUrl, extractPathPrefix(path)); - if (fs.exists(basePath) && fs.stat(basePath).isFile()) { - basePath = fs.dirname(basePath); + for (const paths of Object.values(pathMappings.paths)) { + for (const path of paths) { + let foundMatch = false; + + // We only want base paths that exist and are not files + const {prefix, hasWildcard} = extractPathPrefix(path); + let basePath = fs.resolve(baseUrl, prefix); + if (fs.exists(basePath) && fs.stat(basePath).isFile()) { + basePath = fs.dirname(basePath); + } + + if (fs.exists(basePath)) { + // The `basePath` is itself a directory + basePaths.push(basePath); + foundMatch = true; + } + + if (hasWildcard) { + // The path contains a wildcard (`*`) so also try searching for directories that start + // with the wildcard prefix path segment. + const wildcardContainer = fs.dirname(basePath); + const wildcardPrefix = fs.basename(basePath); + if (isExistingDirectory(fs, wildcardContainer)) { + const candidates = fs.readdir(wildcardContainer); + for (const candidate of candidates) { + if (candidate.startsWith(wildcardPrefix)) { + const candidatePath = fs.resolve(wildcardContainer, candidate); + if (isExistingDirectory(fs, candidatePath)) { + foundMatch = true; + basePaths.push(candidatePath); + } + } + } + } + } + + if (!foundMatch) { + // We neither found a direct match (i.e. `basePath` is an existing directory) nor a + // directory that starts with a wildcard prefix. + 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.`); + } } - 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.`); - } - })); + } } - const dedupedBasePaths = dedupePaths(basePaths); + const dedupedBasePaths = dedupePaths(fs, basePaths); // 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. @@ -70,13 +101,18 @@ export function getBasePaths( return dedupedBasePaths; } +function isExistingDirectory(fs: ReadonlyFileSystem, path: AbsoluteFsPath): boolean { + return fs.exists(path) && fs.stat(path).isDirectory(); +} + /** * Extract everything in the `path` up to the first `*`. * @param path The path to parse. - * @returns The extracted prefix. + * @returns The extracted prefix and a flag to indicate whether there was a wildcard `*`. */ -function extractPathPrefix(path: string) { - return path.split('*', 1)[0]; +function extractPathPrefix(path: string): {prefix: string, hasWildcard: boolean} { + const [prefix, rest] = path.split('*', 2); + return {prefix, hasWildcard: rest !== undefined}; } /** @@ -103,10 +139,10 @@ export function trackDuration(task: () => T extends Promise? * (Note that we do not get `d` even though `d/e` and `d/f` share a base directory, since `d` is not * one of the base paths.) */ -export function dedupePaths(paths: AbsoluteFsPath[]): AbsoluteFsPath[] { +function dedupePaths(fs: PathManipulation, paths: AbsoluteFsPath[]): AbsoluteFsPath[] { const root: Node = {children: new Map()}; for (const path of paths) { - addPath(root, path); + addPath(fs, root, path); } return flattenTree(root); } @@ -114,9 +150,9 @@ export function dedupePaths(paths: AbsoluteFsPath[]): AbsoluteFsPath[] { /** * Add a path (defined by the `segments`) to the current `node` in the tree. */ -function addPath(root: Node, path: AbsoluteFsPath): void { +function addPath(fs: PathManipulation, root: Node, path: AbsoluteFsPath): void { let node = root; - if (!isRoot(path)) { + if (!fs.isRoot(path)) { const segments = path.split('/'); for (let index = 0; index < segments.length; index++) { if (isLeaf(node)) { diff --git a/packages/compiler-cli/ngcc/src/execution/analyze_entry_points.ts b/packages/compiler-cli/ngcc/src/execution/analyze_entry_points.ts index d12c0cbe57..1a876eb53d 100644 --- a/packages/compiler-cli/ngcc/src/execution/analyze_entry_points.ts +++ b/packages/compiler-cli/ngcc/src/execution/analyze_entry_points.ts @@ -19,15 +19,16 @@ import {EntryPoint, EntryPointJsonProperty, EntryPointPackageJson, SUPPORTED_FOR import {cleanOutdatedPackages} from '../writing/cleaning/package_cleaner'; import {AnalyzeEntryPointsFn} from './api'; -import {PartiallyOrderedTasks, TaskQueue} from './tasks/api'; +import {DtsProcessing, 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 { + supportedPropertiesToConsider: EntryPointJsonProperty[], typingsOnly: boolean, + compileAllFormats: boolean, propertiesToConsider: string[], + inParallel: boolean): AnalyzeEntryPointsFn { return () => { logger.debug('Analyzing entry-points...'); const startTime = Date.now(); @@ -48,10 +49,8 @@ export function getAnalyzeEntryPointsFn( for (const entryPoint of entryPoints) { const packageJson = entryPoint.packageJson; - const hasProcessedTypings = hasBeenProcessed(packageJson, 'typings'); - const {propertiesToProcess, equivalentPropertiesMap} = - getPropertiesToProcess(packageJson, supportedPropertiesToConsider, compileAllFormats); - let processDts = !hasProcessedTypings; + const {propertiesToProcess, equivalentPropertiesMap} = getPropertiesToProcess( + packageJson, supportedPropertiesToConsider, compileAllFormats, typingsOnly); if (propertiesToProcess.length === 0) { // This entry-point is unprocessable (i.e. there is no format property that is of interest @@ -62,6 +61,16 @@ export function getAnalyzeEntryPointsFn( continue; } + const hasProcessedTypings = hasBeenProcessed(packageJson, 'typings'); + if (hasProcessedTypings && typingsOnly) { + // Typings for this entry-point have already been processed and we're in typings-only mode, + // so no task has to be created for this entry-point. + logger.debug(`Skipping ${entryPoint.name} : typings have already been processed.`); + continue; + } + let processDts = hasProcessedTypings ? DtsProcessing.No : + typingsOnly ? DtsProcessing.Only : DtsProcessing.Yes; + for (const formatProperty of propertiesToProcess) { if (hasBeenProcessed(entryPoint.packageJson, formatProperty)) { // The format-path which the property maps to is already processed - nothing to do. @@ -73,7 +82,7 @@ export function getAnalyzeEntryPointsFn( tasks.push({entryPoint, formatProperty, formatPropertiesToMarkAsProcessed, processDts}); // Only process typings for the first property (if not already processed). - processDts = false; + processDts = DtsProcessing.No; } } @@ -114,7 +123,7 @@ function logInvalidEntryPoints(logger: Logger, invalidEntryPoints: InvalidEntryP */ function getPropertiesToProcess( packageJson: EntryPointPackageJson, propertiesToConsider: EntryPointJsonProperty[], - compileAllFormats: boolean): { + compileAllFormats: boolean, typingsOnly: boolean): { propertiesToProcess: EntryPointJsonProperty[]; equivalentPropertiesMap: Map; } { @@ -156,7 +165,8 @@ function getPropertiesToProcess( const equivalentPropertiesMap = new Map(); for (const prop of propertiesToConsider) { const formatPath = packageJson[prop]!; - const equivalentProperties = formatPathToProperties[formatPath]; + // If we are only processing typings then there should be no format properties to mark + const equivalentProperties = typingsOnly ? [] : formatPathToProperties[formatPath]; equivalentPropertiesMap.set(prop, equivalentProperties); } diff --git a/packages/compiler-cli/ngcc/src/execution/cluster/executor.ts b/packages/compiler-cli/ngcc/src/execution/cluster/executor.ts index 23155c3ad3..82c8bbcb2d 100644 --- a/packages/compiler-cli/ngcc/src/execution/cluster/executor.ts +++ b/packages/compiler-cli/ngcc/src/execution/cluster/executor.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 {FileSystem} from '../../../../src/ngtsc/file_system'; +import {PathManipulation} from '../../../../src/ngtsc/file_system'; import {Logger} from '../../../../src/ngtsc/logging'; import {AsyncLocker} from '../../locking/async_locker'; import {FileWriter} from '../../writing/file_writer'; @@ -21,7 +21,7 @@ import {ClusterMaster} from './master'; */ export class ClusterExecutor implements Executor { constructor( - private workerCount: number, private fileSystem: FileSystem, private logger: Logger, + private workerCount: number, private fileSystem: PathManipulation, private logger: Logger, private fileWriter: FileWriter, private pkgJsonUpdater: PackageJsonUpdater, private lockFile: AsyncLocker, private createTaskCompletedCallback: CreateTaskCompletedCallback) {} diff --git a/packages/compiler-cli/ngcc/src/execution/cluster/master.ts b/packages/compiler-cli/ngcc/src/execution/cluster/master.ts index f3020a7910..9024a96ff7 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 {AbsoluteFsPath, FileSystem} from '../../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, PathManipulation} from '../../../../src/ngtsc/file_system'; import {Logger} from '../../../../src/ngtsc/logging'; import {FileWriter} from '../../writing/file_writer'; import {PackageJsonUpdater} from '../../writing/package_json_updater'; @@ -35,7 +35,7 @@ export class ClusterMaster { private remainingRespawnAttempts = 3; constructor( - private maxWorkerCount: number, private fileSystem: FileSystem, private logger: Logger, + private maxWorkerCount: number, private fileSystem: PathManipulation, private logger: Logger, private fileWriter: FileWriter, private pkgJsonUpdater: PackageJsonUpdater, analyzeEntryPoints: AnalyzeEntryPointsFn, createTaskCompletedCallback: CreateTaskCompletedCallback) { diff --git a/packages/compiler-cli/ngcc/src/execution/create_compile_function.ts b/packages/compiler-cli/ngcc/src/execution/create_compile_function.ts index 6f8d754ed2..3b2005d60c 100644 --- a/packages/compiler-cli/ngcc/src/execution/create_compile_function.ts +++ b/packages/compiler-cli/ngcc/src/execution/create_compile_function.ts @@ -46,12 +46,13 @@ export function getCreateCompileFn( // (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`). + // should always return a format here (and not `undefined`) unless `formatPath` points to a + // missing or empty file. 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})`); + onTaskCompleted( + task, TaskProcessingOutcome.Failed, + `property \`${formatProperty}\` pointing to a missing or empty file: ${formatPath}`); + return; } logger.info(`Compiling ${entryPoint.name} : ${formatProperty} as ${format}`); diff --git a/packages/compiler-cli/ngcc/src/execution/tasks/api.ts b/packages/compiler-cli/ngcc/src/execution/tasks/api.ts index 864f365099..3185a6ff85 100644 --- a/packages/compiler-cli/ngcc/src/execution/tasks/api.ts +++ b/packages/compiler-cli/ngcc/src/execution/tasks/api.ts @@ -32,8 +32,30 @@ export interface Task extends JsonObject { */ formatPropertiesToMarkAsProcessed: EntryPointJsonProperty[]; - /** Whether to also process typings for this entry-point as part of the task. */ - processDts: boolean; + /** + * Whether to process typings for this entry-point as part of the task. + */ + processDts: DtsProcessing; +} + +/** + * The options for processing Typescript typings (.d.ts) files. + */ +export enum DtsProcessing { + /** + * Yes, process the typings for this entry point as part of the task. + */ + Yes, + /** + * No, do not process the typings as part of this task - they must have already been processed by + * another task or previous ngcc process. + */ + No, + /** + * Only process the typings for this entry-point; do not render any JavaScript files for the + * `formatProperty` of this task. + */ + Only, } /** diff --git a/packages/compiler-cli/ngcc/src/execution/tasks/completion.ts b/packages/compiler-cli/ngcc/src/execution/tasks/completion.ts index ea4b13d4d8..0fa0081084 100644 --- a/packages/compiler-cli/ngcc/src/execution/tasks/completion.ts +++ b/packages/compiler-cli/ngcc/src/execution/tasks/completion.ts @@ -5,13 +5,13 @@ * 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, resolve} from '../../../../src/ngtsc/file_system'; +import {PathManipulation, ReadonlyFileSystem} from '../../../../src/ngtsc/file_system'; import {Logger} from '../../../../src/ngtsc/logging'; import {markAsProcessed} from '../../packages/build_marker'; import {getEntryPointFormat, PackageJsonFormatProperties} from '../../packages/entry_point'; import {PackageJsonUpdater} from '../../writing/package_json_updater'; -import {Task, TaskCompletedCallback, TaskProcessingOutcome, TaskQueue} from './api'; +import {DtsProcessing, Task, TaskCompletedCallback, TaskProcessingOutcome, TaskQueue} from './api'; /** * A function that can handle a specific outcome of a task completion. @@ -46,14 +46,14 @@ export function composeTaskCompletedCallbacks( * * @param pkgJsonUpdater The service used to update the package.json */ -export function createMarkAsProcessedHandler(pkgJsonUpdater: PackageJsonUpdater): - TaskCompletedHandler { +export function createMarkAsProcessedHandler( + fs: PathManipulation, pkgJsonUpdater: PackageJsonUpdater): TaskCompletedHandler { return (task: Task): void => { const {entryPoint, formatPropertiesToMarkAsProcessed, processDts} = task; - const packageJsonPath = resolve(entryPoint.path, 'package.json'); + const packageJsonPath = fs.resolve(entryPoint.path, 'package.json'); const propsToMarkAsProcessed: PackageJsonFormatProperties[] = [...formatPropertiesToMarkAsProcessed]; - if (processDts) { + if (processDts !== DtsProcessing.No) { propsToMarkAsProcessed.push('typings'); } markAsProcessed( @@ -64,13 +64,9 @@ export function createMarkAsProcessedHandler(pkgJsonUpdater: PackageJsonUpdater) /** * Create a handler that will throw an error. */ -export function createThrowErrorHandler(fs: FileSystem): TaskCompletedHandler { +export function createThrowErrorHandler(fs: ReadonlyFileSystem): TaskCompletedHandler { 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})` + - (message !== null ? ` due to ${message}` : '')); + throw new Error(createErrorMessage(fs, task, message)); }; } @@ -78,13 +74,17 @@ export function createThrowErrorHandler(fs: FileSystem): TaskCompletedHandler { * Create a handler that logs an error and marks the task as failed. */ export function createLogErrorHandler( - logger: Logger, fs: FileSystem, taskQueue: TaskQueue): TaskCompletedHandler { + logger: Logger, fs: ReadonlyFileSystem, taskQueue: TaskQueue): TaskCompletedHandler { 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})` + - (message !== null ? ` due to ${message}` : '')); + logger.error(createErrorMessage(fs, task, message)); }; } + +function createErrorMessage(fs: ReadonlyFileSystem, task: Task, message: string|null): string { + const jsFormat = `\`${task.formatProperty}\` as ${ + getEntryPointFormat(fs, task.entryPoint, task.formatProperty) ?? 'unknown format'}`; + const format = task.typingsOnly ? `typings only using ${jsFormat}` : jsFormat; + message = message !== null ? ` due to ${message}` : ''; + return `Failed to compile entry-point ${task.entryPoint.name} (${format})` + message; +} diff --git a/packages/compiler-cli/ngcc/src/execution/tasks/utils.ts b/packages/compiler-cli/ngcc/src/execution/tasks/utils.ts index b95886c56c..3ea7a8a7ea 100644 --- a/packages/compiler-cli/ngcc/src/execution/tasks/utils.ts +++ b/packages/compiler-cli/ngcc/src/execution/tasks/utils.ts @@ -7,11 +7,12 @@ */ import {DepGraph} from 'dependency-graph'; import {EntryPoint} from '../../packages/entry_point'; -import {PartiallyOrderedTasks, Task, TaskDependencies} from './api'; +import {DtsProcessing, 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: ${DtsProcessing[task.processDts]}}`; /** * Compute a mapping of tasks to the tasks that are dependent on them (if any). @@ -55,7 +56,7 @@ export function computeTaskDependencies( } } - if (task.processDts) { + if (task.processDts !== DtsProcessing.No) { // SANITY CHECK: // There should only be one task per entry-point that generates typings (and thus can be a // dependency of other tasks), so the following should theoretically never happen, but check diff --git a/packages/compiler-cli/ngcc/src/host/esm5_host.ts b/packages/compiler-cli/ngcc/src/host/esm5_host.ts index 5af9c9f123..9cd7c758a8 100644 --- a/packages/compiler-cli/ngcc/src/host/esm5_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm5_host.ts @@ -382,6 +382,12 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { * return _super.apply(this, tslib.__spread(arguments)) || this; * ``` * + * or, since TypeScript 4.2 it would be + * + * ``` + * return _super.apply(this, tslib.__spreadArray([], tslib.__read(arguments))) || this; + * ``` + * * Such constructs can be still considered as synthetic delegate constructors as they are * the product of a common TypeScript to ES5 synthetic constructor, just being downleveled * to ES5 using `tsc`. See: https://github.com/angular/angular/issues/38453. @@ -413,7 +419,10 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { * ``` * var _this = _super.apply(this, tslib.__spread(arguments)) || this; * ``` - * + * or using the syntax emitted since TypeScript 4.2: + * ``` + * return _super.apply(this, tslib.__spreadArray([], tslib.__read(arguments))) || this; + * ``` * * @param statement a statement that may be a synthesized super call * @returns true if the statement looks like a synthesized super call @@ -447,6 +456,10 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { * ``` * return _super.apply(this, tslib.__spread(arguments)) || this; * ``` + * or using the syntax emitted since TypeScript 4.2: + * ``` + * return _super.apply(this, tslib.__spreadArray([], tslib.__read(arguments))) || this; + * ``` * * @param statement a statement that may be a synthesized super call * @returns true if the statement looks like a synthesized super call @@ -473,6 +486,10 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { * ``` * _super.apply(this, tslib.__spread(arguments)) || this; * ``` + * or using the syntax emitted since TypeScript 4.2: + * ``` + * return _super.apply(this, tslib.__spreadArray([], tslib.__read(arguments))) || this; + * ``` * * @param expression an expression that may represent a default super call * @returns true if the expression corresponds with the above form @@ -500,7 +517,8 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { * This structure is generated by TypeScript when transforming ES2015 to ES5, see * https://github.com/Microsoft/TypeScript/blob/v3.2.2/src/compiler/transformers/es2015.ts#L1148-L1163 * - * Additionally, we also handle cases where `arguments` are wrapped by a TypeScript spread helper. + * Additionally, we also handle cases where `arguments` are wrapped by a TypeScript spread + * helper. * This can happen if ES2015 class output contain auto-generated constructors due to class * members. The ES2015 output will be using `super(...arguments)` to delegate to the superclass, * but once downleveled to ES5, the spread operator will be persisted through a TypeScript spread @@ -510,6 +528,12 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { * _super.apply(this, __spread(arguments)) || this; * ``` * + * or, since TypeScript 4.2 it would be + * + * ``` + * _super.apply(this, tslib.__spreadArray([], tslib.__read(arguments))) || this; + * ``` + * * More details can be found in: https://github.com/angular/angular/issues/38453. * * @param expression an expression that may represent a default super call @@ -538,32 +562,79 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { // The other scenario we intend to detect: The `arguments` variable might be wrapped with the // TypeScript spread helper (either through tslib or inlined). This can happen if an explicit // delegate constructor uses `super(...arguments)` in ES2015 and is downleveled to ES5 using - // `--downlevelIteration`. The output in such cases would not directly pass the function - // `arguments` to the `super` call, but wrap it in a TS spread helper. The output would match - // the following pattern: `super.apply(this, tslib.__spread(arguments))`. We check for such - // constructs below, but perform the detection of the call expression definition as last as - // that is the most expensive operation here. - if (!ts.isCallExpression(argumentsExpr) || argumentsExpr.arguments.length !== 1 || - !isArgumentsIdentifier(argumentsExpr.arguments[0])) { + // `--downlevelIteration`. + return this.isSpreadArgumentsExpression(argumentsExpr); + } + + /** + * Determines if the provided expression is one of the following call expressions: + * + * 1. `__spread(arguments)` + * 2. `__spreadArray([], __read(arguments))` + * + * The tslib helpers may have been emitted inline as in the above example, or they may be read + * from a namespace import. + */ + private isSpreadArgumentsExpression(expression: ts.Expression): boolean { + const call = this.extractKnownHelperCall(expression); + if (call === null) { return false; } - const argumentsCallExpr = argumentsExpr.expression; - let argumentsCallDeclaration: Declaration|null = null; + if (call.helper === KnownDeclaration.TsHelperSpread) { + // `__spread(arguments)` + return call.args.length === 1 && isArgumentsIdentifier(call.args[0]); + } else if (call.helper === KnownDeclaration.TsHelperSpreadArray) { + // `__spreadArray([], __read(arguments))` + if (call.args.length !== 2) { + return false; + } - // The `__spread` helper could be globally available, or accessed through a namespaced - // import. Hence we support a property access here as long as it resolves to the actual - // known TypeScript spread helper. - if (ts.isIdentifier(argumentsCallExpr)) { - argumentsCallDeclaration = this.getDeclarationOfIdentifier(argumentsCallExpr); - } else if ( - ts.isPropertyAccessExpression(argumentsCallExpr) && - ts.isIdentifier(argumentsCallExpr.name)) { - argumentsCallDeclaration = this.getDeclarationOfIdentifier(argumentsCallExpr.name); + const firstArg = call.args[0]; + if (!ts.isArrayLiteralExpression(firstArg) || firstArg.elements.length !== 0) { + return false; + } + + const secondArg = this.extractKnownHelperCall(call.args[1]); + if (secondArg === null || secondArg.helper !== KnownDeclaration.TsHelperRead) { + return false; + } + + return secondArg.args.length === 1 && isArgumentsIdentifier(secondArg.args[0]); + } else { + return false; + } + } + + /** + * Inspects the provided expression and determines if it corresponds with a known helper function + * as receiver expression. + */ + private extractKnownHelperCall(expression: ts.Expression): + {helper: KnownDeclaration, args: ts.NodeArray}|null { + if (!ts.isCallExpression(expression)) { + return null; } - return argumentsCallDeclaration !== null && - argumentsCallDeclaration.known === KnownDeclaration.TsHelperSpread; + const receiverExpr = expression.expression; + + // The helper could be globally available, or accessed through a namespaced import. Hence we + // support a property access here as long as it resolves to the actual known TypeScript helper. + let receiver: Declaration|null = null; + if (ts.isIdentifier(receiverExpr)) { + receiver = this.getDeclarationOfIdentifier(receiverExpr); + } else if (ts.isPropertyAccessExpression(receiverExpr) && ts.isIdentifier(receiverExpr.name)) { + receiver = this.getDeclarationOfIdentifier(receiverExpr.name); + } + + if (receiver === null || receiver.known === null) { + return null; + } + + return { + helper: receiver.known, + args: expression.arguments, + }; } } diff --git a/packages/compiler-cli/ngcc/src/host/umd_host.ts b/packages/compiler-cli/ngcc/src/host/umd_host.ts index fa8080d1e4..1467c2141e 100644 --- a/packages/compiler-cli/ngcc/src/host/umd_host.ts +++ b/packages/compiler-cli/ngcc/src/host/umd_host.ts @@ -46,7 +46,7 @@ export class UmdReflectionHost extends Esm5ReflectionHost { } getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null { - // First we try one of the the following: + // First we try one of the following: // 1. The `exports` identifier - referring to the current file/module. // 2. An identifier (e.g. `foo`) that refers to an imported UMD module. // 3. A UMD style export identifier (e.g. the `foo` of `exports.foo`). diff --git a/packages/compiler-cli/ngcc/src/locking/lock_file.ts b/packages/compiler-cli/ngcc/src/locking/lock_file.ts index dedd579449..08f141c0ee 100644 --- a/packages/compiler-cli/ngcc/src/locking/lock_file.ts +++ b/packages/compiler-cli/ngcc/src/locking/lock_file.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 {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, PathManipulation} from '../../../src/ngtsc/file_system'; -export function getLockFilePath(fs: FileSystem) { +export function getLockFilePath(fs: PathManipulation) { return fs.resolve(require.resolve('@angular/compiler-cli/ngcc'), '../__ngcc_lock_file__'); } diff --git a/packages/compiler-cli/ngcc/src/locking/sync_locker.ts b/packages/compiler-cli/ngcc/src/locking/sync_locker.ts index 58f0762416..54cca4d4a5 100644 --- a/packages/compiler-cli/ngcc/src/locking/sync_locker.ts +++ b/packages/compiler-cli/ngcc/src/locking/sync_locker.ts @@ -54,8 +54,7 @@ export class SyncLocker { const pid = this.lockFile.read(); throw new Error( `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 running multiple builds in parallel then you might try pre-processing your node_modules via the command line ngcc tool before starting the builds.\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/main.ts b/packages/compiler-cli/ngcc/src/main.ts index b6d74e19df..99f155ee92 100644 --- a/packages/compiler-cli/ngcc/src/main.ts +++ b/packages/compiler-cli/ngcc/src/main.ts @@ -8,7 +8,7 @@ /// -import {AbsoluteFsPath, FileSystem, resolve} from '../../src/ngtsc/file_system'; +import {AbsoluteFsPath, FileSystem, ReadonlyFileSystem} from '../../src/ngtsc/file_system'; import {Logger} from '../../src/ngtsc/logging'; import {ParsedConfiguration} from '../../src/perform_compile'; @@ -56,6 +56,7 @@ export function mainNgcc(options: AsyncNgccOptions|SyncNgccOptions): void|Promis basePath, targetEntryPointPath, propertiesToConsider, + typingsOnly, compileAllFormats, logger, pathMappings, @@ -78,8 +79,9 @@ export function mainNgcc(options: AsyncNgccOptions|SyncNgccOptions): void|Promis // Bail out early if the work is already done. const supportedPropertiesToConsider = ensureSupportedProperties(propertiesToConsider); - const absoluteTargetEntryPointPath = - targetEntryPointPath !== undefined ? resolve(basePath, targetEntryPointPath) : null; + const absoluteTargetEntryPointPath = targetEntryPointPath !== undefined ? + fileSystem.resolve(basePath, targetEntryPointPath) : + null; const finder = getEntryPointFinder( fileSystem, logger, dependencyResolver, config, entryPointManifest, absBasePath, absoluteTargetEntryPointPath, pathMappings, @@ -95,7 +97,7 @@ export function mainNgcc(options: AsyncNgccOptions|SyncNgccOptions): void|Promis const inParallel = workerCount > 1; const analyzeEntryPoints = getAnalyzeEntryPointsFn( - logger, finder, fileSystem, supportedPropertiesToConsider, compileAllFormats, + logger, finder, fileSystem, supportedPropertiesToConsider, typingsOnly, compileAllFormats, propertiesToConsider, inParallel); // Create an updater that will actually write to disk. @@ -140,9 +142,10 @@ function ensureSupportedProperties(properties: string[]): EntryPointJsonProperty function getCreateTaskCompletedCallback( pkgJsonUpdater: PackageJsonUpdater, errorOnFailedEntryPoint: boolean, logger: Logger, - fileSystem: FileSystem): CreateTaskCompletedCallback { + fileSystem: ReadonlyFileSystem): CreateTaskCompletedCallback { return taskQueue => composeTaskCompletedCallbacks({ - [TaskProcessingOutcome.Processed]: createMarkAsProcessedHandler(pkgJsonUpdater), + [TaskProcessingOutcome.Processed]: + createMarkAsProcessedHandler(fileSystem, pkgJsonUpdater), [TaskProcessingOutcome.Failed]: errorOnFailedEntryPoint ? createThrowErrorHandler(fileSystem) : createLogErrorHandler(logger, fileSystem, taskQueue), @@ -175,7 +178,7 @@ function getExecutor( } function getDependencyResolver( - fileSystem: FileSystem, logger: Logger, config: NgccConfiguration, + fileSystem: ReadonlyFileSystem, logger: Logger, config: NgccConfiguration, pathMappings: PathMappings|undefined): DependencyResolver { const moduleResolver = new ModuleResolver(fileSystem, pathMappings); const esmDependencyHost = new EsmDependencyHost(fileSystem, moduleResolver); @@ -193,7 +196,7 @@ function getDependencyResolver( } function getEntryPointFinder( - fs: FileSystem, logger: Logger, resolver: DependencyResolver, config: NgccConfiguration, + fs: ReadonlyFileSystem, logger: Logger, resolver: DependencyResolver, config: NgccConfiguration, entryPointManifest: EntryPointManifest, basePath: AbsoluteFsPath, absoluteTargetEntryPointPath: AbsoluteFsPath|null, pathMappings: PathMappings|undefined, tsConfig: ParsedConfiguration|null, projectPath: AbsoluteFsPath): EntryPointFinder { diff --git a/packages/compiler-cli/ngcc/src/ngcc_options.ts b/packages/compiler-cli/ngcc/src/ngcc_options.ts index 52fb3ab4d8..7acc637360 100644 --- a/packages/compiler-cli/ngcc/src/ngcc_options.ts +++ b/packages/compiler-cli/ngcc/src/ngcc_options.ts @@ -7,7 +7,7 @@ */ import * as os from 'os'; -import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem} from '../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, PathManipulation} from '../../src/ngtsc/file_system'; import {ConsoleLogger, Logger, LogLevel} from '../../src/ngtsc/logging'; import {ParsedConfiguration, readConfiguration} from '../../src/perform_compile'; @@ -42,9 +42,21 @@ export interface SyncNgccOptions { */ propertiesToConsider?: string[]; + /** + * Whether to only process the typings files for this entry-point. + * + * This is useful when running ngcc only to provide typings files to downstream tooling such as + * the Angular Language Service or ng-packagr. Defaults to `false`. + * + * If this is set to `true` then `compileAllFormats` is forced to `false`. + */ + typingsOnly?: boolean; + /** * Whether to process all formats specified by (`propertiesToConsider`) or to stop processing - * this entry-point at the first matching format. Defaults to `true`. + * this entry-point at the first matching format. + * + * Defaults to `true`, but is forced to `false` if `typingsOnly` is `true`. */ compileAllFormats?: boolean; @@ -172,10 +184,11 @@ export function getSharedSetup(options: NgccOptions): SharedSetup&RequiredNgccOp basePath, targetEntryPointPath, propertiesToConsider = SUPPORTED_FORMAT_PROPERTIES, + typingsOnly = false, compileAllFormats = true, createNewEntryPointFormats = false, logger = new ConsoleLogger(LogLevel.info), - pathMappings = getPathMappingsFromTsConfig(tsConfig, projectPath), + pathMappings = getPathMappingsFromTsConfig(fileSystem, tsConfig, projectPath), async = false, errorOnFailedEntryPoint = false, enableI18nLegacyMessageIdFormat = true, @@ -188,12 +201,19 @@ export function getSharedSetup(options: NgccOptions): SharedSetup&RequiredNgccOp errorOnFailedEntryPoint = true; } + if (typingsOnly) { + // If we only want to process the typings then we do not want to waste time trying to process + // multiple JS formats. + compileAllFormats = false; + } + checkForSolutionStyleTsConfig(fileSystem, logger, projectPath, options.tsConfigPath, tsConfig); return { basePath, targetEntryPointPath, propertiesToConsider, + typingsOnly, compileAllFormats, createNewEntryPointFormats, logger, @@ -239,7 +259,7 @@ export function clearTsConfigCache() { } function checkForSolutionStyleTsConfig( - fileSystem: FileSystem, logger: Logger, projectPath: AbsoluteFsPath, + fileSystem: PathManipulation, logger: Logger, projectPath: AbsoluteFsPath, tsConfigPath: string|null|undefined, tsConfig: ParsedConfiguration|null): void { if (tsConfigPath !== null && !tsConfigPath && tsConfig !== null && tsConfig.rootNames.length === 0 && tsConfig.projectReferences !== undefined && diff --git a/packages/compiler-cli/ngcc/src/packages/bundle_program.ts b/packages/compiler-cli/ngcc/src/packages/bundle_program.ts index 04ad6faaa9..acf3b42ee1 100644 --- a/packages/compiler-cli/ngcc/src/packages/bundle_program.ts +++ b/packages/compiler-cli/ngcc/src/packages/bundle_program.ts @@ -7,7 +7,7 @@ */ import * as ts from 'typescript'; -import {AbsoluteFsPath, dirname, FileSystem, resolve} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {patchTsGetExpandoInitializer, restoreGetExpandoInitializer} from './patch_ts_expando_initializer'; @@ -34,10 +34,10 @@ export interface BundleProgram { * Create a bundle program. */ export function makeBundleProgram( - fs: FileSystem, isCore: boolean, pkg: AbsoluteFsPath, path: AbsoluteFsPath, r3FileName: string, - options: ts.CompilerOptions, host: ts.CompilerHost, + fs: ReadonlyFileSystem, isCore: boolean, pkg: AbsoluteFsPath, path: AbsoluteFsPath, + r3FileName: string, options: ts.CompilerOptions, host: ts.CompilerHost, additionalFiles: AbsoluteFsPath[] = []): BundleProgram { - const r3SymbolsPath = isCore ? findR3SymbolsPath(fs, dirname(path), r3FileName) : null; + const r3SymbolsPath = isCore ? findR3SymbolsPath(fs, fs.dirname(path), r3FileName) : null; let rootPaths = r3SymbolsPath ? [path, r3SymbolsPath, ...additionalFiles] : [path, ...additionalFiles]; @@ -58,8 +58,8 @@ export function makeBundleProgram( * Search the given directory hierarchy to find the path to the `r3_symbols` file. */ export function findR3SymbolsPath( - fs: FileSystem, directory: AbsoluteFsPath, filename: string): AbsoluteFsPath|null { - const r3SymbolsFilePath = resolve(directory, filename); + fs: ReadonlyFileSystem, directory: AbsoluteFsPath, filename: string): AbsoluteFsPath|null { + const r3SymbolsFilePath = fs.resolve(directory, filename); if (fs.exists(r3SymbolsFilePath)) { return r3SymbolsFilePath; } @@ -72,12 +72,12 @@ export function findR3SymbolsPath( .filter(p => p !== 'node_modules') // Only interested in directories (and only those that are not symlinks) .filter(p => { - const stat = fs.lstat(resolve(directory, p)); + const stat = fs.lstat(fs.resolve(directory, p)); return stat.isDirectory() && !stat.isSymbolicLink(); }); for (const subDirectory of subDirectories) { - const r3SymbolsFilePath = findR3SymbolsPath(fs, resolve(directory, subDirectory), filename); + const r3SymbolsFilePath = findR3SymbolsPath(fs, fs.resolve(directory, subDirectory), filename); if (r3SymbolsFilePath) { return r3SymbolsFilePath; } diff --git a/packages/compiler-cli/ngcc/src/packages/configuration.ts b/packages/compiler-cli/ngcc/src/packages/configuration.ts index 9423158251..93583509b1 100644 --- a/packages/compiler-cli/ngcc/src/packages/configuration.ts +++ b/packages/compiler-cli/ngcc/src/packages/configuration.ts @@ -9,7 +9,7 @@ import {createHash} from 'crypto'; import {satisfies} from 'semver'; import * as vm from 'vm'; -import {AbsoluteFsPath, dirname, FileSystem, join, resolve} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, PathManipulation, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {PackageJsonFormatPropertiesMap} from './entry_point'; @@ -186,13 +186,14 @@ export class ProcessedNgccPackageConfig implements Omit [resolve(packagePath, relativePath), config]); + Object.entries(entryPoints).map(([ + relativePath, config + ]) => [fs.resolve(packagePath, relativePath), config]); this.packagePath = packagePath; this.entryPoints = new Map(absolutePathEntries); @@ -230,7 +231,7 @@ export class NgccConfiguration { private cache = new Map(); readonly hash: string; - constructor(private fs: FileSystem, baseDir: AbsoluteFsPath) { + constructor(private fs: ReadonlyFileSystem, baseDir: AbsoluteFsPath) { this.defaultConfig = this.processProjectConfig(DEFAULT_NGCC_CONFIG); this.projectConfig = this.processProjectConfig(this.loadProjectConfig(baseDir)); this.hash = this.computeHash(); @@ -261,7 +262,7 @@ export class NgccConfiguration { getPackageConfig(packageName: string, packagePath: AbsoluteFsPath, version: string|null): ProcessedNgccPackageConfig { const rawPackageConfig = this.getRawPackageConfig(packageName, packagePath, version); - return new ProcessedNgccPackageConfig(packagePath, rawPackageConfig); + return new ProcessedNgccPackageConfig(this.fs, packagePath, rawPackageConfig); } private getRawPackageConfig( @@ -320,7 +321,7 @@ export class NgccConfiguration { } private loadProjectConfig(baseDir: AbsoluteFsPath): NgccProjectConfig { - const configFilePath = join(baseDir, NGCC_CONFIG_FILENAME); + const configFilePath = this.fs.join(baseDir, NGCC_CONFIG_FILENAME); if (this.fs.exists(configFilePath)) { try { return this.evalSrcFile(configFilePath); @@ -334,7 +335,7 @@ export class NgccConfiguration { private loadPackageConfig(packagePath: AbsoluteFsPath, version: string|null): VersionedPackageConfig|null { - const configFilePath = join(packagePath, NGCC_CONFIG_FILENAME); + const configFilePath = this.fs.join(packagePath, NGCC_CONFIG_FILENAME); if (this.fs.exists(configFilePath)) { try { const packageConfig = this.evalSrcFile(configFilePath); @@ -357,7 +358,7 @@ export class NgccConfiguration { module: {exports: theExports}, exports: theExports, require, - __dirname: dirname(srcPath), + __dirname: this.fs.dirname(srcPath), __filename: srcPath }; vm.runInNewContext(src, sandbox, {filename: srcPath}); diff --git a/packages/compiler-cli/ngcc/src/packages/entry_point.ts b/packages/compiler-cli/ngcc/src/packages/entry_point.ts index 2d725217b0..74ee579f44 100644 --- a/packages/compiler-cli/ngcc/src/packages/entry_point.ts +++ b/packages/compiler-cli/ngcc/src/packages/entry_point.ts @@ -5,11 +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 {relative} from 'canonical-path'; -import {basename} from 'path'; import * as ts from 'typescript'; -import {AbsoluteFsPath, FileSystem, join, resolve} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, PathManipulation, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; import {parseStatementForUmdModule} from '../host/umd_host'; import {resolveFileWithPostfixes} from '../utils'; @@ -130,10 +128,10 @@ export type GetEntryPointResult = * entry-point. */ export function getEntryPointInfo( - fs: FileSystem, config: NgccConfiguration, logger: Logger, packagePath: AbsoluteFsPath, + fs: ReadonlyFileSystem, config: NgccConfiguration, logger: Logger, packagePath: AbsoluteFsPath, entryPointPath: AbsoluteFsPath): GetEntryPointResult { - const packagePackageJsonPath = resolve(packagePath, 'package.json'); - const entryPointPackageJsonPath = resolve(entryPointPath, 'package.json'); + const packagePackageJsonPath = fs.resolve(packagePath, 'package.json'); + const entryPointPackageJsonPath = fs.resolve(entryPointPath, 'package.json'); const loadedPackagePackageJson = loadPackageJson(fs, packagePackageJsonPath); const loadedEntryPointPackageJson = (packagePackageJsonPath === entryPointPackageJsonPath) ? loadedPackagePackageJson : @@ -163,7 +161,7 @@ export function getEntryPointInfo( return IGNORED_ENTRY_POINT; } else { entryPointPackageJson = mergeConfigAndPackageJson( - loadedEntryPointPackageJson, entryPointConfig, packagePath, entryPointPath); + fs, loadedEntryPointPackageJson, entryPointConfig, packagePath, entryPointPath); } const typings = entryPointPackageJson.typings || entryPointPackageJson.types || @@ -176,7 +174,8 @@ export function getEntryPointInfo( // An entry-point is assumed to be compiled by Angular if there is either: // * a `metadata.json` file next to the typings entry-point // * a custom config for this entry-point - const metadataPath = resolve(entryPointPath, typings.replace(/\.d\.ts$/, '') + '.metadata.json'); + const metadataPath = + fs.resolve(entryPointPath, typings.replace(/\.d\.ts$/, '') + '.metadata.json'); const compiledByAngular = entryPointConfig !== undefined || fs.exists(metadataPath); const entryPointInfo: EntryPoint = { @@ -185,7 +184,7 @@ export function getEntryPointInfo( packageName, packagePath, packageJson: entryPointPackageJson, - typings: resolve(entryPointPath, typings), + typings: fs.resolve(entryPointPath, typings), compiledByAngular, ignoreMissingDependencies: entryPointConfig !== undefined ? !!entryPointConfig.ignoreMissingDependencies : false, @@ -208,8 +207,8 @@ export function isEntryPoint(result: GetEntryPointResult): result is EntryPoint * @returns An entry-point format or `undefined` if none match the given property. */ export function getEntryPointFormat( - fs: FileSystem, entryPoint: EntryPoint, property: EntryPointJsonProperty): EntryPointFormat| - undefined { + fs: ReadonlyFileSystem, entryPoint: EntryPoint, + property: EntryPointJsonProperty): EntryPointFormat|undefined { switch (property) { case 'fesm2015': return 'esm2015'; @@ -226,13 +225,13 @@ export function getEntryPointFormat( if (typeof browserFile !== 'string') { return undefined; } - return sniffModuleFormat(fs, join(entryPoint.path, browserFile)); + return sniffModuleFormat(fs, fs.join(entryPoint.path, browserFile)); case 'main': const mainFile = entryPoint.packageJson['main']; if (mainFile === undefined) { return undefined; } - return sniffModuleFormat(fs, join(entryPoint.path, mainFile)); + return sniffModuleFormat(fs, fs.join(entryPoint.path, mainFile)); case 'module': const moduleFilePath = entryPoint.packageJson['module']; // As of version 10, the `module` property in `package.json` should point to @@ -254,17 +253,17 @@ export function getEntryPointFormat( * @param packageJsonPath the absolute path to the `package.json` file. * @returns JSON from the `package.json` file if it is valid, `null` otherwise. */ -function loadPackageJson(fs: FileSystem, packageJsonPath: AbsoluteFsPath): EntryPointPackageJson| - null { +function loadPackageJson( + fs: ReadonlyFileSystem, packageJsonPath: AbsoluteFsPath): EntryPointPackageJson|null { try { - return JSON.parse(fs.readFile(packageJsonPath)); + return JSON.parse(fs.readFile(packageJsonPath)) as EntryPointPackageJson; } catch { return null; } } -function sniffModuleFormat(fs: FileSystem, sourceFilePath: AbsoluteFsPath): EntryPointFormat| - undefined { +function sniffModuleFormat( + fs: ReadonlyFileSystem, sourceFilePath: AbsoluteFsPath): EntryPointFormat|undefined { const resolvedPath = resolveFileWithPostfixes(fs, sourceFilePath, ['', '.js', '/index.js']); if (resolvedPath === null) { return undefined; @@ -285,18 +284,19 @@ function sniffModuleFormat(fs: FileSystem, sourceFilePath: AbsoluteFsPath): Entr } function mergeConfigAndPackageJson( - entryPointPackageJson: EntryPointPackageJson|null, entryPointConfig: NgccEntryPointConfig, - packagePath: AbsoluteFsPath, entryPointPath: AbsoluteFsPath): EntryPointPackageJson { + fs: PathManipulation, entryPointPackageJson: EntryPointPackageJson|null, + entryPointConfig: NgccEntryPointConfig, packagePath: AbsoluteFsPath, + entryPointPath: AbsoluteFsPath): EntryPointPackageJson { if (entryPointPackageJson !== null) { return {...entryPointPackageJson, ...entryPointConfig.override}; } else { - const name = `${basename(packagePath)}/${relative(packagePath, entryPointPath)}`; + const name = `${fs.basename(packagePath)}/${fs.relative(packagePath, entryPointPath)}`; return {name, ...entryPointConfig.override}; } } function guessTypingsFromPackageJson( - fs: FileSystem, entryPointPath: AbsoluteFsPath, + fs: ReadonlyFileSystem, entryPointPath: AbsoluteFsPath, entryPointPackageJson: EntryPointPackageJson): AbsoluteFsPath|null { for (const prop of SUPPORTED_FORMAT_PROPERTIES) { const field = entryPointPackageJson[prop]; @@ -305,7 +305,7 @@ function guessTypingsFromPackageJson( continue; } const relativeTypingsPath = field.replace(/\.js$/, '.d.ts'); - const typingsPath = resolve(entryPointPath, relativeTypingsPath); + const typingsPath = fs.resolve(entryPointPath, relativeTypingsPath); if (fs.exists(typingsPath)) { return typingsPath; } @@ -321,14 +321,15 @@ function guessTypingsFromPackageJson( * - The version is read off of the `version` property of the package's `package.json` file (if * available). * - * @param fs The `FileSystem` instance to use for parsing `packagePath` (if needed). + * @param fs The file-system to use for processing `packagePath`. * @param packagePath the absolute path to the package. * @param packagePackageJson the parsed `package.json` of the package (if available). * @param entryPointPackageJson the parsed `package.json` of an entry-point (if available). * @returns the computed name and version of the package. */ function getPackageNameAndVersion( - fs: FileSystem, packagePath: AbsoluteFsPath, packagePackageJson: EntryPointPackageJson|null, + fs: PathManipulation, packagePath: AbsoluteFsPath, + packagePackageJson: EntryPointPackageJson|null, entryPointPackageJson: EntryPointPackageJson| null): {packageName: string, packageVersion: string|null} { let packageName: string; 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 4acd7f0031..25fffcf41c 100644 --- a/packages/compiler-cli/ngcc/src/packages/entry_point_bundle.ts +++ b/packages/compiler-cli/ngcc/src/packages/entry_point_bundle.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ import * as ts from 'typescript'; -import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, FileSystem, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; +import {DtsProcessing} from '../execution/tasks/api'; import {PathMappings} from '../path_mappings'; import {BundleProgram, makeBundleProgram} from './bundle_program'; import {EntryPoint, EntryPointFormat} from './entry_point'; @@ -25,6 +26,7 @@ export interface EntryPointBundle { rootDirs: AbsoluteFsPath[]; src: BundleProgram; dts: BundleProgram|null; + dtsProcessing: DtsProcessing; enableI18nLegacyMessageIdFormat: boolean; } @@ -37,7 +39,7 @@ export interface EntryPointBundle { * @param formatPath The path to the source files for this bundle. * @param isCore This entry point is the Angular core package. * @param format The underlying format of the bundle. - * @param transformDts Whether to transform the typings along with this bundle. + * @param dtsProcessing Whether to transform the typings along with this bundle. * @param pathMappings An optional set of mappings to use when compiling files. * @param mirrorDtsFromSrc If true then the `dts` program will contain additional files that * were guessed by mapping the `src` files to `dts` files. @@ -47,7 +49,7 @@ export interface EntryPointBundle { export function makeEntryPointBundle( fs: FileSystem, entryPoint: EntryPoint, sharedFileCache: SharedFileCache, moduleResolutionCache: ts.ModuleResolutionCache, formatPath: string, isCore: boolean, - format: EntryPointFormat, transformDts: boolean, pathMappings?: PathMappings, + format: EntryPointFormat, dtsProcessing: DtsProcessing, pathMappings?: PathMappings, mirrorDtsFromSrc: boolean = false, enableI18nLegacyMessageIdFormat: boolean = true): EntryPointBundle { // Create the TS program and necessary helpers. @@ -64,13 +66,14 @@ export function makeEntryPointBundle( const typingsPath = fs.resolve(entryPoint.path, entryPoint.typings); const src = makeBundleProgram( fs, isCore, entryPoint.packagePath, absFormatPath, 'r3_symbols.js', options, srcHost); - const additionalDtsFiles = transformDts && mirrorDtsFromSrc ? + const additionalDtsFiles = dtsProcessing !== DtsProcessing.No && mirrorDtsFromSrc ? computePotentialDtsFilesFromJsFiles(fs, src.program, absFormatPath, typingsPath) : []; - const dts = transformDts ? makeBundleProgram( - fs, isCore, entryPoint.packagePath, typingsPath, 'r3_symbols.d.ts', - {...options, allowJs: false}, dtsHost, additionalDtsFiles) : - null; + const dts = dtsProcessing !== DtsProcessing.No ? + makeBundleProgram( + fs, isCore, entryPoint.packagePath, typingsPath, 'r3_symbols.d.ts', + {...options, allowJs: false}, dtsHost, additionalDtsFiles) : + null; const isFlatCore = isCore && src.r3SymbolsFile === null; return { @@ -81,12 +84,13 @@ export function makeEntryPointBundle( isFlatCore, src, dts, + dtsProcessing, enableI18nLegacyMessageIdFormat }; } function computePotentialDtsFilesFromJsFiles( - fs: FileSystem, srcProgram: ts.Program, formatPath: AbsoluteFsPath, + fs: ReadonlyFileSystem, srcProgram: ts.Program, formatPath: AbsoluteFsPath, typingsPath: AbsoluteFsPath) { const formatRoot = fs.dirname(formatPath); const typingsRoot = fs.dirname(typingsPath); 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 4bb317ce48..3c951dd5b5 100644 --- a/packages/compiler-cli/ngcc/src/packages/entry_point_manifest.ts +++ b/packages/compiler-cli/ngcc/src/packages/entry_point_manifest.ts @@ -13,7 +13,7 @@ import {EntryPointWithDependencies} from '../dependencies/dependency_host'; import {NGCC_VERSION} from './build_marker'; import {NgccConfiguration} from './configuration'; -import {getEntryPointInfo, isEntryPoint} from './entry_point'; +import {getEntryPointInfo, isEntryPoint, PackageJsonFormatProperties} from './entry_point'; /** * Manages reading and writing a manifest file that contains a list of all the entry-points that @@ -197,3 +197,9 @@ export interface EntryPointManifestFile { lockFileHash: string; entryPointPaths: EntryPointPaths[]; } + + +/** The JSON format of the entrypoint properties. */ +export type NewEntryPointPropertiesMap = { + [Property in PackageJsonFormatProperties as `${Property}_ivy_ngcc`]?: string; +}; diff --git a/packages/compiler-cli/ngcc/src/packages/source_file_cache.ts b/packages/compiler-cli/ngcc/src/packages/source_file_cache.ts index d9b0c33637..2305e3dde0 100644 --- a/packages/compiler-cli/ngcc/src/packages/source_file_cache.ts +++ b/packages/compiler-cli/ngcc/src/packages/source_file_cache.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import * as ts from 'typescript'; -import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; /** * A cache that holds on to source files that can be shared for processing all entry-points in a @@ -29,7 +29,7 @@ import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system'; export class SharedFileCache { private sfCache = new Map(); - constructor(private fs: FileSystem) {} + constructor(private fs: ReadonlyFileSystem) {} /** * Loads a `ts.SourceFile` if the provided `fileName` is deemed appropriate to be cached. To @@ -95,7 +95,7 @@ const DEFAULT_LIB_PATTERN = ['node_modules', 'typescript', 'lib', /^lib\..+\.d\. * @param absPath The path for which to determine if it corresponds with a default library file. * @param fs The filesystem to use for inspecting the path. */ -export function isDefaultLibrary(absPath: AbsoluteFsPath, fs: FileSystem): boolean { +export function isDefaultLibrary(absPath: AbsoluteFsPath, fs: ReadonlyFileSystem): boolean { return isFile(absPath, DEFAULT_LIB_PATTERN, fs); } @@ -109,7 +109,7 @@ const ANGULAR_DTS_PATTERN = ['node_modules', '@angular', /./, /\.d\.ts$/]; * @param absPath The path for which to determine if it corresponds with an @angular .d.ts file. * @param fs The filesystem to use for inspecting the path. */ -export function isAngularDts(absPath: AbsoluteFsPath, fs: FileSystem): boolean { +export function isAngularDts(absPath: AbsoluteFsPath, fs: ReadonlyFileSystem): boolean { return isFile(absPath, ANGULAR_DTS_PATTERN, fs); } @@ -122,7 +122,7 @@ export function isAngularDts(absPath: AbsoluteFsPath, fs: FileSystem): boolean { * @param fs The filesystem to use for inspecting the path. */ function isFile( - path: AbsoluteFsPath, segments: ReadonlyArray, fs: FileSystem): boolean { + path: AbsoluteFsPath, segments: ReadonlyArray, fs: ReadonlyFileSystem): boolean { for (let i = segments.length - 1; i >= 0; i--) { const pattern = segments[i]; const segment = fs.basename(path); @@ -147,7 +147,7 @@ function isFile( export class EntryPointFileCache { private readonly sfCache = new Map(); - constructor(private fs: FileSystem, private sharedFileCache: SharedFileCache) {} + constructor(private fs: ReadonlyFileSystem, private sharedFileCache: SharedFileCache) {} /** * Returns and caches a parsed `ts.SourceFile` for the provided `fileName`. If the `fileName` is @@ -178,7 +178,7 @@ export class EntryPointFileCache { } } -function readFile(absPath: AbsoluteFsPath, fs: FileSystem): string|undefined { +function readFile(absPath: AbsoluteFsPath, fs: ReadonlyFileSystem): string|undefined { if (!fs.exists(absPath) || !fs.stat(absPath).isFile()) { return undefined; } @@ -190,7 +190,7 @@ function readFile(absPath: AbsoluteFsPath, fs: FileSystem): string|undefined { * * @param fs The filesystem to use for path operations. */ -export function createModuleResolutionCache(fs: FileSystem): ts.ModuleResolutionCache { +export function createModuleResolutionCache(fs: ReadonlyFileSystem): ts.ModuleResolutionCache { return ts.createModuleResolutionCache(fs.pwd(), fileName => { return fs.isCaseSensitive() ? fileName : fileName.toLowerCase(); }); diff --git a/packages/compiler-cli/ngcc/src/packages/transformer.ts b/packages/compiler-cli/ngcc/src/packages/transformer.ts index c8cf06dd37..78aa506c02 100644 --- a/packages/compiler-cli/ngcc/src/packages/transformer.ts +++ b/packages/compiler-cli/ngcc/src/packages/transformer.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; import {ParsedConfiguration} from '../../..'; -import {FileSystem} from '../../../src/ngtsc/file_system'; +import {ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; import {TypeScriptReflectionHost} from '../../../src/ngtsc/reflection'; import {DecorationAnalyzer} from '../analysis/decoration_analyzer'; @@ -17,6 +17,7 @@ import {NgccReferencesRegistry} from '../analysis/ngcc_references_registry'; import {ExportInfo, PrivateDeclarationsAnalyzer} from '../analysis/private_declarations_analyzer'; import {SwitchMarkerAnalyses, SwitchMarkerAnalyzer} from '../analysis/switch_marker_analyzer'; import {CompiledFile} from '../analysis/types'; +import {DtsProcessing} from '../execution/tasks/api'; import {CommonJsReflectionHost} from '../host/commonjs_host'; import {DelegatingReflectionHost} from '../host/delegating_host'; import {Esm2015ReflectionHost} from '../host/esm2015_host'; @@ -64,7 +65,7 @@ export type TransformResult = { */ export class Transformer { constructor( - private fs: FileSystem, private logger: Logger, + private fs: ReadonlyFileSystem, private logger: Logger, private tsConfig: ParsedConfiguration|null = null) {} /** @@ -92,15 +93,19 @@ export class Transformer { } // Transform the source files and source maps. - const srcFormatter = this.getRenderingFormatter(ngccReflectionHost, bundle); + let renderedFiles: FileToWrite[] = []; - const renderer = - new Renderer(reflectionHost, srcFormatter, this.fs, this.logger, bundle, this.tsConfig); - let renderedFiles = renderer.renderProgram( - decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); + if (bundle.dtsProcessing !== DtsProcessing.Only) { + // Render the transformed JavaScript files only if we are not doing "typings-only" processing. + const srcFormatter = this.getRenderingFormatter(ngccReflectionHost, bundle); + const renderer = + new Renderer(reflectionHost, srcFormatter, this.fs, this.logger, bundle, this.tsConfig); + renderedFiles = renderer.renderProgram( + decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); + } if (bundle.dts) { - const dtsFormatter = new EsmRenderingFormatter(reflectionHost, bundle.isCore); + const dtsFormatter = new EsmRenderingFormatter(this.fs, reflectionHost, bundle.isCore); const dtsRenderer = new DtsRenderer(dtsFormatter, this.fs, this.logger, reflectionHost, bundle); const renderedDtsFiles = dtsRenderer.renderProgram( @@ -129,16 +134,16 @@ export class Transformer { getRenderingFormatter(host: NgccReflectionHost, bundle: EntryPointBundle): RenderingFormatter { switch (bundle.format) { case 'esm2015': - return new EsmRenderingFormatter(host, bundle.isCore); + return new EsmRenderingFormatter(this.fs, host, bundle.isCore); case 'esm5': - return new Esm5RenderingFormatter(host, bundle.isCore); + return new Esm5RenderingFormatter(this.fs, host, bundle.isCore); case 'umd': if (!(host instanceof UmdReflectionHost)) { throw new Error('UmdRenderer requires a UmdReflectionHost'); } - return new UmdRenderingFormatter(host, bundle.isCore); + return new UmdRenderingFormatter(this.fs, host, bundle.isCore); case 'commonjs': - return new CommonJsRenderingFormatter(host, bundle.isCore); + return new CommonJsRenderingFormatter(this.fs, host, bundle.isCore); default: throw new Error(`Renderer for "${bundle.format}" not yet implemented.`); } diff --git a/packages/compiler-cli/ngcc/src/path_mappings.ts b/packages/compiler-cli/ngcc/src/path_mappings.ts index 5a0fa4ff75..e41311d0d6 100644 --- a/packages/compiler-cli/ngcc/src/path_mappings.ts +++ b/packages/compiler-cli/ngcc/src/path_mappings.ts @@ -5,10 +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, resolve} from '../../src/ngtsc/file_system'; +import {AbsoluteFsPath, PathManipulation} from '../../src/ngtsc/file_system'; import {ParsedConfiguration} from '../../src/perform_compile'; - export type PathMappings = { baseUrl: string, paths: {[key: string]: string[]} @@ -18,11 +17,12 @@ export type PathMappings = { * If `pathMappings` is not provided directly, then try getting it from `tsConfig`, if available. */ export function getPathMappingsFromTsConfig( - tsConfig: ParsedConfiguration|null, projectPath: AbsoluteFsPath): PathMappings|undefined { + fs: PathManipulation, 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), + baseUrl: fs.resolve(projectPath, tsConfig.options.baseUrl), paths: tsConfig.options.paths, }; } 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 962f94ad29..f18a0181dc 100644 --- a/packages/compiler-cli/ngcc/src/rendering/commonjs_rendering_formatter.ts +++ b/packages/compiler-cli/ngcc/src/rendering/commonjs_rendering_formatter.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 {dirname, relative} from 'canonical-path'; +import {PathManipulation} from '@angular/compiler-cli/src/ngtsc/file_system'; import MagicString from 'magic-string'; import * as ts from 'typescript'; @@ -24,8 +24,8 @@ import {stripExtension} from './utils'; * wrapper function for AMD, CommonJS and global module formats. */ export class CommonJsRenderingFormatter extends Esm5RenderingFormatter { - constructor(protected commonJsHost: NgccReflectionHost, isCore: boolean) { - super(commonJsHost, isCore); + constructor(fs: PathManipulation, protected commonJsHost: NgccReflectionHost, isCore: boolean) { + super(fs, commonJsHost, isCore); } /** @@ -39,7 +39,7 @@ export class CommonJsRenderingFormatter extends Esm5RenderingFormatter { const insertionPoint = this.findEndOfImports(file); const renderedImports = - imports.map(i => `var ${i.qualifier} = require('${i.specifier}');\n`).join(''); + imports.map(i => `var ${i.qualifier.text} = require('${i.specifier}');\n`).join(''); output.appendLeft(insertionPoint, renderedImports); } @@ -51,7 +51,7 @@ export class CommonJsRenderingFormatter extends Esm5RenderingFormatter { importManager: ImportManager, file: ts.SourceFile): void { exports.forEach(e => { const basePath = stripExtension(e.from); - const relativePath = './' + relative(dirname(entryPointBasePath), basePath); + const relativePath = './' + this.fs.relative(this.fs.dirname(entryPointBasePath), basePath); const namedImport = entryPointBasePath !== basePath ? importManager.generateNamedImport(relativePath, e.identifier) : {symbol: e.identifier, moduleImport: null}; diff --git a/packages/compiler-cli/ngcc/src/rendering/dts_renderer.ts b/packages/compiler-cli/ngcc/src/rendering/dts_renderer.ts index 465a4566d1..337cb673d9 100644 --- a/packages/compiler-cli/ngcc/src/rendering/dts_renderer.ts +++ b/packages/compiler-cli/ngcc/src/rendering/dts_renderer.ts @@ -8,7 +8,7 @@ import MagicString from 'magic-string'; import * as ts from 'typescript'; -import {FileSystem} from '../../../src/ngtsc/file_system'; +import {ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {Reexport} from '../../../src/ngtsc/imports'; import {Logger} from '../../../src/ngtsc/logging'; import {CompileResult} from '../../../src/ngtsc/transform'; @@ -56,8 +56,8 @@ export interface DtsClassInfo { */ export class DtsRenderer { constructor( - private dtsFormatter: RenderingFormatter, private fs: FileSystem, private logger: Logger, - private host: NgccReflectionHost, private bundle: EntryPointBundle) {} + private dtsFormatter: RenderingFormatter, private fs: ReadonlyFileSystem, + private logger: Logger, private host: NgccReflectionHost, private bundle: EntryPointBundle) {} renderProgram( decorationAnalyses: DecorationAnalyses, 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 8cb3e68310..2f5fdc4b5f 100644 --- a/packages/compiler-cli/ngcc/src/rendering/esm5_rendering_formatter.ts +++ b/packages/compiler-cli/ngcc/src/rendering/esm5_rendering_formatter.ts @@ -65,8 +65,7 @@ export class Esm5RenderingFormatter extends EsmRenderingFormatter { */ printStatement(stmt: Statement, sourceFile: ts.SourceFile, importManager: ImportManager): string { const node = translateStatement( - stmt, importManager, - {downlevelLocalizedStrings: true, downlevelVariableDeclarations: true}); + stmt, importManager, {downlevelTaggedTemplates: true, downlevelVariableDeclarations: true}); const code = this.printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); return code; 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 28aeaec3b1..7edf77e072 100644 --- a/packages/compiler-cli/ngcc/src/rendering/esm_rendering_formatter.ts +++ b/packages/compiler-cli/ngcc/src/rendering/esm_rendering_formatter.ts @@ -9,7 +9,7 @@ import {Statement} from '@angular/compiler'; import MagicString from 'magic-string'; import * as ts from 'typescript'; -import {absoluteFromSourceFile, AbsoluteFsPath, dirname, relative, toRelativeImport} from '../../../src/ngtsc/file_system'; +import {absoluteFromSourceFile, AbsoluteFsPath, PathManipulation, toRelativeImport} from '../../../src/ngtsc/file_system'; import {Reexport} from '../../../src/ngtsc/imports'; import {Import, ImportManager, translateStatement} from '../../../src/ngtsc/translator'; import {isDtsPath} from '../../../src/ngtsc/util/src/typescript'; @@ -28,7 +28,9 @@ import {stripExtension} from './utils'; export class EsmRenderingFormatter implements RenderingFormatter { protected printer = ts.createPrinter({newLine: ts.NewLineKind.LineFeed}); - constructor(protected host: NgccReflectionHost, protected isCore: boolean) {} + constructor( + protected fs: PathManipulation, protected host: NgccReflectionHost, + protected isCore: boolean) {} /** * Add the imports at the top of the file, after any imports that are already there. @@ -40,7 +42,7 @@ export class EsmRenderingFormatter implements RenderingFormatter { const insertionPoint = this.findEndOfImports(sf); const renderedImports = - imports.map(i => `import * as ${i.qualifier} from '${i.specifier}';\n`).join(''); + imports.map(i => `import * as ${i.qualifier.text} from '${i.specifier}';\n`).join(''); output.appendLeft(insertionPoint, renderedImports); } @@ -57,7 +59,7 @@ export class EsmRenderingFormatter implements RenderingFormatter { if (from) { const basePath = stripExtension(from); - const relativePath = relative(dirname(entryPointBasePath), basePath); + const relativePath = this.fs.relative(this.fs.dirname(entryPointBasePath), basePath); const relativeImport = toRelativeImport(relativePath); exportFrom = entryPointBasePath !== basePath ? ` from '${relativeImport}'` : ''; } @@ -171,7 +173,7 @@ export class EsmRenderingFormatter implements RenderingFormatter { } /** - * Rewrite the the IVY switch markers to indicate we are in IVY mode. + * Rewrite the IVY switch markers to indicate we are in IVY mode. */ rewriteSwitchableDeclarations( outputText: MagicString, sourceFile: ts.SourceFile, @@ -198,7 +200,7 @@ export class EsmRenderingFormatter implements RenderingFormatter { const ngModuleName = info.ngModule.node.name.text; const declarationFile = absoluteFromSourceFile(info.declaration.getSourceFile()); const ngModuleFile = absoluteFromSourceFile(info.ngModule.node.getSourceFile()); - const relativePath = relative(dirname(declarationFile), ngModuleFile); + const relativePath = this.fs.relative(this.fs.dirname(declarationFile), ngModuleFile); const relativeImport = toRelativeImport(relativePath); const importPath = info.ngModule.ownedByModuleGuess || (declarationFile !== ngModuleFile ? stripExtension(relativeImport) : null); diff --git a/packages/compiler-cli/ngcc/src/rendering/renderer.ts b/packages/compiler-cli/ngcc/src/rendering/renderer.ts index 8e02accfda..8cbe2edea2 100644 --- a/packages/compiler-cli/ngcc/src/rendering/renderer.ts +++ b/packages/compiler-cli/ngcc/src/rendering/renderer.ts @@ -9,7 +9,7 @@ import {ConstantPool, Expression, jsDocComment, LeadingComment, Statement, Wrapp import MagicString from 'magic-string'; import * as ts from 'typescript'; -import {FileSystem} from '../../../src/ngtsc/file_system'; +import {ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; import {ImportManager} from '../../../src/ngtsc/translator'; import {ParsedConfiguration} from '../../../src/perform_compile'; @@ -33,7 +33,7 @@ import {FileToWrite, getImportRewriter, stripExtension} from './utils'; export class Renderer { constructor( private host: NgccReflectionHost, private srcFormatter: RenderingFormatter, - private fs: FileSystem, private logger: Logger, private bundle: EntryPointBundle, + private fs: ReadonlyFileSystem, private logger: Logger, private bundle: EntryPointBundle, private tsConfig: ParsedConfiguration|null = null) {} renderProgram( diff --git a/packages/compiler-cli/ngcc/src/rendering/source_maps.ts b/packages/compiler-cli/ngcc/src/rendering/source_maps.ts index ec1b64c1f4..5091ac5ae3 100644 --- a/packages/compiler-cli/ngcc/src/rendering/source_maps.ts +++ b/packages/compiler-cli/ngcc/src/rendering/source_maps.ts @@ -9,9 +9,9 @@ import {fromObject, generateMapFileComment, SourceMapConverter} from 'convert-so import MagicString from 'magic-string'; import * as ts from 'typescript'; -import {absoluteFrom, absoluteFromSourceFile, basename, FileSystem} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, absoluteFromSourceFile, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; -import {RawSourceMap, SourceFileLoader} from '../../../src/ngtsc/sourcemaps'; +import {ContentOrigin, RawSourceMap, SourceFileLoader} from '../../../src/ngtsc/sourcemaps'; import {FileToWrite} from './utils'; @@ -26,24 +26,24 @@ export interface SourceMapInfo { * with an appropriate source-map comment pointing to the merged source-map. */ export function renderSourceAndMap( - logger: Logger, fs: FileSystem, sourceFile: ts.SourceFile, + logger: Logger, fs: ReadonlyFileSystem, sourceFile: ts.SourceFile, generatedMagicString: MagicString): FileToWrite[] { - const generatedPath = absoluteFromSourceFile(sourceFile); - const generatedMapPath = absoluteFrom(`${generatedPath}.map`); + const sourceFilePath = absoluteFromSourceFile(sourceFile); + const sourceMapPath = absoluteFrom(`${sourceFilePath}.map`); const generatedContent = generatedMagicString.toString(); const generatedMap: RawSourceMap = generatedMagicString.generateMap( - {file: generatedPath, source: generatedPath, includeContent: true}); + {file: sourceFilePath, source: sourceFilePath, includeContent: true}); try { const loader = new SourceFileLoader(fs, logger, {}); const generatedFile = loader.loadSourceFile( - generatedPath, generatedContent, {map: generatedMap, mapPath: generatedMapPath}); + sourceFilePath, generatedContent, {map: generatedMap, mapPath: sourceMapPath}); const rawMergedMap: RawSourceMap = generatedFile.renderFlattenedSourceMap(); const mergedMap = fromObject(rawMergedMap); - const firstSource = generatedFile.sources[0]; - if (firstSource && (firstSource.rawMap !== null || !sourceFile.isDeclarationFile) && - firstSource.inline) { + const originalFile = loader.loadSourceFile(sourceFilePath, generatedMagicString.original); + if (originalFile.rawMap === null && !sourceFile.isDeclarationFile || + originalFile.rawMap?.origin === ContentOrigin.Inline) { // We render an inline source map if one of: // * there was no input source map and this is not a typings file; // * the input source map exists and was inline. @@ -52,21 +52,21 @@ export function renderSourceAndMap( // the input file because these inline source maps can be very large and it impacts on the // performance of IDEs that need to read them to provide intellisense etc. return [ - {path: generatedPath, contents: `${generatedFile.contents}\n${mergedMap.toComment()}`} - ]; - } else { - const sourceMapComment = generateMapFileComment(`${basename(generatedPath)}.map`); - return [ - {path: generatedPath, contents: `${generatedFile.contents}\n${sourceMapComment}`}, - {path: generatedMapPath, contents: mergedMap.toJSON()} + {path: sourceFilePath, contents: `${generatedFile.contents}\n${mergedMap.toComment()}`} ]; } - } catch (e) { - logger.error(`Error when flattening the source-map "${generatedMapPath}" for "${ - generatedPath}": ${e.toString()}`); + + const sourceMapComment = generateMapFileComment(`${fs.basename(sourceFilePath)}.map`); return [ - {path: generatedPath, contents: generatedContent}, - {path: generatedMapPath, contents: fromObject(generatedMap).toJSON()}, + {path: sourceFilePath, contents: `${generatedFile.contents}\n${sourceMapComment}`}, + {path: sourceMapPath, contents: mergedMap.toJSON()} + ]; + } catch (e) { + logger.error(`Error when flattening the source-map "${sourceMapPath}" for "${ + sourceFilePath}": ${e.toString()}`); + return [ + {path: sourceFilePath, contents: generatedContent}, + {path: sourceMapPath, 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 3fbf9755de..662c046d07 100644 --- a/packages/compiler-cli/ngcc/src/rendering/umd_rendering_formatter.ts +++ b/packages/compiler-cli/ngcc/src/rendering/umd_rendering_formatter.ts @@ -5,10 +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 {dirname, relative} from 'canonical-path'; import MagicString from 'magic-string'; import * as ts from 'typescript'; +import {PathManipulation} from '../../../src/ngtsc/file_system'; import {Reexport} from '../../../src/ngtsc/imports'; import {Import, ImportManager} from '../../../src/ngtsc/translator'; import {ExportInfo} from '../analysis/private_declarations_analyzer'; @@ -26,8 +26,8 @@ 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(fs: PathManipulation, protected umdHost: UmdReflectionHost, isCore: boolean) { + super(fs, umdHost, isCore); } /** @@ -87,7 +87,7 @@ export class UmdRenderingFormatter extends Esm5RenderingFormatter { lastStatement ? lastStatement.getEnd() : factoryFunction.body.getEnd() - 1; exports.forEach(e => { const basePath = stripExtension(e.from); - const relativePath = './' + relative(dirname(entryPointBasePath), basePath); + const relativePath = './' + this.fs.relative(this.fs.dirname(entryPointBasePath), basePath); const namedImport = entryPointBasePath !== basePath ? importManager.generateNamedImport(relativePath, e.identifier) : {symbol: e.identifier, moduleImport: null}; @@ -225,7 +225,7 @@ function renderFactoryParameters( } const parameters = factoryFunction.parameters; - const parameterString = imports.map(i => i.qualifier).join(','); + const parameterString = imports.map(i => i.qualifier.text).join(','); if (parameters.length > 0) { const injectionPoint = parameters[0].getFullStart(); output.appendLeft(injectionPoint, parameterString + ','); diff --git a/packages/compiler-cli/ngcc/src/utils.ts b/packages/compiler-cli/ngcc/src/utils.ts index de8b4c3193..7ca4d1923b 100644 --- a/packages/compiler-cli/ngcc/src/utils.ts +++ b/packages/compiler-cli/ngcc/src/utils.ts @@ -7,7 +7,7 @@ */ import * as ts from 'typescript'; -import {absoluteFrom, AbsoluteFsPath, FileSystem, isRooted} from '../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, isRooted, ReadonlyFileSystem} from '../../src/ngtsc/file_system'; import {DeclarationNode, KnownDeclaration} from '../../src/ngtsc/reflection'; /** @@ -122,7 +122,7 @@ export class FactoryMap { * @returns An absolute path to the first matching existing file, or `null` if none exist. */ export function resolveFileWithPostfixes( - fs: FileSystem, path: AbsoluteFsPath, postFixes: string[]): AbsoluteFsPath|null { + fs: ReadonlyFileSystem, path: AbsoluteFsPath, postFixes: string[]): AbsoluteFsPath|null { for (const postFix of postFixes) { const testPath = absoluteFrom(path + postFix); if (fs.exists(testPath) && fs.stat(testPath).isFile()) { @@ -161,6 +161,10 @@ export function getTsHelperFnFromIdentifier(id: ts.Identifier): KnownDeclaration return KnownDeclaration.TsHelperSpread; case '__spreadArrays': return KnownDeclaration.TsHelperSpreadArrays; + case '__spreadArray': + return KnownDeclaration.TsHelperSpreadArray; + case '__read': + return KnownDeclaration.TsHelperRead; default: return null; } 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 9ce069d9a3..b7a778ee48 100644 --- a/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts +++ b/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts @@ -7,6 +7,7 @@ */ import {absoluteFrom, AbsoluteFsPath, FileSystem, PathSegment} from '../../../../src/ngtsc/file_system'; import {cleanPackageJson} from '../../packages/build_marker'; +import {EntryPointPackageJson} from '../../packages/entry_point'; import {NGCC_BACKUP_EXTENSION} from '../in_place_file_writer'; import {NGCC_DIRECTORY} from '../new_entry_point_file_writer'; @@ -30,7 +31,7 @@ export class PackageJsonCleaner implements CleaningStrategy { return basename === 'package.json'; } clean(path: AbsoluteFsPath, _basename: PathSegment): void { - const packageJson = JSON.parse(this.fs.readFile(path)); + const packageJson = JSON.parse(this.fs.readFile(path)) as EntryPointPackageJson; if (cleanPackageJson(packageJson)) { this.fs.writeFile(path, `${JSON.stringify(packageJson, null, 2)}\n`); } diff --git a/packages/compiler-cli/ngcc/src/writing/cleaning/package_cleaner.ts b/packages/compiler-cli/ngcc/src/writing/cleaning/package_cleaner.ts index 872f180cef..5b3321af2a 100644 --- a/packages/compiler-cli/ngcc/src/writing/cleaning/package_cleaner.ts +++ b/packages/compiler-cli/ngcc/src/writing/cleaning/package_cleaner.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} from '../../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, FileSystem, ReadonlyFileSystem} from '../../../../src/ngtsc/file_system'; import {needsCleaning} from '../../packages/build_marker'; import {EntryPoint} from '../../packages/entry_point'; @@ -16,7 +16,7 @@ import {isLocalDirectory} from './utils'; * A class that can clean ngcc artifacts from a directory. */ export class PackageCleaner { - constructor(private fs: FileSystem, private cleaners: CleaningStrategy[]) {} + constructor(private fs: ReadonlyFileSystem, private cleaners: CleaningStrategy[]) {} /** * Recurse through the file-system cleaning files and directories as determined by the configured diff --git a/packages/compiler-cli/ngcc/src/writing/cleaning/utils.ts b/packages/compiler-cli/ngcc/src/writing/cleaning/utils.ts index 6b2c49538b..8d36f84637 100644 --- a/packages/compiler-cli/ngcc/src/writing/cleaning/utils.ts +++ b/packages/compiler-cli/ngcc/src/writing/cleaning/utils.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} from '../../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, ReadonlyFileSystem} from '../../../../src/ngtsc/file_system'; /** * Returns true if the given `path` is a directory (not a symlink) and actually exists. @@ -13,7 +13,7 @@ import {AbsoluteFsPath, FileSystem} from '../../../../src/ngtsc/file_system'; * @param fs the current filesystem * @param path the path to check */ -export function isLocalDirectory(fs: FileSystem, path: AbsoluteFsPath): boolean { +export function isLocalDirectory(fs: ReadonlyFileSystem, path: AbsoluteFsPath): boolean { if (fs.exists(path)) { const stat = fs.lstat(path); return stat.isDirectory(); 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 a3931bf926..114d2f72ea 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 {absoluteFromSourceFile, AbsoluteFsPath, dirname, FileSystem, isLocalRelativePath, join, relative, resolve} from '../../../src/ngtsc/file_system'; +import {absoluteFromSourceFile, AbsoluteFsPath, FileSystem, isLocalRelativePath} from '../../../src/ngtsc/file_system'; import {Logger} from '../../../src/ngtsc/logging'; import {isDtsPath} from '../../../src/ngtsc/util/src/typescript'; import {EntryPoint, EntryPointJsonProperty} from '../packages/entry_point'; @@ -39,8 +39,8 @@ export class NewEntryPointFileWriter extends InPlaceFileWriter { formatProperties: EntryPointJsonProperty[]) { // The new folder is at the root of the overall package const entryPoint = bundle.entryPoint; - const ngccFolder = join(entryPoint.packagePath, NGCC_DIRECTORY); - this.copyBundle(bundle, entryPoint.packagePath, ngccFolder); + const ngccFolder = this.fs.join(entryPoint.packagePath, NGCC_DIRECTORY); + this.copyBundle(bundle, entryPoint.packagePath, ngccFolder, transformedFiles); transformedFiles.forEach(file => this.writeFile(file, entryPoint.packagePath, ngccFolder)); this.updatePackageJson(entryPoint, formatProperties, ngccFolder); } @@ -67,27 +67,66 @@ export class NewEntryPointFileWriter extends InPlaceFileWriter { } protected copyBundle( - bundle: EntryPointBundle, packagePath: AbsoluteFsPath, ngccFolder: AbsoluteFsPath) { + bundle: EntryPointBundle, packagePath: AbsoluteFsPath, ngccFolder: AbsoluteFsPath, + transformedFiles: FileToWrite[]) { + const doNotCopy = new Set(transformedFiles.map(f => f.path)); bundle.src.program.getSourceFiles().forEach(sourceFile => { - const relativePath = relative(packagePath, absoluteFromSourceFile(sourceFile)); + const originalPath = absoluteFromSourceFile(sourceFile); + if (doNotCopy.has(originalPath)) { + return; + } + const relativePath = this.fs.relative(packagePath, originalPath); const isInsidePackage = isLocalRelativePath(relativePath); if (!sourceFile.isDeclarationFile && isInsidePackage) { - const newFilePath = resolve(ngccFolder, relativePath); - this.fs.ensureDir(dirname(newFilePath)); - this.fs.copyFile(absoluteFromSourceFile(sourceFile), newFilePath); + const newPath = this.fs.resolve(ngccFolder, relativePath); + this.fs.ensureDir(this.fs.dirname(newPath)); + this.fs.copyFile(originalPath, newPath); + this.copyAndUpdateSourceMap(originalPath, newPath); } }); } + /** + * If a source file has an associated source-map, then copy this, while updating its sourceRoot + * accordingly. + * + * For now don't try to parse the source for inline source-maps or external source-map links, + * since that is more complex and will slow ngcc down. + * Instead just check for a source-map file residing next to the source file, which is by far + * the most common case. + * + * @param originalSrcPath absolute path to the original source file being copied. + * @param newSrcPath absolute path to where the source will be written. + */ + protected copyAndUpdateSourceMap(originalSrcPath: AbsoluteFsPath, newSrcPath: AbsoluteFsPath): + void { + const sourceMapPath = (originalSrcPath + '.map') as AbsoluteFsPath; + if (this.fs.exists(sourceMapPath)) { + try { + const sourceMap = + JSON.parse(this.fs.readFile(sourceMapPath)) as {sourceRoot: string, [key: string]: any}; + const newSourceMapPath = (newSrcPath + '.map') as AbsoluteFsPath; + const relativePath = + this.fs.relative(this.fs.dirname(newSourceMapPath), this.fs.dirname(sourceMapPath)); + sourceMap.sourceRoot = this.fs.join(relativePath, sourceMap.sourceRoot || '.'); + this.fs.ensureDir(this.fs.dirname(newSourceMapPath)); + this.fs.writeFile(newSourceMapPath, JSON.stringify(sourceMap)); + } catch (e) { + this.logger.warn(`Failed to process source-map at ${sourceMapPath}`); + this.logger.warn(e.message ?? e); + } + } + } + protected writeFile(file: FileToWrite, packagePath: AbsoluteFsPath, ngccFolder: AbsoluteFsPath): void { if (isDtsPath(file.path.replace(/\.map$/, ''))) { // This is either `.d.ts` or `.d.ts.map` file super.writeFileAndBackup(file); } else { - const relativePath = relative(packagePath, file.path); - const newFilePath = resolve(ngccFolder, relativePath); - this.fs.ensureDir(dirname(newFilePath)); + const relativePath = this.fs.relative(packagePath, file.path); + const newFilePath = this.fs.resolve(ngccFolder, relativePath); + this.fs.ensureDir(this.fs.dirname(newFilePath)); this.fs.writeFile(newFilePath, file.contents); } } @@ -97,8 +136,8 @@ export class NewEntryPointFileWriter extends InPlaceFileWriter { // This is either `.d.ts` or `.d.ts.map` file super.revertFileAndBackup(filePath); } else if (this.fs.exists(filePath)) { - const relativePath = relative(packagePath, filePath); - const newFilePath = resolve(packagePath, NGCC_DIRECTORY, relativePath); + const relativePath = this.fs.relative(packagePath, filePath); + const newFilePath = this.fs.resolve(packagePath, NGCC_DIRECTORY, relativePath); this.fs.removeFile(newFilePath); } } @@ -112,15 +151,15 @@ export class NewEntryPointFileWriter extends InPlaceFileWriter { } const packageJson = entryPoint.packageJson; - const packageJsonPath = join(entryPoint.path, 'package.json'); + const packageJsonPath = this.fs.join(entryPoint.path, 'package.json'); // All format properties point to the same format-path. const oldFormatProp = formatProperties[0]!; const oldFormatPath = packageJson[oldFormatProp]!; - const oldAbsFormatPath = resolve(entryPoint.path, oldFormatPath); + const oldAbsFormatPath = this.fs.resolve(entryPoint.path, oldFormatPath); const newAbsFormatPath = - resolve(ngccFolder, relative(entryPoint.packagePath, oldAbsFormatPath)); - const newFormatPath = relative(entryPoint.path, newAbsFormatPath); + this.fs.resolve(ngccFolder, this.fs.relative(entryPoint.packagePath, oldAbsFormatPath)); + const newFormatPath = this.fs.relative(entryPoint.path, newAbsFormatPath); // Update all properties in `package.json` (both in memory and on disk). const update = this.pkgJsonUpdater.createUpdate(); @@ -146,7 +185,7 @@ export class NewEntryPointFileWriter extends InPlaceFileWriter { } const packageJson = entryPoint.packageJson; - const packageJsonPath = join(entryPoint.path, 'package.json'); + const packageJsonPath = this.fs.join(entryPoint.path, 'package.json'); // Revert all properties in `package.json` (both in memory and on disk). // Since `updatePackageJson()` only adds properties, it is safe to just remove them (if they 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 744581abef..b3388ce102 100644 --- a/packages/compiler-cli/ngcc/src/writing/package_json_updater.ts +++ b/packages/compiler-cli/ngcc/src/writing/package_json_updater.ts @@ -130,8 +130,9 @@ export class DirectPackageJsonUpdater implements PackageJsonUpdater { // Read and parse the `package.json` content. // NOTE: We are not using `preExistingParsedJson` (even if specified) to avoid corrupting the // content on disk in case `preExistingParsedJson` is outdated. - const parsedJson = - this.fs.exists(packageJsonPath) ? JSON.parse(this.fs.readFile(packageJsonPath)) : {}; + const parsedJson = this.fs.exists(packageJsonPath) ? + JSON.parse(this.fs.readFile(packageJsonPath)) as JsonObject : + {}; // Apply all changes to both the canonical representation (read from disk) and any pre-existing, // in-memory representation. diff --git a/packages/compiler-cli/ngcc/test/BUILD.bazel b/packages/compiler-cli/ngcc/test/BUILD.bazel index c337e11621..0aa28599ac 100644 --- a/packages/compiler-cli/ngcc/test/BUILD.bazel +++ b/packages/compiler-cli/ngcc/test/BUILD.bazel @@ -19,9 +19,11 @@ ts_library( "//packages/compiler-cli/src/ngtsc/file_system", "//packages/compiler-cli/src/ngtsc/file_system/testing", "//packages/compiler-cli/src/ngtsc/imports", + "//packages/compiler-cli/src/ngtsc/incremental/semantic_graph", "//packages/compiler-cli/src/ngtsc/logging/testing", "//packages/compiler-cli/src/ngtsc/partial_evaluator", "//packages/compiler-cli/src/ngtsc/reflection", + "//packages/compiler-cli/src/ngtsc/sourcemaps", "//packages/compiler-cli/src/ngtsc/testing", "//packages/compiler-cli/src/ngtsc/transform", "//packages/compiler-cli/src/ngtsc/translator", @@ -101,6 +103,11 @@ jasmine_node_test( # Disabled in AOT mode because we want ngcc to compile non-AOT Angular packages. "no-ivy-aot", ], + templated_args = [ + # TODO(josephperrott): update dependency usages to no longer need bazel patch module resolver + # See: https://github.com/bazelbuild/rules_nodejs/wiki#--bazel_patch_module_resolver-now-defaults-to-false-2324 + "--bazel_patch_module_resolver", + ], deps = [ ":integration_lib", "@npm//canonical-path", 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 42e10f387f..f390fa19ab 100644 --- a/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts @@ -10,6 +10,7 @@ import * as ts from 'typescript'; import {FatalDiagnosticError, makeDiagnostic} from '../../../src/ngtsc/diagnostics'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; +import {SemanticSymbol} from '../../../src/ngtsc/incremental/semantic_graph'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; import {ClassDeclaration, DeclarationNode, Decorator} from '../../../src/ngtsc/reflection'; import {loadFakeCore, loadTestFiles} from '../../../src/ngtsc/testing'; @@ -21,8 +22,9 @@ import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; import {Migration, MigrationHost} from '../../src/migrations/migration'; import {getRootFiles, makeTestEntryPointBundle} from '../helpers/utils'; -type DecoratorHandlerWithResolve = DecoratorHandler&{ - resolve: NonNullable['resolve']>; +type DecoratorHandlerWithResolve = + DecoratorHandler&{ + resolve: NonNullable['resolve']>; }; runInEachFileSystem(() => { @@ -46,6 +48,7 @@ runInEachFileSystem(() => { const handler = jasmine.createSpyObj('TestDecoratorHandler', [ 'detect', 'analyze', + 'symbol', 'register', 'resolve', 'compileFull', @@ -79,7 +82,7 @@ runInEachFileSystem(() => { handler.analyze.and.callFake((decl: DeclarationNode, dec: Decorator) => { logs.push(`analyze: ${(decl as any).name.text}@${dec.name}`); return { - analysis: {decoratorName: dec.name}, + analysis: !options.analyzeError ? {decoratorName: dec.name} : undefined, diagnostics: options.analyzeError ? [makeDiagnostic(9999, decl, 'analyze diagnostic')] : undefined }; @@ -407,7 +410,7 @@ runInEachFileSystem(() => { `, }, ], - {analyzeError: true, resolveError: true}); + {analyzeError: true, resolveError: false}); analyzer.analyzeProgram(); expect(diagnosticLogs.length).toEqual(1); expect(diagnosticLogs[0]).toEqual(jasmine.objectContaining({code: -999999})); @@ -442,7 +445,7 @@ runInEachFileSystem(() => { describe('declaration files', () => { it('should not run decorator handlers against declaration files', () => { - class FakeDecoratorHandler implements DecoratorHandler<{}|null, unknown, unknown> { + class FakeDecoratorHandler implements DecoratorHandler<{}|null, unknown, null, unknown> { name = 'FakeDecoratorHandler'; precedence = HandlerPrecedence.PRIMARY; @@ -452,6 +455,9 @@ runInEachFileSystem(() => { analyze(): AnalysisOutput { throw new Error('analyze should not have been called'); } + symbol(): null { + throw new Error('symbol should not have been called'); + } compileFull(): CompileResult { throw new Error('compile should not have been called'); } 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 8bc7203e11..2e9cd992c2 100644 --- a/packages/compiler-cli/ngcc/test/analysis/migration_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/migration_host_spec.ts @@ -11,16 +11,18 @@ import * as ts from 'typescript'; import {makeDiagnostic} from '../../../src/ngtsc/diagnostics'; import {absoluteFrom} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {SemanticSymbol} from '../../../src/ngtsc/incremental/semantic_graph'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; import {ClassDeclaration, Decorator, isNamedClassDeclaration} from '../../../src/ngtsc/reflection'; import {getDeclaration, loadTestFiles} from '../../../src/ngtsc/testing'; -import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence, TraitState} from '../../../src/ngtsc/transform'; +import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../../src/ngtsc/transform'; import {DefaultMigrationHost} from '../../src/analysis/migration_host'; import {NgccTraitCompiler} from '../../src/analysis/ngcc_trait_compiler'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; import {createComponentDecorator} from '../../src/migrations/utils'; import {EntryPointBundle} from '../../src/packages/entry_point_bundle'; import {makeTestEntryPointBundle} from '../helpers/utils'; +import {getTraitDiagnostics} from '../host/util'; runInEachFileSystem(() => { describe('DefaultMigrationHost', () => { @@ -43,7 +45,8 @@ runInEachFileSystem(() => { }); function createMigrationHost({entryPoint, handlers}: { - entryPoint: EntryPointBundle; handlers: DecoratorHandler[] + entryPoint: EntryPointBundle; + handlers: DecoratorHandler[] }) { const reflectionHost = new Esm2015ReflectionHost(new MockLogger(), false, entryPoint.src); const compiler = new NgccTraitCompiler(handlers, reflectionHost); @@ -78,12 +81,13 @@ runInEachFileSystem(() => { const record = compiler.recordFor(mockClazz)!; const migratedTrait = record.traits[0]; - if (migratedTrait.state !== TraitState.ERRORED) { + const diagnostics = getTraitDiagnostics(migratedTrait); + if (diagnostics === null) { return fail('Expected migrated class trait to be in an error state'); } - expect(migratedTrait.diagnostics.length).toBe(1); - expect(ts.flattenDiagnosticMessageText(migratedTrait.diagnostics[0].messageText, '\n')) + expect(diagnostics.length).toBe(1); + expect(ts.flattenDiagnosticMessageText(diagnostics[0].messageText, '\n')) .toEqual( `test diagnostic\n` + ` Occurs for @Component decorator inserted by an automatic migration\n` + @@ -188,7 +192,7 @@ runInEachFileSystem(() => { }); }); -class DetectDecoratorHandler implements DecoratorHandler { +class DetectDecoratorHandler implements DecoratorHandler { readonly name = DetectDecoratorHandler.name; constructor(private decorator: string, readonly precedence: HandlerPrecedence) {} @@ -208,12 +212,16 @@ class DetectDecoratorHandler implements DecoratorHandler): null { + return null; + } + compileFull(node: ClassDeclaration): CompileResult|CompileResult[] { return []; } } -class DiagnosticProducingHandler implements DecoratorHandler { +class DiagnosticProducingHandler implements DecoratorHandler { readonly name = DiagnosticProducingHandler.name; readonly precedence = HandlerPrecedence.PRIMARY; @@ -226,6 +234,10 @@ class DiagnosticProducingHandler implements DecoratorHandler): null { + return null; + } + compileFull(node: ClassDeclaration): CompileResult|CompileResult[] { return []; } 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 e48d2f2f33..2cd0ddd0c6 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 @@ -9,6 +9,7 @@ import {ErrorCode, makeDiagnostic, ngErrorCode} from '../../../src/ngtsc/diagnostics'; import {absoluteFrom} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {SemanticSymbol} from '../../../src/ngtsc/incremental/semantic_graph'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; import {ClassDeclaration, Decorator, isNamedClassDeclaration} from '../../../src/ngtsc/reflection'; import {getDeclaration, loadTestFiles} from '../../../src/ngtsc/testing'; @@ -18,6 +19,7 @@ import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; import {createComponentDecorator} from '../../src/migrations/utils'; import {EntryPointBundle} from '../../src/packages/entry_point_bundle'; import {makeTestEntryPointBundle} from '../helpers/utils'; +import {getTraitDiagnostics} from '../host/util'; runInEachFileSystem(() => { describe('NgccTraitCompiler', () => { @@ -38,7 +40,8 @@ runInEachFileSystem(() => { }); function createCompiler({entryPoint, handlers}: { - entryPoint: EntryPointBundle; handlers: DecoratorHandler[] + entryPoint: EntryPointBundle; + handlers: DecoratorHandler[] }) { const reflectionHost = new Esm2015ReflectionHost(new MockLogger(), false, entryPoint.src); return new NgccTraitCompiler(handlers, reflectionHost); @@ -233,12 +236,13 @@ runInEachFileSystem(() => { const record = compiler.recordFor(mockClazz)!; const migratedTrait = record.traits[0]; - if (migratedTrait.state !== TraitState.ERRORED) { + const diagnostics = getTraitDiagnostics(migratedTrait); + if (diagnostics === null) { return fail('Expected migrated class trait to be in an error state'); } - expect(migratedTrait.diagnostics.length).toBe(1); - expect(migratedTrait.diagnostics[0].messageText).toEqual(`test diagnostic`); + expect(diagnostics.length).toBe(1); + expect(diagnostics[0].messageText).toEqual(`test diagnostic`); }); }); @@ -293,7 +297,7 @@ runInEachFileSystem(() => { }); }); -class TestHandler implements DecoratorHandler { +class TestHandler implements DecoratorHandler { constructor(readonly name: string, protected log: string[]) {} precedence = HandlerPrecedence.PRIMARY; @@ -308,6 +312,10 @@ class TestHandler implements DecoratorHandler { return {}; } + symbol(node: ClassDeclaration, analysis: Readonly): null { + return null; + } + compileFull(node: ClassDeclaration): CompileResult|CompileResult[] { this.log.push(this.name + ':compile:' + node.name.text); return []; 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 2ec0a1b7b7..2084962b95 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,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 {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, relative} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; import {loadTestFiles} from '../../../src/ngtsc/testing'; @@ -114,7 +114,7 @@ runInEachFileSystem(() => { loadTestFiles(createPackage(basePath, 'some-package')); spyOn(config, 'getPackageConfig') .and.returnValue( - new ProcessedNgccPackageConfig(_Abs('/project/node_modules/some-package'), { + new ProcessedNgccPackageConfig(fs, _Abs('/project/node_modules/some-package'), { entryPoints: { '.': {ignore: true}, }, @@ -137,7 +137,7 @@ runInEachFileSystem(() => { ]); spyOn(config, 'getPackageConfig') .and.returnValue( - new ProcessedNgccPackageConfig(_Abs('/project/node_modules/some-package'), { + new ProcessedNgccPackageConfig(fs, _Abs('/project/node_modules/some-package'), { entryPoints: { './sub-entry-point-1': {ignore: true}, }, @@ -210,7 +210,7 @@ runInEachFileSystem(() => { // Modify the manifest to prove that we use it to find the entry-points const manifestPath = _Abs('/sub_entry_points/node_modules/__ngcc_entry_points__.json'); - const manifestFile: EntryPointManifestFile = JSON.parse(fs.readFile(manifestPath)); + const manifestFile = JSON.parse(fs.readFile(manifestPath)) as EntryPointManifestFile; manifestFile.entryPointPaths.pop(); fs.writeFile(manifestPath, JSON.stringify(manifestFile)); @@ -457,7 +457,7 @@ runInEachFileSystem(() => { function dumpEntryPointPaths( basePath: AbsoluteFsPath, entryPoints: EntryPoint[]): [string, string][] { return entryPoints.map( - x => [relative(basePath, x.packagePath), relative(basePath, x.path)]); + x => [fs.relative(basePath, x.packagePath), fs.relative(basePath, x.path)]); } }); }); diff --git a/packages/compiler-cli/ngcc/test/entry_point_finder/program_based_entry_point_finder_spec.ts b/packages/compiler-cli/ngcc/test/entry_point_finder/program_based_entry_point_finder_spec.ts index 5f2eccc366..b38af49eb8 100644 --- a/packages/compiler-cli/ngcc/test/entry_point_finder/program_based_entry_point_finder_spec.ts +++ b/packages/compiler-cli/ngcc/test/entry_point_finder/program_based_entry_point_finder_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 {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, relative} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; import {loadTestFiles} from '../../../src/ngtsc/testing'; @@ -231,7 +231,7 @@ runInEachFileSystem(() => { function dumpEntryPointPaths( basePath: AbsoluteFsPath, entryPoints: EntryPoint[]): [string, string][] { return entryPoints.map( - x => [relative(basePath, x.packagePath), relative(basePath, x.path)]); + x => [fs.relative(basePath, x.packagePath), fs.relative(basePath, x.path)]); } }); }); 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 e2fce59297..f4c231e44b 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,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 {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, relative} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; import {loadTestFiles} from '../../../src/ngtsc/testing'; @@ -16,7 +16,7 @@ import {ModuleResolver} from '../../src/dependencies/module_resolver'; import {TargetedEntryPointFinder} from '../../src/entry_point_finder/targeted_entry_point_finder'; import {NGCC_VERSION} from '../../src/packages/build_marker'; import {NgccConfiguration, ProcessedNgccPackageConfig} from '../../src/packages/configuration'; -import {EntryPoint} from '../../src/packages/entry_point'; +import {EntryPoint, EntryPointPackageJson} from '../../src/packages/entry_point'; import {PathMappings} from '../../src/path_mappings'; runInEachFileSystem(() => { @@ -118,7 +118,7 @@ runInEachFileSystem(() => { loadTestFiles(createPackage(basePath, 'some-package')); spyOn(config, 'getPackageConfig') .and.returnValue( - new ProcessedNgccPackageConfig(_Abs('/project/node_modules/some-package'), { + new ProcessedNgccPackageConfig(fs, _Abs('/project/node_modules/some-package'), { entryPoints: { '.': {ignore: true}, }, @@ -355,6 +355,56 @@ runInEachFileSystem(() => { ]); }); + it('should correctly compute the package path for a target whose name contains the string of another package', + () => { + // Create the "my-lib" package - it doesn't need to be a real entry-point + const myLibPath = _Abs('/project/dist/my-lib'); + loadTestFiles([{ + name: fs.resolve(myLibPath, 'package.json'), + contents: JSON.stringify({name: 'my-lib'}) + }]); + + // Create the "my-lib-other" Angular entry-point + const myLibOtherPath = _Abs('/project/dist/my-lib-other'); + loadTestFiles([ + { + name: fs.resolve(myLibOtherPath, 'package.json'), + contents: JSON.stringify({ + name: `my-lib-other`, + typings: `./my-lib-other.d.ts`, + fesm2015: `./fesm2015/my-lib-other.js`, + esm5: `./esm5/my-lib-other.js`, + main: `./common/my-lib-other.js`, + }) + }, + {name: fs.resolve(myLibOtherPath, 'my-lib-other.metadata.json'), contents: 'metadata'}, + {name: fs.resolve(myLibOtherPath, 'my-lib-other.d.ts'), contents: 'typings'}, + {name: fs.resolve(myLibOtherPath, 'fesm2015/my-lib-other.js'), contents: ''}, + {name: fs.resolve(myLibOtherPath, 'esm5/my-lib-other.js'), contents: ''}, + {name: fs.resolve(myLibOtherPath, 'commonjs/my-lib-other.js'), contents: ''}, + ]); + + const basePath = _Abs('/project/node_modules'); + const pathMappings: PathMappings = { + baseUrl: '/project', + paths: { + 'lib1': ['dist/my-lib'], + 'lib2': ['dist/my-lib-other'], + } + }; + + const srcHost = new EsmDependencyHost(fs, new ModuleResolver(fs, pathMappings)); + const dtsHost = new DtsDependencyHost(fs, pathMappings); + resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost); + const finder = new TargetedEntryPointFinder( + fs, config, logger, resolver, basePath, pathMappings, myLibOtherPath); + const {entryPoints} = finder.findEntryPoints(); + + expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([ + ['../dist/my-lib-other', '../dist/my-lib-other'], + ]); + }); + it('should handle pathMappings that map to files or non-existent directories', () => { const basePath = _Abs('/path_mapped/node_modules'); const targetPath = _Abs('/path_mapped/node_modules/test'); @@ -383,7 +433,7 @@ runInEachFileSystem(() => { function dumpEntryPointPaths( basePath: AbsoluteFsPath, entryPoints: EntryPoint[]): [string, string][] { return entryPoints.map( - x => [relative(basePath, x.packagePath), relative(basePath, x.path)]); + x => [fs.relative(basePath, x.packagePath), fs.relative(basePath, x.path)]); } }); @@ -419,7 +469,7 @@ runInEachFileSystem(() => { loadTestFiles(createPackage(basePath, 'some-package')); spyOn(config, 'getPackageConfig') .and.returnValue( - new ProcessedNgccPackageConfig(_Abs('/project/node_modules/some-package'), { + new ProcessedNgccPackageConfig(fs, _Abs('/project/node_modules/some-package'), { entryPoints: { '.': {ignore: true}, }, @@ -505,7 +555,7 @@ runInEachFileSystem(() => { // Add a build marker to the package.json const packageJsonPath = _Abs(`${targetPath}/package.json`); - const packageJson = JSON.parse(fs.readFile(packageJsonPath)); + const packageJson = JSON.parse(fs.readFile(packageJsonPath)) as EntryPointPackageJson; packageJson.__processed_by_ivy_ngcc__ = { esm5: NGCC_VERSION, }; @@ -529,7 +579,7 @@ runInEachFileSystem(() => { // Add build markers to the package.json const packageJsonPath = _Abs(`${targetPath}/package.json`); - const packageJson = JSON.parse(fs.readFile(packageJsonPath)); + const packageJson = JSON.parse(fs.readFile(packageJsonPath)) as EntryPointPackageJson; packageJson.__processed_by_ivy_ngcc__ = { fesm2015: NGCC_VERSION, esm5: NGCC_VERSION, @@ -575,7 +625,7 @@ runInEachFileSystem(() => { // Add build markers to the package.json const packageJsonPath = _Abs(`${targetPath}/package.json`); - const packageJson = JSON.parse(fs.readFile(packageJsonPath)); + const packageJson = JSON.parse(fs.readFile(packageJsonPath)) as EntryPointPackageJson; packageJson.__processed_by_ivy_ngcc__ = { esm5: NGCC_VERSION, }; @@ -601,7 +651,7 @@ runInEachFileSystem(() => { // Add build markers to the package.json const packageJsonPath = _Abs(`${targetPath}/package.json`); - const packageJson = JSON.parse(fs.readFile(packageJsonPath)); + const packageJson = JSON.parse(fs.readFile(packageJsonPath)) as EntryPointPackageJson; packageJson.__processed_by_ivy_ngcc__ = { fesm2015: NGCC_VERSION, }; 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 index 536e23559b..8d365844c5 100644 --- a/packages/compiler-cli/ngcc/test/entry_point_finder/utils_spec.ts +++ b/packages/compiler-cli/ngcc/test/entry_point_finder/utils_spec.ts @@ -48,6 +48,24 @@ runInEachFileSystem(() => { ]); }); + it('should find base-paths that start with a wildcard prefix', () => { + const projectDirectory = _('/path/to/project'); + const fs = getFileSystem(); + fs.ensureDir(fs.resolve(projectDirectory, 'dist')); + fs.ensureDir(fs.resolve(projectDirectory, 'dist-a')); + fs.ensureDir(fs.resolve(projectDirectory, 'dist-b')); + + const sourceDirectory = _('/path/to/project/node_modules'); + const pathMappings = {baseUrl: projectDirectory, paths: {'@dist*': ['dist*']}}; + const basePaths = getBasePaths(logger, sourceDirectory, pathMappings); + expect(basePaths).toEqual([ + sourceDirectory, + fs.resolve(projectDirectory, 'dist'), + fs.resolve(projectDirectory, 'dist-a'), + fs.resolve(projectDirectory, 'dist-b'), + ]); + }); + it('should not be confused by folders that have the same starting string', () => { const projectDirectory = _('/path/to/project'); const fs = getFileSystem(); 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 990d9c465d..359beb7ab5 100644 --- a/packages/compiler-cli/ngcc/test/execution/cluster/worker_spec.ts +++ b/packages/compiler-cli/ngcc/test/execution/cluster/worker_spec.ts @@ -15,7 +15,7 @@ import {AbsoluteFsPath} from '../../../../src/ngtsc/file_system'; import {MockLogger} from '../../../../src/ngtsc/logging/testing'; import {CreateCompileFn} from '../../../src/execution/api'; import {startWorker} from '../../../src/execution/cluster/worker'; -import {Task, TaskCompletedCallback, TaskProcessingOutcome} from '../../../src/execution/tasks/api'; +import {DtsProcessing, Task, TaskCompletedCallback, TaskProcessingOutcome} from '../../../src/execution/tasks/api'; import {FileToWrite} from '../../../src/rendering/utils'; import {mockProperty, spyProperty} from '../../helpers/spy_utils'; @@ -124,7 +124,7 @@ describe('startWorker()', () => { const mockTask = { entryPoint: {name: 'foo'}, formatProperty: 'es2015', - processDts: true, + processDts: DtsProcessing.Yes, } as unknown as Task; startWorker(mockLogger, createCompileFnSpy); @@ -134,7 +134,7 @@ describe('startWorker()', () => { expect(processSendSpy).not.toHaveBeenCalled(); expect(mockLogger.logs.debug[0]).toEqual([ - '[Worker #42] Processing task: {entryPoint: foo, formatProperty: es2015, processDts: true}', + '[Worker #42] Processing task: {entryPoint: foo, formatProperty: es2015, processDts: Yes}', ]); }); @@ -142,7 +142,7 @@ describe('startWorker()', () => { const mockTask = { entryPoint: {name: 'foo'}, formatProperty: 'es2015', - processDts: true, + processDts: DtsProcessing.Yes, } as unknown as Task; let err: string|Error; @@ -178,7 +178,7 @@ describe('startWorker()', () => { const mockTask = { entryPoint: {name: 'foo'}, formatProperty: 'es2015', - processDts: true, + processDts: DtsProcessing.Yes, } as unknown as Task; const noMemError = Object.assign(new Error('ENOMEM: not enough memory'), {code: 'ENOMEM'}); diff --git a/packages/compiler-cli/ngcc/test/execution/helpers.ts b/packages/compiler-cli/ngcc/test/execution/helpers.ts index 30ecd02b4c..f01537a6a0 100644 --- a/packages/compiler-cli/ngcc/test/execution/helpers.ts +++ b/packages/compiler-cli/ngcc/test/execution/helpers.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {DepGraph} from 'dependency-graph'; -import {PartiallyOrderedTasks, Task} from '../../src/execution/tasks/api'; +import {DtsProcessing, PartiallyOrderedTasks, Task} from '../../src/execution/tasks/api'; import {EntryPoint} from '../../src/packages/entry_point'; /** @@ -52,7 +52,8 @@ 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); + const processDts = tIdx === 0 ? DtsProcessing.Yes : DtsProcessing.No; + tasks.push({entryPoint, formatProperty: `prop-${tIdx}`, processDts} as Task); } } 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 8be126b4ca..30f466602b 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 @@ -7,7 +7,7 @@ */ import {MockLogger} from '../../../../../src/ngtsc/logging/testing'; -import {PartiallyOrderedTasks, TaskQueue} from '../../../../src/execution/tasks/api'; +import {DtsProcessing, PartiallyOrderedTasks, TaskQueue} from '../../../../src/execution/tasks/api'; import {ParallelTaskQueue} from '../../../../src/execution/tasks/queues/parallel_task_queue'; import {computeTaskDependencies} from '../../../../src/execution/tasks/utils'; import {createTasksAndGraph} from '../../helpers'; @@ -141,9 +141,9 @@ describe('ParallelTaskQueue', () => { // Verify that the first two tasks are for the first entry-point. expect(tasks[0].entryPoint.name).toBe('entry-point-0'); - expect(tasks[0].processDts).toBe(true); + expect(tasks[0].processDts).toBe(DtsProcessing.Yes); expect(tasks[1].entryPoint.name).toBe('entry-point-0'); - expect(tasks[1].processDts).toBe(false); + expect(tasks[1].processDts).toBe(DtsProcessing.No); // Verify that the last two tasks are for the second entry-point. expect(tasks[2].entryPoint.name).toBe('entry-point-1'); @@ -282,7 +282,7 @@ describe('ParallelTaskQueue', () => { expect(() => queue.markAsCompleted(tasks[2])) .toThrowError( `Trying to mark task that was not in progress as completed: ` + - `{entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}`); + `{entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes}`); }); it('should remove the completed task from the lists of blocking tasks (so other tasks can be unblocked)', @@ -340,13 +340,13 @@ describe('ParallelTaskQueue', () => { expect(() => queue.markAsUnprocessed(tasks[0])) .toThrowError( `Trying to mark task that was not in progress as unprocessed: ` + - `{entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}`); + `{entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}`); // Try with a task that is not yet started. expect(() => queue.markAsUnprocessed(tasks[2])) .toThrowError( `Trying to mark task that was not in progress as unprocessed: ` + - `{entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}`); + `{entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes}`); }); it('should not remove the unprocessed task from the lists of blocking tasks', () => { @@ -399,23 +399,23 @@ describe('ParallelTaskQueue', () => { expect(queue.toString()) .toContain( ' Unprocessed tasks (3): \n' + - ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}\n'); + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes}\n'); const task1 = queue.getNextTask()!; expect(queue.toString()) .toContain( ' Unprocessed tasks (2): \n' + - ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}\n'); + ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes}\n'); queue.markAsCompleted(task1); const task2 = queue.getNextTask()!; expect(queue.toString()) .toContain( ' Unprocessed tasks (1): \n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}\n'); + ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes}\n'); queue.markAsCompleted(task2); processNextTask(queue); @@ -430,14 +430,14 @@ describe('ParallelTaskQueue', () => { expect(queue.toString()) .toContain( ' In-progress tasks (1): \n' + - ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}\n'); + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}\n'); queue.markAsCompleted(task1); const task2 = queue.getNextTask()!; expect(queue.toString()) .toContain( ' In-progress tasks (1): \n' + - ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: true}\n'); + ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: Yes}\n'); queue.markAsCompleted(task2); processNextTask(queue); @@ -463,29 +463,29 @@ describe('ParallelTaskQueue', () => { .toContain( ' Blocked tasks (6): \n' + // #0.1 blocked by its typings #0.0 - ' - {entryPoint: entry-point-0, formatProperty: prop-1, processDts: false} (1): \n' + - ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}\n' + + ' - {entryPoint: entry-point-0, formatProperty: prop-1, processDts: No} (1): \n' + + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}\n' + // #1.0 blocked by #0.0 - ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: true} (1): \n' + - ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}\n' + + ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: Yes} (1): \n' + + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}\n' + // #1.1 blocked by #0.0 and its typings #1.0 - ' - {entryPoint: entry-point-1, formatProperty: prop-1, processDts: false} (2): \n' + - ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: true}\n' + + ' - {entryPoint: entry-point-1, formatProperty: prop-1, processDts: No} (2): \n' + + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: Yes}\n' + // #3.0 blocked by #0.0 (transitively), #1.0 and #2.0. - ' - {entryPoint: entry-point-3, formatProperty: prop-0, processDts: true} (3): \n' + - ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}\n' + + ' - {entryPoint: entry-point-3, formatProperty: prop-0, processDts: Yes} (3): \n' + + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes}\n' + // #3.1 blocked by #0.0 (transitively), #1.0 and #2.0, and its typings #3.0 - ' - {entryPoint: entry-point-3, formatProperty: prop-1, processDts: false} (4): \n' + - ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-3, formatProperty: prop-0, processDts: true}\n' + + ' - {entryPoint: entry-point-3, formatProperty: prop-1, processDts: No} (4): \n' + + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-3, formatProperty: prop-0, processDts: Yes}\n' + // #2.1 blocked by its typings #2.0 - ' - {entryPoint: entry-point-2, formatProperty: prop-1, processDts: false} (1): \n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}'); + ' - {entryPoint: entry-point-2, formatProperty: prop-1, processDts: No} (1): \n' + + ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes}'); expect(processNextTask(queue)).toBe(tasks[0]); // Process #0.0. expect(processNextTask(queue)).toBe(tasks[2]); // Process #1.0. @@ -493,23 +493,23 @@ describe('ParallelTaskQueue', () => { .toContain( ' Blocked tasks (3): \n' + // #3.0 blocked by #2.0. - ' - {entryPoint: entry-point-3, formatProperty: prop-0, processDts: true} (1): \n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}\n' + + ' - {entryPoint: entry-point-3, formatProperty: prop-0, processDts: Yes} (1): \n' + + ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes}\n' + // #3.1 blocked by #2.0 and its typings #3.0 - ' - {entryPoint: entry-point-3, formatProperty: prop-1, processDts: false} (2): \n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-3, formatProperty: prop-0, processDts: true}\n' + + ' - {entryPoint: entry-point-3, formatProperty: prop-1, processDts: No} (2): \n' + + ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-3, formatProperty: prop-0, processDts: Yes}\n' + // #2.1 blocked by its typings #2.0 - ' - {entryPoint: entry-point-2, formatProperty: prop-1, processDts: false} (1): \n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}'); + ' - {entryPoint: entry-point-2, formatProperty: prop-1, processDts: No} (1): \n' + + ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes}'); expect(processNextTask(queue)).toBe(tasks[4]); // Process #2.0. expect(queue.toString()) .toContain( ' Blocked tasks (1): \n' + // #3.1 blocked by its typings #3.0 - ' - {entryPoint: entry-point-3, formatProperty: prop-1, processDts: false} (1): \n' + - ' - {entryPoint: entry-point-3, formatProperty: prop-0, processDts: true}'); + ' - {entryPoint: entry-point-3, formatProperty: prop-1, processDts: No} (1): \n' + + ' - {entryPoint: entry-point-3, formatProperty: prop-0, processDts: Yes}'); expect(processNextTask(queue)).toBe(tasks[6]); // Process #3.0. expect(queue.toString()).toContain(' Blocked tasks (0): '); }); @@ -532,13 +532,13 @@ describe('ParallelTaskQueue', () => { 'ParallelTaskQueue\n' + ' All tasks completed: false\n' + ' Unprocessed tasks (3): \n' + - ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}\n' + + ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes}\n' + ' In-progress tasks (0): \n' + ' Blocked tasks (1): \n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true} (1): \n' + - ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: true}'); + ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes} (1): \n' + + ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: Yes}'); // Start processing tasks #1 and #0 (#2 is still blocked on #1). expect(queue2.getNextTask()).toBe(tasks2[1]); @@ -548,13 +548,13 @@ describe('ParallelTaskQueue', () => { 'ParallelTaskQueue\n' + ' All tasks completed: false\n' + ' Unprocessed tasks (1): \n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}\n' + + ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes}\n' + ' In-progress tasks (2): \n' + - ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}\n' + + ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}\n' + ' Blocked tasks (1): \n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true} (1): \n' + - ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: true}'); + ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes} (1): \n' + + ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: Yes}'); // Complete task #1 nd start processing #2 (which is not unblocked). queue2.markAsCompleted(tasks2[1]); @@ -565,8 +565,8 @@ describe('ParallelTaskQueue', () => { ' All tasks completed: false\n' + ' Unprocessed tasks (0): \n' + ' In-progress tasks (2): \n' + - ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}\n' + + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: Yes}\n' + ' Blocked tasks (0): '); // Complete tasks #2 and #0. All tasks are now completed. 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 327efd9fbb..85b8b63031 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 @@ -8,7 +8,7 @@ import {DepGraph} from 'dependency-graph'; import {MockLogger} from '../../../../../src/ngtsc/logging/testing'; -import {PartiallyOrderedTasks, Task, TaskQueue} from '../../../../src/execution/tasks/api'; +import {DtsProcessing, PartiallyOrderedTasks, Task, TaskQueue} from '../../../../src/execution/tasks/api'; import {SerialTaskQueue} from '../../../../src/execution/tasks/queues/serial_task_queue'; import {computeTaskDependencies} from '../../../../src/execution/tasks/utils'; import {EntryPoint} from '../../../../src/packages/entry_point'; @@ -32,8 +32,8 @@ describe('SerialTaskQueue', () => { for (let i = 0; i < taskCount; i++) { 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); + const processDts = i % 2 === 0 ? DtsProcessing.Yes : DtsProcessing.No; + tasks.push({entryPoint: entryPoint, formatProperty: `prop-${i}`, processDts} as Task); graph.addNode(entryPoint.path); } const dependencies = computeTaskDependencies(tasks, graph); @@ -131,7 +131,7 @@ describe('SerialTaskQueue', () => { expect(() => queue.getNextTask()) .toThrowError( `Trying to get next task, while there is already a task in progress: ` + - `{entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}`); + `{entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}`); }); }); @@ -153,7 +153,7 @@ describe('SerialTaskQueue', () => { expect(() => queue.markAsCompleted(tasks[2])) .toThrowError( `Trying to mark task that was not in progress as completed: ` + - `{entryPoint: entry-point-2, formatProperty: prop-2, processDts: true}`); + `{entryPoint: entry-point-2, formatProperty: prop-2, processDts: Yes}`); }); }); @@ -177,13 +177,13 @@ describe('SerialTaskQueue', () => { expect(() => queue.markAsUnprocessed(tasks[0])) .toThrowError( `Trying to mark task that was not in progress as unprocessed: ` + - `{entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}`); + `{entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}`); // Try with a task that is not yet started. expect(() => queue.markAsUnprocessed(tasks[2])) .toThrowError( `Trying to mark task that was not in progress as unprocessed: ` + - `{entryPoint: entry-point-2, formatProperty: prop-2, processDts: true}`); + `{entryPoint: entry-point-2, formatProperty: prop-2, processDts: Yes}`); }); }); @@ -215,23 +215,23 @@ describe('SerialTaskQueue', () => { expect(queue.toString()) .toContain( ' Unprocessed tasks (3): \n' + - ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-1, formatProperty: prop-1, processDts: false}\n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-2, processDts: true}\n'); + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-1, formatProperty: prop-1, processDts: No}\n' + + ' - {entryPoint: entry-point-2, formatProperty: prop-2, processDts: Yes}\n'); const task1 = queue.getNextTask()!; expect(queue.toString()) .toContain( ' Unprocessed tasks (2): \n' + - ' - {entryPoint: entry-point-1, formatProperty: prop-1, processDts: false}\n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-2, processDts: true}\n'); + ' - {entryPoint: entry-point-1, formatProperty: prop-1, processDts: No}\n' + + ' - {entryPoint: entry-point-2, formatProperty: prop-2, processDts: Yes}\n'); queue.markAsCompleted(task1); const task2 = queue.getNextTask()!; expect(queue.toString()) .toContain( ' Unprocessed tasks (1): \n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-2, processDts: true}\n'); + ' - {entryPoint: entry-point-2, formatProperty: prop-2, processDts: Yes}\n'); queue.markAsCompleted(task2); processNextTask(queue); @@ -246,14 +246,14 @@ describe('SerialTaskQueue', () => { expect(queue.toString()) .toContain( ' In-progress tasks (1): \n' + - ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}'); + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}'); queue.markAsCompleted(task1); const task2 = queue.getNextTask()!; expect(queue.toString()) .toContain( ' In-progress tasks (1): \n' + - ' - {entryPoint: entry-point-1, formatProperty: prop-1, processDts: false}'); + ' - {entryPoint: entry-point-1, formatProperty: prop-1, processDts: No}'); queue.markAsCompleted(task2); processNextTask(queue); @@ -275,9 +275,9 @@ describe('SerialTaskQueue', () => { 'SerialTaskQueue\n' + ' All tasks completed: false\n' + ' Unprocessed tasks (3): \n' + - ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-1, formatProperty: prop-1, processDts: false}\n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-2, processDts: true}\n' + + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-1, formatProperty: prop-1, processDts: No}\n' + + ' - {entryPoint: entry-point-2, formatProperty: prop-2, processDts: Yes}\n' + ' In-progress tasks (0): '); processNextTask(queue2); @@ -287,9 +287,9 @@ describe('SerialTaskQueue', () => { 'SerialTaskQueue\n' + ' All tasks completed: false\n' + ' Unprocessed tasks (1): \n' + - ' - {entryPoint: entry-point-2, formatProperty: prop-2, processDts: true}\n' + + ' - {entryPoint: entry-point-2, formatProperty: prop-2, processDts: Yes}\n' + ' In-progress tasks (1): \n' + - ' - {entryPoint: entry-point-1, formatProperty: prop-1, processDts: false}'); + ' - {entryPoint: entry-point-1, formatProperty: prop-1, processDts: No}'); queue2.markAsCompleted(task); processNextTask(queue2); diff --git a/packages/compiler-cli/ngcc/test/execution/utils_spec.ts b/packages/compiler-cli/ngcc/test/execution/utils_spec.ts index 76d92e25f0..a50684e663 100644 --- a/packages/compiler-cli/ngcc/test/execution/utils_spec.ts +++ b/packages/compiler-cli/ngcc/test/execution/utils_spec.ts @@ -5,6 +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 {DtsProcessing} from '../../src/execution/tasks/api'; import {computeTaskDependencies, sortTasksByPriority} from '../../src/execution/tasks/utils'; import {createTasksAndGraph} from './helpers'; @@ -16,14 +17,14 @@ describe('execution utils', () => { 0: [], // Entry-point #0 does not depend on anything. 1: [0], // Entry-point #1 depends on #0. }); - tasks[1].processDts = true; // Tweak task #1 to also generate typings. + tasks[1].processDts = DtsProcessing.Yes; // Tweak task #1 to also generate typings. expect(() => computeTaskDependencies(tasks, graph)) .toThrowError( 'Invariant violated: Multiple tasks are assigned generating typings for ' + '\'/path/to/entry/point/0\':\n' + - ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}\n' + - ' - {entryPoint: entry-point-0, formatProperty: prop-1, processDts: true}'); + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: Yes}\n' + + ' - {entryPoint: entry-point-0, formatProperty: prop-1, processDts: Yes}'); }); it('should add non-typings tasks to the dependents of typings tasks', () => { 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 51f016e6c6..ca3b483f76 100644 --- a/packages/compiler-cli/ngcc/test/helpers/mock_lock_file.ts +++ b/packages/compiler-cli/ngcc/test/helpers/mock_lock_file.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 {FileSystem} from '../../../src/ngtsc/file_system'; +import {PathManipulation} from '../../../src/ngtsc/file_system'; import {LockFile} from '../../src/locking/lock_file'; /** @@ -13,7 +13,7 @@ import {LockFile} from '../../src/locking/lock_file'; */ export class MockLockFile implements LockFile { constructor( - fs: FileSystem, private log: string[] = [], public path = fs.resolve('/lockfile'), + fs: PathManipulation, private log: string[] = [], public path = fs.resolve('/lockfile'), private pid = '1234') {} write() { this.log.push('write()'); diff --git a/packages/compiler-cli/ngcc/test/helpers/utils.ts b/packages/compiler-cli/ngcc/test/helpers/utils.ts index 382758f3c3..6ddd8a0ef9 100644 --- a/packages/compiler-cli/ngcc/test/helpers/utils.ts +++ b/packages/compiler-cli/ngcc/test/helpers/utils.ts @@ -9,6 +9,7 @@ import * as ts from 'typescript'; import {absoluteFrom, AbsoluteFsPath, getFileSystem, NgtscCompilerHost} from '../../../src/ngtsc/file_system'; import {TestFile} from '../../../src/ngtsc/file_system/testing'; +import {DtsProcessing} from '../../src/execution/tasks/api'; import {BundleProgram, makeBundleProgram} from '../../src/packages/bundle_program'; import {NgccEntryPointConfig} from '../../src/packages/configuration'; import {EntryPoint, EntryPointFormat} from '../../src/packages/entry_point'; @@ -55,6 +56,7 @@ export function makeTestEntryPointBundle( rootDirs: [absoluteFrom('/')], src, dts, + dtsProcessing: dtsRootNames ? DtsProcessing.Yes : DtsProcessing.No, isCore, isFlatCore, enableI18nLegacyMessageIdFormat 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 fb8315506f..3143a8b15d 100644 --- a/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts @@ -1530,11 +1530,15 @@ exports.MissingClass2 = MissingClass2; break; case 'inlined': fileHeader = - `var __spread = (this && this.__spread) || function (...args) { /* ... */ }`; + `var __spread = (this && this.__spread) || function (...args) { /* ... */ };\n` + + `var __spreadArray = (this && this.__spreadArray) || function (...args) { /* ... */ };\n` + + `var __read = (this && this.__read) || function (...args) { /* ... */ };\n`; break; case 'inlined_with_suffix': fileHeader = - `var __spread$1 = (this && this.__spread$1) || function (...args) { /* ... */ }`; + `var __spread$1 = (this && this.__spread$1) || function (...args) { /* ... */ };\n` + + `var __spreadArray$1 = (this && this.__spreadArray$1) || function (...args) { /* ... */ };\n` + + `var __read$2 = (this && this.__read$2) || function (...args) { /* ... */ };\n`; break; } const file = { @@ -1635,6 +1639,17 @@ exports.MissingClass2 = MissingClass2; expect(parameters).toBeNull(); }); + it('recognizes delegate super call using inline spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + return _super.apply(this, __spreadArray([], __read(arguments))) || this; + }`, + 'inlined'); + + expect(parameters).toBeNull(); + }); + it('recognizes delegate super call using inline spread helper with suffix', () => { const parameters = getConstructorParameters( ` @@ -1646,6 +1661,17 @@ exports.MissingClass2 = MissingClass2; expect(parameters).toBeNull(); }); + it('recognizes delegate super call using inline spreadArray helper with suffix', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + return _super.apply(this, __spreadArray$1([], __read$2(arguments))) || this; + }`, + 'inlined_with_suffix'); + + expect(parameters).toBeNull(); + }); + it('recognizes delegate super call using imported spread helper', () => { const parameters = getConstructorParameters( ` @@ -1657,6 +1683,17 @@ exports.MissingClass2 = MissingClass2; expect(parameters).toBeNull(); }); + it('recognizes delegate super call using imported spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + return _super.apply(this, tslib.__spreadArray([], tslib.__read(arguments))) || this; + }`, + 'imported'); + + expect(parameters).toBeNull(); + }); + describe('with class member assignment', () => { it('recognizes delegate super call using inline spread helper', () => { const parameters = getConstructorParameters( @@ -1671,6 +1708,19 @@ exports.MissingClass2 = MissingClass2; expect(parameters).toBeNull(); }); + it('recognizes delegate super call using inline spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, __spreadArray([], __read(arguments))) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'inlined'); + + expect(parameters).toBeNull(); + }); + it('recognizes delegate super call using inline spread helper with suffix', () => { const parameters = getConstructorParameters( ` @@ -1684,6 +1734,19 @@ exports.MissingClass2 = MissingClass2; expect(parameters).toBeNull(); }); + it('recognizes delegate super call using inline spreadArray helper with suffix', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, __spreadArray$1([], __read$2(arguments))) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'inlined_with_suffix'); + + expect(parameters).toBeNull(); + }); + it('recognizes delegate super call using imported spread helper', () => { const parameters = getConstructorParameters( ` @@ -1696,6 +1759,19 @@ exports.MissingClass2 = MissingClass2; expect(parameters).toBeNull(); }); + + it('recognizes delegate super call using imported spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, tslib.__spreadArray([], tslib.__read(arguments))) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'imported'); + + expect(parameters).toBeNull(); + }); }); it('handles the case where a unique name was generated for _super or _this', () => { @@ -1999,10 +2075,14 @@ exports.MissingClass2 = MissingClass2; function __assign(t, ...sources) { /* ... */ } function __spread(...args) { /* ... */ } function __spreadArrays(...args) { /* ... */ } + function __spreadArray(to, from) { /* ... */ } + function __read(o) { /* ... */ } var a = __assign({foo: 'bar'}, {baz: 'qux'}); var b = __spread(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = __read(['foo', 'bar']); `, }; loadTestFiles([file]); @@ -2018,6 +2098,8 @@ exports.MissingClass2 = MissingClass2; testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead); }); it('should recognize suffixed TypeScript helpers (as function declarations)', () => { @@ -2027,10 +2109,14 @@ exports.MissingClass2 = MissingClass2; function __assign$1(t, ...sources) { /* ... */ } function __spread$2(...args) { /* ... */ } function __spreadArrays$3(...args) { /* ... */ } + function __spreadArray$3(to, from) { /* ... */ } + function __read$3(o) { /* ... */ } var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray$3(['foo', 'bar'], ['baz', 'qux']); + var e = __read$3(['foo', 'bar']); `, }; loadTestFiles([file]); @@ -2046,6 +2132,8 @@ exports.MissingClass2 = MissingClass2; testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray$3', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read$3', KnownDeclaration.TsHelperRead); }); it('should recognize TypeScript helpers (as variable declarations)', () => { @@ -2055,10 +2143,14 @@ exports.MissingClass2 = MissingClass2; var __assign = (this && this.__assign) || function (t, ...sources) { /* ... */ } var __spread = (this && this.__spread) || function (...args) { /* ... */ } var __spreadArrays = (this && this.__spreadArrays) || function (...args) { /* ... */ } + var __spreadArray = (this && this.__spreadArray) || function (to, from) { /* ... */ } + var __read = (this && this.__read) || function (o) { /* ... */ } var a = __assign({foo: 'bar'}, {baz: 'qux'}); var b = __spread(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = __read(['foo', 'bar']); `, }; loadTestFiles([file]); @@ -2074,6 +2166,8 @@ exports.MissingClass2 = MissingClass2; testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead); }); it('should recognize suffixed TypeScript helpers (as variable declarations)', () => { @@ -2083,10 +2177,14 @@ exports.MissingClass2 = MissingClass2; var __assign$1 = (this && this.__assign$1) || function (t, ...sources) { /* ... */ } var __spread$2 = (this && this.__spread$2) || function (...args) { /* ... */ } var __spreadArrays$3 = (this && this.__spreadArrays$3) || function (...args) { /* ... */ } + var __spreadArray$3 = (this && this.__spreadArray$3) || function (to, from) { /* ... */ } + var __read$3 = (this && this.__read$3) || function (o) { /* ... */ } var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray$3(['foo', 'bar'], ['baz', 'qux']); + var e = __read$3(['foo', 'bar']); `, }; loadTestFiles([file]); @@ -2102,6 +2200,8 @@ exports.MissingClass2 = MissingClass2; testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray$3', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read$3', KnownDeclaration.TsHelperRead); }); it('should recognize imported TypeScript helpers', () => { @@ -2114,6 +2214,8 @@ exports.MissingClass2 = MissingClass2; var a = tslib_1.__assign({foo: 'bar'}, {baz: 'qux'}); var b = tslib_1.__spread(['foo', 'bar'], ['baz', 'qux']); var c = tslib_1.__spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = tslib_1.__spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = tslib_1.__read(['foo', 'bar']); `, }, { @@ -2122,6 +2224,8 @@ exports.MissingClass2 = MissingClass2; export declare function __assign(t: any, ...sources: any[]): any; export declare function __spread(...args: any[][]): any[]; export declare function __spreadArrays(...args: any[][]): any[]; + export declare function __spreadArray(to: any[], from: any[]): any[]; + export declare function __read(o: any, n?: number): any[]; `, }, ]; @@ -2140,6 +2244,8 @@ exports.MissingClass2 = MissingClass2; testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign, 'tslib'); testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread, 'tslib'); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays, 'tslib'); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray, 'tslib'); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead, 'tslib'); }); it('should recognize undeclared, unimported TypeScript helpers (by name)', () => { @@ -2149,6 +2255,8 @@ exports.MissingClass2 = MissingClass2; var a = __assign({foo: 'bar'}, {baz: 'qux'}); var b = __spread(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = __read(['foo', 'bar']); `, }; loadTestFiles([file]); @@ -2174,6 +2282,8 @@ exports.MissingClass2 = MissingClass2; testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead); }); it('should recognize suffixed, undeclared, unimported TypeScript helpers (by name)', () => { @@ -2183,6 +2293,8 @@ exports.MissingClass2 = MissingClass2; var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray$3(['foo', 'bar'], ['baz', 'qux']); + var e = __read$3(['foo', 'bar']); `, }; loadTestFiles([file]); @@ -2208,6 +2320,8 @@ exports.MissingClass2 = MissingClass2; testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray$3', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read$3', KnownDeclaration.TsHelperRead); }); it('should recognize enum declarations with string values', () => { @@ -2441,6 +2555,8 @@ exports.MissingClass2 = MissingClass2; export declare function __assign(t: any, ...sources: any[]): any; export declare function __spread(...args: any[][]): any[]; export declare function __spreadArrays(...args: any[][]): any[]; + export declare function __spreadArray(to: any[], from: any[]): any[]; + export declare function __read(o: any, n?: number): any[]; export declare function __unknownHelper(...args: any[]): any; `, }; @@ -2456,6 +2572,8 @@ exports.MissingClass2 = MissingClass2; ['__assign', KnownDeclaration.TsHelperAssign], ['__spread', KnownDeclaration.TsHelperSpread], ['__spreadArrays', KnownDeclaration.TsHelperSpreadArrays], + ['__spreadArray', KnownDeclaration.TsHelperSpreadArray], + ['__read', KnownDeclaration.TsHelperRead], ['__unknownHelper', null], ]); }); 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 d258f63c4f..b6521969bf 100644 --- a/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts @@ -1485,18 +1485,22 @@ runInEachFileSystem(() => { switch (mode) { case 'imported': - fileHeader = `import {__spread} from 'tslib';`; + fileHeader = `import {__spread, __spreadArray, __read} from 'tslib';`; break; case 'imported_namespace': fileHeader = `import * as tslib from 'tslib';`; break; case 'inlined': fileHeader = - `var __spread = (this && this.__spread) || function (...args) { /* ... */ }`; + `var __spread = (this && this.__spread) || function (...args) { /* ... */ };\n` + + `var __spreadArray = (this && this.__spreadArray) || function (...args) { /* ... */ };\n` + + `var __read = (this && this.__read) || function (...args) { /* ... */ };\n`; break; case 'inlined_with_suffix': fileHeader = - `var __spread$1 = (this && this.__spread$1) || function (...args) { /* ... */ }`; + `var __spread$1 = (this && this.__spread$1) || function (...args) { /* ... */ };\n` + + `var __spreadArray$1 = (this && this.__spreadArray$1) || function (...args) { /* ... */ };\n` + + `var __read$2 = (this && this.__read$2) || function (...args) { /* ... */ };\n`; break; } @@ -1595,6 +1599,17 @@ runInEachFileSystem(() => { expect(parameters).toBeNull(); }); + it('recognizes delegate super call using inline spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + return _super.apply(this, __spreadArray([], __read(arguments))) || this; + }`, + 'inlined'); + + expect(parameters).toBeNull(); + }); + it('recognizes delegate super call using inline spread helper with suffix', () => { const parameters = getConstructorParameters( ` @@ -1606,6 +1621,17 @@ runInEachFileSystem(() => { expect(parameters).toBeNull(); }); + it('recognizes delegate super call using inline spreadArray helper with suffix', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + return _super.apply(this, __spreadArray$1([], __read$2(arguments))) || this; + }`, + 'inlined_with_suffix'); + + expect(parameters).toBeNull(); + }); + it('recognizes delegate super call using imported spread helper', () => { const parameters = getConstructorParameters( ` @@ -1617,6 +1643,17 @@ runInEachFileSystem(() => { expect(parameters).toBeNull(); }); + it('recognizes delegate super call using imported spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + return _super.apply(this, __spreadArray([], __read(arguments))) || this; + }`, + 'imported'); + + expect(parameters).toBeNull(); + }); + it('recognizes delegate super call using namespace imported spread helper', () => { const parameters = getConstructorParameters( ` @@ -1628,6 +1665,17 @@ runInEachFileSystem(() => { expect(parameters).toBeNull(); }); + it('recognizes delegate super call using namespace imported spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + return _super.apply(this, tslib.__spreadArray([], tslib.__read(arguments))) || this; + }`, + 'imported_namespace'); + + expect(parameters).toBeNull(); + }); + describe('with class member assignment', () => { it('recognizes delegate super call using inline spread helper', () => { const parameters = getConstructorParameters( @@ -1642,6 +1690,19 @@ runInEachFileSystem(() => { expect(parameters).toBeNull(); }); + it('recognizes delegate super call using inline spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, __spreadArray([], __read(arguments))) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'inlined'); + + expect(parameters).toBeNull(); + }); + it('recognizes delegate super call using inline spread helper with suffix', () => { const parameters = getConstructorParameters( ` @@ -1655,6 +1716,19 @@ runInEachFileSystem(() => { expect(parameters).toBeNull(); }); + it('recognizes delegate super call using inline spreadArray helper with suffix', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, __spreadArray$1([], __read$2(arguments))) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'inlined_with_suffix'); + + expect(parameters).toBeNull(); + }); + it('recognizes delegate super call using imported spread helper', () => { const parameters = getConstructorParameters( ` @@ -1668,6 +1742,19 @@ runInEachFileSystem(() => { expect(parameters).toBeNull(); }); + it('recognizes delegate super call using imported spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, __spreadArray([], __read(arguments))) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'imported'); + + expect(parameters).toBeNull(); + }); + it('recognizes delegate super call using namespace imported spread helper', () => { const parameters = getConstructorParameters( ` @@ -1680,6 +1767,19 @@ runInEachFileSystem(() => { expect(parameters).toBeNull(); }); + + it('recognizes delegate super call using namespace imported spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, tslib.__spreadArray([], tslib.__read(arguments))) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'imported_namespace'); + + expect(parameters).toBeNull(); + }); }); it('handles the case where a unique name was generated for _super or _this', () => { @@ -2059,10 +2159,14 @@ runInEachFileSystem(() => { function __assign(t, ...sources) { /* ... */ } function __spread(...args) { /* ... */ } function __spreadArrays(...args) { /* ... */ } + function __spreadArray(to, from) { /* ... */ } + function __read(o) { /* ... */ } var a = __assign({foo: 'bar'}, {baz: 'qux'}); var b = __spread(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = __read(['foo', 'bar']); `, }; loadTestFiles([file]); @@ -2077,6 +2181,8 @@ runInEachFileSystem(() => { testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead); }); it('should recognize suffixed TypeScript helpers (as function declarations)', () => { @@ -2086,10 +2192,14 @@ runInEachFileSystem(() => { function __assign$1(t, ...sources) { /* ... */ } function __spread$2(...args) { /* ... */ } function __spreadArrays$3(...args) { /* ... */ } + function __spreadArray$3(to, from) { /* ... */ } + function __read$3(o) { /* ... */ } var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray$3(['foo', 'bar'], ['baz', 'qux']); + var e = __read$3(['foo', 'bar']); `, }; loadTestFiles([file]); @@ -2104,6 +2214,8 @@ runInEachFileSystem(() => { testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray$3', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read$3', KnownDeclaration.TsHelperRead); }); it('should recognize TypeScript helpers (as variable declarations)', () => { @@ -2113,11 +2225,15 @@ runInEachFileSystem(() => { var __assign = (this && this.__assign) || function (t, ...sources) { /* ... */ } var __spread = (this && this.__spread) || function (...args) { /* ... */ } var __spreadArrays = (this && this.__spreadArrays) || function (...args) { /* ... */ } + var __spreadArray = (this && this.__spreadArray) || function (to, from) { /* ... */ } + var __read = (this && this._read) || function (o) { /* ... */ } var a = __assign({foo: 'bar'}, {baz: 'qux'}); var b = __spread(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); - `, + var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = __read(['foo', 'bar']); + `, }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); @@ -2131,6 +2247,8 @@ runInEachFileSystem(() => { testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead); }); it('should recognize suffixed TypeScript helpers (as variable declarations)', () => { @@ -2140,10 +2258,14 @@ runInEachFileSystem(() => { var __assign$1 = (this && this.__assign$1) || function (t, ...sources) { /* ... */ } var __spread$2 = (this && this.__spread$2) || function (...args) { /* ... */ } var __spreadArrays$3 = (this && this.__spreadArrays$3) || function (...args) { /* ... */ } + var __spreadArray$3 = (this && this.__spreadArray$3) || function (to, from) { /* ... */ } + var __read$3 = (this && this.__read$3) || function (o) { /* ... */ } var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray$3(['foo', 'bar'], ['baz', 'qux']); + var e = __read$3(['foo', 'bar']); `, }; loadTestFiles([file]); @@ -2158,6 +2280,8 @@ runInEachFileSystem(() => { testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray$3', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read$3', KnownDeclaration.TsHelperRead); }); it('should recognize imported TypeScript helpers (named imports)', () => { @@ -2165,11 +2289,13 @@ runInEachFileSystem(() => { { name: _('/test.js'), contents: ` - import {__assign, __spread, __spreadArrays} from 'tslib'; + import {__assign, __spread, __spreadArrays, __spreadArray, __read} from 'tslib'; var a = __assign({foo: 'bar'}, {baz: 'qux'}); var b = __spread(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = __read(['foo', 'bar']); `, }, { @@ -2178,6 +2304,8 @@ runInEachFileSystem(() => { export declare function __assign(t: any, ...sources: any[]): any; export declare function __spread(...args: any[][]): any[]; export declare function __spreadArrays(...args: any[][]): any[]; + export declare function __spreadArray(to: any[], from: any[]): any[]; + export declare function __read(o: any, n?: number): any[]; `, }, ]; @@ -2195,6 +2323,8 @@ runInEachFileSystem(() => { testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign, 'tslib'); testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread, 'tslib'); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays, 'tslib'); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray, 'tslib'); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead, 'tslib'); }); it('should recognize imported TypeScript helpers (star import)', () => { @@ -2207,6 +2337,8 @@ runInEachFileSystem(() => { var a = tslib_1.__assign({foo: 'bar'}, {baz: 'qux'}); var b = tslib_1.__spread(['foo', 'bar'], ['baz', 'qux']); var c = tslib_1.__spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = tslib_1.__spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = tslib_1.__read(['foo', 'bar']); `, }, { @@ -2215,6 +2347,8 @@ runInEachFileSystem(() => { export declare function __assign(t: any, ...sources: any[]): any; export declare function __spread(...args: any[][]): any[]; export declare function __spreadArrays(...args: any[][]): any[]; + export declare function __spreadArray(to: any[], from: any[]): any[]; + export declare function __read(o: any, n?: number): any[]; `, }, ]; @@ -2232,6 +2366,8 @@ runInEachFileSystem(() => { testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign, 'tslib'); testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread, 'tslib'); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays, 'tslib'); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray, 'tslib'); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead, 'tslib'); }); it('should recognize undeclared, unimported TypeScript helpers (by name)', () => { @@ -2241,6 +2377,8 @@ runInEachFileSystem(() => { var a = __assign({foo: 'bar'}, {baz: 'qux'}); var b = __spread(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = __read(['foo', 'bar']); `, }; loadTestFiles([file]); @@ -2263,6 +2401,8 @@ runInEachFileSystem(() => { testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead); }); it('should recognize suffixed, undeclared, unimported TypeScript helpers (by name)', () => { @@ -2272,6 +2412,8 @@ runInEachFileSystem(() => { var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray$3(['foo', 'bar'], ['baz', 'qux']); + var e = __read$3(['foo', 'bar']); `, }; loadTestFiles([file]); @@ -2294,6 +2436,8 @@ runInEachFileSystem(() => { testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray$3', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead); }); it('should recognize enum declarations with string values', () => { @@ -2456,6 +2600,8 @@ runInEachFileSystem(() => { export declare function __assign(t: any, ...sources: any[]): any; export declare function __spread(...args: any[][]): any[]; export declare function __spreadArrays(...args: any[][]): any[]; + export declare function __spreadArray(to: any[], from: any[]): any[]; + export declare function __read(o: any, n?: number): any[]; export declare function __unknownHelper(...args: any[]): any; `, }; @@ -2470,6 +2616,8 @@ runInEachFileSystem(() => { ['__assign', KnownDeclaration.TsHelperAssign], ['__spread', KnownDeclaration.TsHelperSpread], ['__spreadArrays', KnownDeclaration.TsHelperSpreadArrays], + ['__spreadArray', KnownDeclaration.TsHelperSpreadArray], + ['__read', KnownDeclaration.TsHelperRead], ['__unknownHelper', 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 870d49edae..b7114b3bc0 100644 --- a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts @@ -1784,6 +1784,8 @@ runInEachFileSystem(() => { }(this, (function (exports) { 'use strict'; var __spread = (this && this.__spread) || function (...args) { /* ... */ } + var __spreadArray = (this && this.__spreadArray) || function (...args) { /* ... */ } + var __read = (this && this.__read) || function (...args) { /* ... */ } `; break; case 'inlined_with_suffix': @@ -1795,6 +1797,8 @@ runInEachFileSystem(() => { }(this, (function (exports) { 'use strict'; var __spread$1 = (this && this.__spread$1) || function (...args) { /* ... */ } + var __spreadArray$1 = (this && this.__spreadArray$1) || function (...args) { /* ... */ } + var __read$2 = (this && this.__read$2) || function (...args) { /* ... */ } `; break; } @@ -1897,6 +1901,17 @@ runInEachFileSystem(() => { expect(parameters).toBeNull(); }); + it('recognizes delegate super call using inline spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + return _super.apply(this, __spreadArray([], __read(arguments))) || this; + }`, + 'inlined'); + + expect(parameters).toBeNull(); + }); + it('recognizes delegate super call using inline spread helper with suffix', () => { const parameters = getConstructorParameters( ` @@ -1908,6 +1923,17 @@ runInEachFileSystem(() => { expect(parameters).toBeNull(); }); + it('recognizes delegate super call using inline spreadArray helper with suffix', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + return _super.apply(this, __spreadArray$1([], __read$2(arguments))) || this; + }`, + 'inlined_with_suffix'); + + expect(parameters).toBeNull(); + }); + it('recognizes delegate super call using imported spread helper', () => { const parameters = getConstructorParameters( ` @@ -1919,6 +1945,17 @@ runInEachFileSystem(() => { expect(parameters).toBeNull(); }); + it('recognizes delegate super call using imported spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + return _super.apply(this, tslib_1.__spreadArray([], tslib.__read(arguments))) || this; + }`, + 'imported'); + + expect(parameters).toBeNull(); + }); + describe('with class member assignment', () => { it('recognizes delegate super call using inline spread helper', () => { const parameters = getConstructorParameters( @@ -1933,6 +1970,19 @@ runInEachFileSystem(() => { expect(parameters).toBeNull(); }); + it('recognizes delegate super call using inline spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, __spreadArray([], __read(arguments))) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'inlined'); + + expect(parameters).toBeNull(); + }); + it('recognizes delegate super call using inline spread helper with suffix', () => { const parameters = getConstructorParameters( ` @@ -1946,6 +1996,19 @@ runInEachFileSystem(() => { expect(parameters).toBeNull(); }); + it('recognizes delegate super call using inline spreadArray helper with suffix', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, __spreadArray$1([], __read$2(arguments))) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'inlined_with_suffix'); + + expect(parameters).toBeNull(); + }); + it('recognizes delegate super call using imported spread helper', () => { const parameters = getConstructorParameters( ` @@ -1958,6 +2021,19 @@ runInEachFileSystem(() => { expect(parameters).toBeNull(); }); + + it('recognizes delegate super call using imported spreadArray helper', () => { + const parameters = getConstructorParameters( + ` + function TestClass() { + var _this = _super.apply(this, tslib_1.__spreadArray([], tslib.__read(arguments))) || this; + _this.synthesizedProperty = null; + return _this; + }`, + 'imported'); + + expect(parameters).toBeNull(); + }); }); it('handles the case where a unique name was generated for _super or _this', () => { @@ -2331,10 +2407,14 @@ runInEachFileSystem(() => { function __assign(t, ...sources) { /* ... */ } function __spread(...args) { /* ... */ } function __spreadArrays(...args) { /* ... */ } + function __spreadArray(to, from) { /* ... */ } + function __read(o) { /* ... */ } var a = __assign({foo: 'bar'}, {baz: 'qux'}); var b = __spread(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = __read(['foo', 'bar']); }))); `, }; @@ -2349,6 +2429,8 @@ runInEachFileSystem(() => { testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead); }); it('should recognize suffixed TypeScript helpers (as function declarations)', () => { @@ -2363,10 +2445,14 @@ runInEachFileSystem(() => { function __assign$1(t, ...sources) { /* ... */ } function __spread$2(...args) { /* ... */ } function __spreadArrays$3(...args) { /* ... */ } + function __spreadArray$3(to, from) { /* ... */ } + function __read$3(o) { /* ... */ } var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray$3(['foo', 'bar'], ['baz', 'qux']); + var e = __read$3(['foo', 'bar']); }))); `, }; @@ -2381,6 +2467,8 @@ runInEachFileSystem(() => { testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray$3', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read$3', KnownDeclaration.TsHelperRead); }); it('should recognize TypeScript helpers (as variable declarations)', () => { @@ -2395,10 +2483,14 @@ runInEachFileSystem(() => { var __assign = (this && this.__assign) || function (t, ...sources) { /* ... */ } var __spread = (this && this.__spread) || function (...args) { /* ... */ } var __spreadArrays = (this && this.__spreadArrays) || function (...args) { /* ... */ } + var __spreadArray = (this && this.__spreadArray) || function (to, from) { /* ... */ } + var __read = (this && this.__read) || function (o) { /* ... */ } var a = __assign({foo: 'bar'}, {baz: 'qux'}); var b = __spread(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = __read(['foo', 'bar']); }))); `, }; @@ -2413,6 +2505,8 @@ runInEachFileSystem(() => { testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead); }); it('should recognize suffixed TypeScript helpers (as variable declarations)', () => { @@ -2427,10 +2521,14 @@ runInEachFileSystem(() => { var __assign$1 = (this && this.__assign$1) || function (t, ...sources) { /* ... */ } var __spread$2 = (this && this.__spread$2) || function (...args) { /* ... */ } var __spreadArrays$3 = (this && this.__spreadArrays$3) || function (...args) { /* ... */ } + var __spreadArray$3 = (this && this.__spreadArray$3) || function (to, from) { /* ... */ } + var __read$3 = (this && this.__read$3) || function (o) { /* ... */ } var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray$3(['foo', 'bar'], ['baz', 'qux']); + var e = __read$3(['foo', 'bar']); }))); `, }; @@ -2445,6 +2543,8 @@ runInEachFileSystem(() => { testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray$3', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read$3', KnownDeclaration.TsHelperRead); }); it('should recognize imported TypeScript helpers', () => { @@ -2460,6 +2560,8 @@ runInEachFileSystem(() => { var a = tslib_1.__assign({foo: 'bar'}, {baz: 'qux'}); var b = tslib_1.__spread(['foo', 'bar'], ['baz', 'qux']); var c = tslib_1.__spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = tslib_1.__spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = tslib_1.__read(['foo', 'bar']); }))); `, }, @@ -2469,6 +2571,8 @@ runInEachFileSystem(() => { export declare function __assign(t: any, ...sources: any[]): any; export declare function __spread(...args: any[][]): any[]; export declare function __spreadArrays(...args: any[][]): any[]; + export declare function __spreadArray(to: any[], from: any[]): any[]; + export declare function __read(o: any, n?: number): any[]; `, }, ]; @@ -2487,6 +2591,8 @@ runInEachFileSystem(() => { testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign, 'tslib'); testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread, 'tslib'); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays, 'tslib'); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray, 'tslib'); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead, 'tslib'); }); it('should recognize undeclared, unimported TypeScript helpers (by name)', () => { @@ -2501,6 +2607,8 @@ runInEachFileSystem(() => { var a = __assign({foo: 'bar'}, {baz: 'qux'}); var b = __spread(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray(['foo', 'bar'], ['baz', 'qux']); + var e = __read(['foo', 'bar']); }))); `, }; @@ -2527,6 +2635,8 @@ runInEachFileSystem(() => { testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read', KnownDeclaration.TsHelperRead); }); it('should recognize suffixed, undeclared, unimported TypeScript helpers (by name)', () => { @@ -2541,6 +2651,8 @@ runInEachFileSystem(() => { var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); + var d = __spreadArray$3(['foo', 'bar'], ['baz', 'qux']); + var e = __read$3(['foo', 'bar']); }))); `, }; @@ -2567,6 +2679,8 @@ runInEachFileSystem(() => { testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + testForHelper('d', '__spreadArray$3', KnownDeclaration.TsHelperSpreadArray); + testForHelper('e', '__read$3', KnownDeclaration.TsHelperRead); }); it('should recognize enum declarations with string values', () => { @@ -2841,6 +2955,8 @@ runInEachFileSystem(() => { export declare function __assign(t: any, ...sources: any[]): any; export declare function __spread(...args: any[][]): any[]; export declare function __spreadArrays(...args: any[][]): any[]; + export declare function __spreadArray(to: any[], from: any[]): any[]; + export declare function __read(o: any, n?: number): any[]; export declare function __unknownHelper(...args: any[]): any; `, }; @@ -2855,6 +2971,8 @@ runInEachFileSystem(() => { ['__assign', KnownDeclaration.TsHelperAssign], ['__spread', KnownDeclaration.TsHelperSpread], ['__spreadArrays', KnownDeclaration.TsHelperSpreadArrays], + ['__spreadArray', KnownDeclaration.TsHelperSpreadArray], + ['__read', KnownDeclaration.TsHelperRead], ['__unknownHelper', null], ]); }); diff --git a/packages/compiler-cli/ngcc/test/host/util.ts b/packages/compiler-cli/ngcc/test/host/util.ts index 5767be4bf7..4fc662a552 100644 --- a/packages/compiler-cli/ngcc/test/host/util.ts +++ b/packages/compiler-cli/ngcc/test/host/util.ts @@ -5,7 +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 {Trait, TraitState} from '@angular/compiler-cli/src/ngtsc/transform'; import * as ts from 'typescript'; + +import {SemanticSymbol} from '../../../src/ngtsc/incremental/semantic_graph'; import {CtorParameter, TypeValueReferenceKind} from '../../../src/ngtsc/reflection'; /** @@ -48,3 +51,18 @@ export function expectTypeValueReferencesForParameters( } }); } + +export function getTraitDiagnostics(trait: Trait): + ts.Diagnostic[]|null { + if (trait.state === TraitState.Analyzed) { + return trait.analysisDiagnostics; + } else if (trait.state === TraitState.Resolved) { + const diags = [ + ...(trait.analysisDiagnostics ?? []), + ...(trait.resolveDiagnostics ?? []), + ]; + return diags.length > 0 ? diags : null; + } else { + return null; + } +} diff --git a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts index e1d0e5bdd0..1420293481 100644 --- a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts +++ b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts @@ -10,7 +10,7 @@ import {readFileSync} from 'fs'; import * as os from 'os'; -import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, join} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system'; import {Folder, MockFileSystem, runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; import {loadStandardTestFiles, loadTestFiles} from '../../../src/ngtsc/testing'; @@ -25,6 +25,7 @@ import {DirectPackageJsonUpdater, PackageJsonUpdater} from '../../src/writing/pa import {compileIntoApf, compileIntoFlatEs2015Package, compileIntoFlatEs5Package} from './util'; +const ANGULAR_CORE_IMPORT_REGEX = /import \* as ɵngcc\d+ from '@angular\/core';/; const testFiles = loadStandardTestFiles({fakeCore: false, rxjs: true}); runInEachFileSystem(() => { @@ -63,7 +64,7 @@ runInEachFileSystem(() => { function setupAngularCoreEsm5() { const pkgPath = _('/node_modules/@angular/core'); const pkgJsonPath = fs.join(pkgPath, 'package.json'); - const pkgJson = JSON.parse(fs.readFile(pkgJsonPath)); + const pkgJson = JSON.parse(fs.readFile(pkgJsonPath)) as EntryPointPackageJson; fs.ensureDir(fs.join(pkgPath, 'fesm5')); fs.writeFile( @@ -153,6 +154,85 @@ runInEachFileSystem(() => { expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toBeUndefined(); }); + it('should report an error, if one of the format-paths is missing or empty', () => { + loadTestFiles([ + // A package with a format-path (main) that points to a missing file. + { + name: _(`/dist/pkg-with-missing-main/package.json`), + contents: ` + { + "name": "pkg-with-missing-main", + "typings": "./index.d.ts", + "es2015": "./index-es2015.js", + "fesm5": "./index-es5.js", + "main": "./index-missing.js" + } + `, + }, + { + name: _('/dist/pkg-with-missing-main/index.d.ts'), + contents: 'export type DummyData = boolean;' + }, + { + name: _('/dist/pkg-with-missing-main/index-es2015.js'), + contents: 'var DUMMY_DATA = true;' + }, + {name: _('/dist/pkg-with-missing-main/index-es5.js'), contents: 'var DUMMY_DATA = true;'}, + {name: _('/dist/pkg-with-missing-main/index.metadata.json'), contents: 'DUMMY DATA'}, + + // A package with a format-path (main) that points to an empty file. + { + name: _(`/dist/pkg-with-empty-main/package.json`), + contents: ` + { + "name": "pkg-with-empty-main", + "typings": "./index.d.ts", + "es2015": "./index-es2015.js", + "fesm5": "./index-es5.js", + "main": "./index-empty.js" + } + `, + }, + { + name: _('/dist/pkg-with-empty-main/index.d.ts'), + contents: 'export type DummyData = boolean;' + }, + {name: _('/dist/pkg-with-empty-main/index-empty.js'), contents: ''}, + {name: _('/dist/pkg-with-empty-main/index-es2015.js'), contents: 'var DUMMY_DATA = true;'}, + {name: _('/dist/pkg-with-empty-main/index-es5.js'), contents: 'var DUMMY_DATA = true;'}, + {name: _('/dist/pkg-with-empty-main/index.metadata.json'), contents: 'DUMMY DATA'}, + ]); + + const logger = new MockLogger(); + mainNgcc({ + basePath: '/dist', + propertiesToConsider: ['es2015', 'main', 'fesm5'], + logger, + }); + + expect(loadPackage('pkg-with-missing-main', _('/dist')).__processed_by_ivy_ngcc__).toEqual({ + es2015: jasmine.any(String), + fesm5: jasmine.any(String), + typings: jasmine.any(String), + }); + expect(loadPackage('pkg-with-empty-main', _('/dist')).__processed_by_ivy_ngcc__).toEqual({ + es2015: jasmine.any(String), + fesm5: jasmine.any(String), + typings: jasmine.any(String), + }); + + expect(logger.logs.error).toEqual([ + [ + 'Failed to compile entry-point pkg-with-missing-main (`main` as unknown format) due to ' + + 'property `main` pointing to a missing or empty file: ./index-missing.js', + ], + [ + 'Failed to compile entry-point pkg-with-empty-main (`main` as unknown format) due to ' + + 'property `main` pointing to a missing or empty file: ./index-empty.js', + ], + ]); + }); + it('should generate correct metadata for decorated getter/setter properties', () => { setupAngularCoreEsm5(); compileIntoFlatEs5Package('test-package', { @@ -181,7 +261,8 @@ runInEachFileSystem(() => { const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`)).replace(/\s+/g, ' '); expect(jsContents) .toContain( - '/*@__PURE__*/ (function () { ɵngcc0.ɵsetClassMetadata(FooDirective, ' + + '(function () { (typeof ngDevMode === "undefined" || ngDevMode) && ' + + 'ɵngcc0.ɵsetClassMetadata(FooDirective, ' + '[{ type: Directive, args: [{ selector: \'[foo]\' }] }], ' + 'function () { return []; }, ' + '{ bar: [{ type: Input }] }); })();'); @@ -267,6 +348,84 @@ runInEachFileSystem(() => { }); }); + it(`should be able to detect synthesized constructors in ES5 with downlevelIteration enabled (imported helpers)`, + () => { + setupAngularCoreEsm5(); + compileIntoApf( + 'test-package', { + '/index.ts': ` + import {Injectable} from '@angular/core'; + + @Injectable() + export class Base {} + + @Injectable() + export class SubClass extends Base { + constructor() { + // Note: mimic the situation where TS is first emitted into ES2015, resulting + // in the spread super call below, and then downleveled into ES5 using the + // "downlevelIteration" option. + super(...arguments); + this.foo = 'bar'; + } + } + `, + }, + {importHelpers: true, noEmitHelpers: true, downlevelIteration: true}); + + mainNgcc({ + basePath: '/node_modules', + targetEntryPointPath: 'test-package', + propertiesToConsider: ['esm5'], + }); + + const jsContents = fs.readFile(_(`/node_modules/test-package/esm5/src/index.js`)); + // Verify that the ES5 bundle does contain the expected downleveling syntax. + expect(jsContents).toContain('__spreadArray([], __read(arguments))'); + expect(jsContents) + .toContain( + 'var ɵSubClass_BaseFactory; return function SubClass_Factory(t) { return (ɵSubClass_BaseFactory || (ɵSubClass_BaseFactory = ɵngcc0.ɵɵgetInheritedFactory(SubClass)))(t || SubClass); };'); + }); + + it(`should be able to detect synthesized constructors in ES5 with downlevelIteration enabled (emitted helpers)`, + () => { + setupAngularCoreEsm5(); + compileIntoApf( + 'test-package', { + '/index.ts': ` + import {Injectable} from '@angular/core'; + + @Injectable() + export class Base {} + + @Injectable() + export class SubClass extends Base { + constructor() { + // Note: mimic the situation where TS is first emitted into ES2015, resulting + // in the spread super call below, and then downleveled into ES5 using the + // "downlevelIteration" option. + super(...arguments); + this.foo = 'bar'; + } + } + `, + }, + {importHelpers: false, noEmitHelpers: false, downlevelIteration: true}); + + mainNgcc({ + basePath: '/node_modules', + targetEntryPointPath: 'test-package', + propertiesToConsider: ['esm5'], + }); + + const jsContents = fs.readFile(_(`/node_modules/test-package/esm5/src/index.js`)); + // Verify that the ES5 bundle does contain the expected downleveling syntax. + expect(jsContents).toContain('__spreadArray([], __read(arguments))'); + expect(jsContents) + .toContain( + 'var ɵSubClass_BaseFactory; return function SubClass_Factory(t) { return (ɵSubClass_BaseFactory || (ɵSubClass_BaseFactory = ɵngcc0.ɵɵgetInheritedFactory(SubClass)))(t || SubClass); };'); + }); + it('should not add `const` in ES5 generated code', () => { setupAngularCoreEsm5(); compileIntoFlatEs5Package('test-package', { @@ -522,7 +681,8 @@ runInEachFileSystem(() => { const processedFile = fs.readFile(_('/node_modules/test-package/index.js')); expect(processedFile) - .toContain('FooModule.ɵmod = ɵngcc0.ɵɵdefineNgModule({ type: FooModule });'); + .toContain( + 'FooModule.ɵmod = /*@__PURE__*/ ɵngcc0.ɵɵdefineNgModule({ type: FooModule });'); expect(processedFile) .toContain( 'ɵngcc0.ɵɵsetNgModuleScope(FooModule, { declarations: function () { return [exports.FooDirective]; } });'); @@ -571,7 +731,7 @@ runInEachFileSystem(() => { fail('should have thrown'); } catch (e) { expect(e.message).toContain( - 'Failed to compile entry-point test-package (esm2015 as esm2015) due to compilation errors:'); + 'Failed to compile entry-point test-package (`esm2015` as esm2015) due to compilation errors:'); expect(e.message).toContain('NG1010'); expect(e.message).toContain('selector must be a string'); } @@ -633,8 +793,8 @@ runInEachFileSystem(() => { expect(jsContents) .toContain( `TestClass.ɵfac = function TestClass_Factory(t) { return new (t || TestClass)(); };\n` + - `TestClass.ɵpipe = ɵngcc0.ɵɵdefinePipe({ name: "myTestPipe", type: TestClass, pure: true });\n` + - `TestClass.ɵprov = ɵngcc0.ɵɵdefineInjectable({`); + `TestClass.ɵpipe = /*@__PURE__*/ ɵngcc0.ɵɵdefinePipe({ name: "myTestPipe", type: TestClass, pure: true });\n` + + `TestClass.ɵprov = /*@__PURE__*/ ɵngcc0.ɵɵdefineInjectable({`); }); // https://github.com/angular/angular/issues/38883 @@ -663,7 +823,7 @@ runInEachFileSystem(() => { expect(jsContents) .toContain( `TestClass.ɵfac = function TestClass_Factory(t) { ` + - `return new (t || TestClass)(ɵngcc0.ɵɵinjectPipeChangeDetectorRef()); };`); + `return new (t || TestClass)(ɵngcc0.ɵɵdirectiveInject(ɵngcc0.ChangeDetectorRef, 16)); };`); }); it('should use the correct type name in typings files when an export has a different name in source files', @@ -692,7 +852,8 @@ 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}, never>`); + expect(dtsContents) + .toContain(`static ɵfac: ɵngcc0.ɵɵFactoryDeclaration<${exportedName}, never>`); }); it('should include constructor metadata in factory definitions', () => { @@ -705,7 +866,7 @@ runInEachFileSystem(() => { const dtsContents = fs.readFile(_('/node_modules/@angular/common/common.d.ts')); expect(dtsContents) .toContain( - `static ɵfac: ɵngcc0.ɵɵFactoryDef`); + `static ɵfac: ɵngcc0.ɵɵFactoryDeclaration`); }); it('should add generic type for ModuleWithProviders and generate exports for private modules', @@ -1016,7 +1177,7 @@ runInEachFileSystem(() => { function markPropertiesAsProcessed(packagePath: string, properties: EntryPointJsonProperty[]) { const basePath = _('/node_modules'); - const targetPackageJsonPath = join(basePath, packagePath, 'package.json'); + const targetPackageJsonPath = fs.join(basePath, packagePath, 'package.json'); const targetPackage = loadPackage(packagePath); markAsProcessed( pkgJsonUpdater, targetPackage, targetPackageJsonPath, ['typings', ...properties]); @@ -1239,9 +1400,154 @@ runInEachFileSystem(() => { }); }); + describe('with typingsOnly set to true', () => { + it('should only compile the typings', () => { + mainNgcc({ + basePath: '/node_modules', + propertiesToConsider: ['module', 'fesm2015', 'main'], + typingsOnly: true, + compileAllFormats: true, + logger: new MockLogger(), + }); + expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ + typings: '0.0.0-PLACEHOLDER', + }); + expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toEqual({ + typings: '0.0.0-PLACEHOLDER', + }); + expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toEqual({ + typings: '0.0.0-PLACEHOLDER', + }); + expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({ + typings: '0.0.0-PLACEHOLDER', + }); + + // Doesn't touch original source files + expect(fs.readFile(_(`/node_modules/@angular/common/esm2015/src/common_module.js`))) + .not.toMatch(ANGULAR_CORE_IMPORT_REGEX); + // Or create a backup of the original + expect(fs.exists( + _(`/node_modules/@angular/common/esm2015/src/common_module.js.__ivy_ngcc_bak`))) + .toBe(false); + + // Overwrites .d.ts files + expect(fs.readFile(_(`/node_modules/@angular/common/common.d.ts`))) + .toMatch(ANGULAR_CORE_IMPORT_REGEX); + // And makes a backup + expect(fs.exists(_(`/node_modules/@angular/common/common.d.ts.__ivy_ngcc_bak`))).toBe(true); + }); + + it('should not compile anything when typings have already been processed', () => { + let logger = new MockLogger(); + mainNgcc({ + basePath: '/node_modules', + propertiesToConsider: ['esm2015'], + targetEntryPointPath: '@angular/core', + typingsOnly: true, + logger, + }); + expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ + typings: '0.0.0-PLACEHOLDER', + }); + expect(fs.readFile(_(`/node_modules/@angular/core/esm2015/src/application_init.js`))) + .not.toMatch(ANGULAR_CORE_IMPORT_REGEX); + expect(logger.logs.debug).toContain([' Successfully compiled @angular/core : esm2015']); + + // Try to process the typings for @angular/core again, now using a different format + // property, to verify that it does not process the entry-point again and that the JS + // files are still untouched. + logger = new MockLogger(); + mainNgcc({ + basePath: '/node_modules', + propertiesToConsider: ['main'], + targetEntryPointPath: '@angular/core', + typingsOnly: true, + logger, + }); + expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ + typings: '0.0.0-PLACEHOLDER', + }); + expect(fs.readFile(_(`/node_modules/@angular/core/esm2015/src/application_init.js`))) + .not.toMatch(ANGULAR_CORE_IMPORT_REGEX); + expect(logger.logs.debug).toContain([ + 'Skipping @angular/core : typings have already been processed.' + ]); + + // Now also process the typings for @angular/common to verify that its dependency on + // @angular/core, which has already been processed and will therefore be skipped, is able + // to succeed. + logger = new MockLogger(); + mainNgcc({ + basePath: '/node_modules', + propertiesToConsider: ['esm2015'], + targetEntryPointPath: '@angular/common', + typingsOnly: true, + logger, + }); + expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ + typings: '0.0.0-PLACEHOLDER', + }); + expect(fs.readFile(_(`/node_modules/@angular/core/esm2015/src/application_init.js`))) + .not.toMatch(ANGULAR_CORE_IMPORT_REGEX); + expect(fs.readFile(_(`/node_modules/@angular/common/esm2015/src/common_module.js`))) + .not.toMatch(ANGULAR_CORE_IMPORT_REGEX); + expect(logger.logs.debug).toContain([ + 'Skipping @angular/core : typings have already been processed.' + ]); + expect(logger.logs.debug).toContain([' Successfully compiled @angular/common : esm2015']); + }); + + it('should cope with compiling the same entry-point multiple times with different formats', + () => { + mainNgcc({ + basePath: '/node_modules', + propertiesToConsider: ['main'], + typingsOnly: true, + logger: new MockLogger(), + }); + expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ + typings: '0.0.0-PLACEHOLDER', + }); + + // If ngcc tries to write out the typings files again, this will throw an exception. + mainNgcc({ + basePath: '/node_modules', + propertiesToConsider: ['esm2015'], + typingsOnly: true, + logger: new MockLogger(), + }); + expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ + typings: '0.0.0-PLACEHOLDER', + }); + }); + + it('should cope with compiling typings only followed by javascript formats', () => { + mainNgcc({ + basePath: '/node_modules', + propertiesToConsider: ['esm2015', 'main'], + typingsOnly: true, + logger: new MockLogger(), + }); + expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ + typings: '0.0.0-PLACEHOLDER', + }); + + // If ngcc tries to write out the typings files again, this will throw an exception. + mainNgcc({ + basePath: '/node_modules', + propertiesToConsider: ['esm2015', 'main'], + logger: new MockLogger(), + }); + expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ + main: '0.0.0-PLACEHOLDER', + esm2015: '0.0.0-PLACEHOLDER', + typings: '0.0.0-PLACEHOLDER', + }); + }); + }); + describe('with createNewEntryPointFormats', () => { it('should create new files rather than overwriting the originals', () => { - const ANGULAR_CORE_IMPORT_REGEX = /import \* as ɵngcc\d+ from '@angular\/core';/; mainNgcc({ basePath: '/node_modules', createNewEntryPointFormats: true, @@ -1389,12 +1695,13 @@ runInEachFileSystem(() => { {basePath: '/node_modules', propertiesToConsider: ['main'], logger: new MockLogger()}); // Check that common/testing ES5 was processed let commonTesting = - JSON.parse(fs.readFile(_('/node_modules/@angular/common/testing/package.json'))); + JSON.parse(fs.readFile(_('/node_modules/@angular/common/testing/package.json'))) as + EntryPointPackageJson; expect(hasBeenProcessed(commonTesting, 'main')).toBe(true); expect(hasBeenProcessed(commonTesting, 'esm2015')).toBe(false); // Modify the manifest to test that is has no effect - let manifest: EntryPointManifestFile = - JSON.parse(fs.readFile(_('/node_modules/__ngcc_entry_points__.json'))); + let manifest = JSON.parse(fs.readFile(_('/node_modules/__ngcc_entry_points__.json'))) as + EntryPointManifestFile; manifest.entryPointPaths = manifest.entryPointPaths.filter(paths => paths[1] !== '@angular/common/testing'); fs.writeFile(_('/node_modules/__ngcc_entry_points__.json'), JSON.stringify(manifest)); @@ -1408,12 +1715,14 @@ runInEachFileSystem(() => { }); // Check that common/testing ES2015 is now processed, despite the manifest not listing it commonTesting = - JSON.parse(fs.readFile(_('/node_modules/@angular/common/testing/package.json'))); + JSON.parse(fs.readFile(_('/node_modules/@angular/common/testing/package.json'))) as + EntryPointPackageJson; expect(hasBeenProcessed(commonTesting, 'main')).toBe(true); expect(hasBeenProcessed(commonTesting, 'esm2015')).toBe(true); // Check that the newly computed manifest has written to disk, containing the path that we // had removed earlier. - manifest = JSON.parse(fs.readFile(_('/node_modules/__ngcc_entry_points__.json'))); + manifest = JSON.parse(fs.readFile(_('/node_modules/__ngcc_entry_points__.json'))) as + EntryPointManifestFile; expect(manifest.entryPointPaths).toContain([ '@angular/common', '@angular/common/testing', @@ -1462,7 +1771,7 @@ runInEachFileSystem(() => { fail('should have thrown'); } catch (e) { expect(e.message).toContain( - 'Failed to compile entry-point fatal-error (es2015 as esm2015) due to compilation errors:'); + 'Failed to compile entry-point fatal-error (`es2015` as esm2015) due to compilation errors:'); expect(e.message).toContain('NG2001'); expect(e.message).toContain('component is missing a template'); } @@ -1540,7 +1849,7 @@ runInEachFileSystem(() => { expect(logger.logs.error.length).toEqual(1); const message = logger.logs.error[0][0]; expect(message).toContain( - 'Failed to compile entry-point fatal-error (es2015 as esm2015) due to compilation errors:'); + 'Failed to compile entry-point fatal-error (`es2015` as esm2015) due to compilation errors:'); expect(message).toContain('NG2001'); expect(message).toContain('component is missing a template'); @@ -1956,14 +2265,14 @@ runInEachFileSystem(() => { const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`)); expect(jsContents) .toContain( - 'DerivedDir.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: DerivedDir, ' + + 'DerivedDir.ɵdir = /*@__PURE__*/ ɵngcc0.ɵɵdefineDirective({ type: DerivedDir, ' + 'selectors: [["", "base", ""]], exportAs: ["base1", "base2"], ' + 'features: [ɵngcc0.ɵɵInheritDefinitionFeature, ɵngcc0.ɵɵCopyDefinitionFeature] });'); const dtsContents = fs.readFile(_(`/node_modules/test-package/index.d.ts`)); expect(dtsContents) .toContain( - 'static ɵdir: ɵngcc0.ɵɵDirectiveDefWithMeta;'); + 'static ɵdir: ɵngcc0.ɵɵDirectiveDeclaration;'); }); it('should generate a component definition with CopyDefinitionFeature for an undecorated child component', @@ -1995,7 +2304,7 @@ runInEachFileSystem(() => { const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`)); - expect(jsContents).toContain('DerivedCmp.ɵcmp = ɵngcc0.ɵɵdefineComponent'); + expect(jsContents).toContain('DerivedCmp.ɵcmp = /*@__PURE__*/ ɵngcc0.ɵɵdefineComponent'); expect(jsContents) .toContain( 'features: [ɵngcc0.ɵɵInheritDefinitionFeature, ɵngcc0.ɵɵCopyDefinitionFeature]'); @@ -2003,7 +2312,7 @@ runInEachFileSystem(() => { const dtsContents = fs.readFile(_(`/node_modules/test-package/index.d.ts`)); expect(dtsContents) .toContain( - 'static ɵcmp: ɵngcc0.ɵɵComponentDefWithMeta;'); + 'static ɵcmp: ɵngcc0.ɵɵComponentDeclaration;'); }); it('should generate directive definitions with CopyDefinitionFeature for undecorated child directives in a long inheritance chain', @@ -2039,13 +2348,13 @@ runInEachFileSystem(() => { const dtsContents = fs.readFile(_(`/node_modules/test-package/index.d.ts`)); expect(dtsContents) .toContain( - 'static ɵdir: ɵngcc0.ɵɵDirectiveDefWithMeta;'); + 'static ɵdir: ɵngcc0.ɵɵDirectiveDeclaration;'); expect(dtsContents) .toContain( - 'static ɵdir: ɵngcc0.ɵɵDirectiveDefWithMeta;'); + 'static ɵdir: ɵngcc0.ɵɵDirectiveDeclaration;'); expect(dtsContents) .toContain( - 'static ɵdir: ɵngcc0.ɵɵDirectiveDefWithMeta;'); + 'static ɵdir: ɵngcc0.ɵɵDirectiveDeclaration;'); }); }); @@ -2247,7 +2556,8 @@ runInEachFileSystem(() => { function loadPackage( packageName: string, basePath: AbsoluteFsPath = _('/node_modules')): EntryPointPackageJson { - return JSON.parse(fs.readFile(fs.resolve(basePath, packageName, 'package.json'))); + return JSON.parse(fs.readFile(fs.resolve(basePath, packageName, 'package.json'))) as + EntryPointPackageJson; } function initMockFileSystem(fs: FileSystem, testFiles: Folder) { 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 21fc15790a..b1445c4085 100644 --- a/packages/compiler-cli/ngcc/test/locking/sync_locker_spec.ts +++ b/packages/compiler-cli/ngcc/test/locking/sync_locker_spec.ts @@ -58,8 +58,7 @@ runInEachFileSystem(() => { expect(() => locker.lock(() => {})) .toThrowError( `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 running multiple builds in parallel then you might try pre-processing your node_modules via the command line ngcc tool before starting the builds.\n` + `(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/ngcc_options_spec.ts b/packages/compiler-cli/ngcc/test/ngcc_options_spec.ts index 1ecb30b037..31ee7522a5 100644 --- a/packages/compiler-cli/ngcc/test/ngcc_options_spec.ts +++ b/packages/compiler-cli/ngcc/test/ngcc_options_spec.ts @@ -99,6 +99,23 @@ runInEachFileSystem(() => { expect(setup.tsConfig?.rootNames).toEqual([]); expect((setup.logger as MockLogger).logs.warn).toEqual([]); }); + + it('should not modify `compileAllFormats` if `typingsOnly` is falsy', () => { + let setup = getSharedSetup({...createOptions(), compileAllFormats: true, typingsOnly: false}); + expect(setup.typingsOnly).toBe(false); + expect(setup.compileAllFormats).toBe(true); + + setup = getSharedSetup({...createOptions(), compileAllFormats: true}); + expect(setup.typingsOnly).toBe(false); + expect(setup.compileAllFormats).toBe(true); + }); + + it('should force `compileAllFormats` to false if `typingsOnly` is true', () => { + const setup = + getSharedSetup({...createOptions(), compileAllFormats: true, typingsOnly: true}); + expect(setup.typingsOnly).toBe(true); + expect(setup.compileAllFormats).toBe(false); + }); }); describe('getMaxNumberOfWorkers', () => { 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 dd9b032795..2d462e6bbe 100644 --- a/packages/compiler-cli/ngcc/test/packages/build_marker_spec.ts +++ b/packages/compiler-cli/ngcc/test/packages/build_marker_spec.ts @@ -85,48 +85,49 @@ runInEachFileSystem(() => { const COMMON_PACKAGE_PATH = _('/node_modules/@angular/common/package.json'); const fs = getFileSystem(); const pkgUpdater = new DirectPackageJsonUpdater(fs); - let pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)); + // TODO: Determine the correct/best type for the `pkg` type. + let pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)) as EntryPointPackageJson; expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined(); expect(pkg.scripts).toBeUndefined(); markAsProcessed(pkgUpdater, pkg, COMMON_PACKAGE_PATH, ['fesm2015', 'fesm5']); - pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)); - expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toBe('0.0.0-PLACEHOLDER'); - expect(pkg.__processed_by_ivy_ngcc__.fesm5).toBe('0.0.0-PLACEHOLDER'); - expect(pkg.__processed_by_ivy_ngcc__.esm2015).toBeUndefined(); - expect(pkg.__processed_by_ivy_ngcc__.esm5).toBeUndefined(); - expect(pkg.scripts.prepublishOnly).toBeDefined(); + pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)) as EntryPointPackageJson; + expect(pkg.__processed_by_ivy_ngcc__!.fesm2015).toBe('0.0.0-PLACEHOLDER'); + expect(pkg.__processed_by_ivy_ngcc__!.fesm5).toBe('0.0.0-PLACEHOLDER'); + expect(pkg.__processed_by_ivy_ngcc__!.esm2015).toBeUndefined(); + expect(pkg.__processed_by_ivy_ngcc__!.esm5).toBeUndefined(); + expect(pkg.scripts!.prepublishOnly).toBeDefined(); markAsProcessed(pkgUpdater, pkg, COMMON_PACKAGE_PATH, ['esm2015', 'esm5']); - pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)); - expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toBe('0.0.0-PLACEHOLDER'); - expect(pkg.__processed_by_ivy_ngcc__.fesm5).toBe('0.0.0-PLACEHOLDER'); - expect(pkg.__processed_by_ivy_ngcc__.esm2015).toBe('0.0.0-PLACEHOLDER'); - expect(pkg.__processed_by_ivy_ngcc__.esm5).toBe('0.0.0-PLACEHOLDER'); - expect(pkg.scripts.prepublishOnly).toBeDefined(); + pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)) as EntryPointPackageJson; + expect(pkg.__processed_by_ivy_ngcc__!.fesm2015).toBe('0.0.0-PLACEHOLDER'); + expect(pkg.__processed_by_ivy_ngcc__!.fesm5).toBe('0.0.0-PLACEHOLDER'); + expect(pkg.__processed_by_ivy_ngcc__!.esm2015).toBe('0.0.0-PLACEHOLDER'); + expect(pkg.__processed_by_ivy_ngcc__!.esm5).toBe('0.0.0-PLACEHOLDER'); + expect(pkg.scripts!.prepublishOnly).toBeDefined(); }); it('should update the packageJson object in-place', () => { const COMMON_PACKAGE_PATH = _('/node_modules/@angular/common/package.json'); const fs = getFileSystem(); const pkgUpdater = new DirectPackageJsonUpdater(fs); - const pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)); + const pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)) as EntryPointPackageJson; expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined(); expect(pkg.scripts).toBeUndefined(); markAsProcessed(pkgUpdater, pkg, COMMON_PACKAGE_PATH, ['fesm2015', 'fesm5']); - expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toBe('0.0.0-PLACEHOLDER'); - expect(pkg.__processed_by_ivy_ngcc__.fesm5).toBe('0.0.0-PLACEHOLDER'); - expect(pkg.__processed_by_ivy_ngcc__.esm2015).toBeUndefined(); - expect(pkg.__processed_by_ivy_ngcc__.esm5).toBeUndefined(); - expect(pkg.scripts.prepublishOnly).toBeDefined(); + expect(pkg.__processed_by_ivy_ngcc__!.fesm2015).toBe('0.0.0-PLACEHOLDER'); + expect(pkg.__processed_by_ivy_ngcc__!.fesm5).toBe('0.0.0-PLACEHOLDER'); + expect(pkg.__processed_by_ivy_ngcc__!.esm2015).toBeUndefined(); + expect(pkg.__processed_by_ivy_ngcc__!.esm5).toBeUndefined(); + expect(pkg.scripts!.prepublishOnly).toBeDefined(); markAsProcessed(pkgUpdater, pkg, COMMON_PACKAGE_PATH, ['esm2015', 'esm5']); - expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toBe('0.0.0-PLACEHOLDER'); - expect(pkg.__processed_by_ivy_ngcc__.fesm5).toBe('0.0.0-PLACEHOLDER'); - expect(pkg.__processed_by_ivy_ngcc__.esm2015).toBe('0.0.0-PLACEHOLDER'); - expect(pkg.__processed_by_ivy_ngcc__.esm5).toBe('0.0.0-PLACEHOLDER'); - expect(pkg.scripts.prepublishOnly).toBeDefined(); + expect(pkg.__processed_by_ivy_ngcc__!.fesm2015).toBe('0.0.0-PLACEHOLDER'); + expect(pkg.__processed_by_ivy_ngcc__!.fesm5).toBe('0.0.0-PLACEHOLDER'); + expect(pkg.__processed_by_ivy_ngcc__!.esm2015).toBe('0.0.0-PLACEHOLDER'); + expect(pkg.__processed_by_ivy_ngcc__!.esm5).toBe('0.0.0-PLACEHOLDER'); + expect(pkg.scripts!.prepublishOnly).toBeDefined(); }); it('should one perform one write operation for all updated properties', () => { @@ -134,7 +135,7 @@ runInEachFileSystem(() => { const fs = getFileSystem(); const pkgUpdater = new DirectPackageJsonUpdater(fs); const writeFileSpy = spyOn(fs, 'writeFile'); - let pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)); + let pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)) as EntryPointPackageJson; markAsProcessed( pkgUpdater, pkg, COMMON_PACKAGE_PATH, ['fesm2015', 'fesm5', 'esm2015', 'esm5']); @@ -146,34 +147,34 @@ runInEachFileSystem(() => { const fs = getFileSystem(); const pkgUpdater = new DirectPackageJsonUpdater(fs); const prepublishOnly = 'existing script'; - let pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)); + let pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)) as EntryPointPackageJson; pkg.scripts = {prepublishOnly}; markAsProcessed(pkgUpdater, pkg, COMMON_PACKAGE_PATH, ['fesm2015']); - pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)); - expect(pkg.scripts.prepublishOnly).toContain('This is not allowed'); - expect(pkg.scripts.prepublishOnly__ivy_ngcc_bak).toBe(prepublishOnly); + pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)) as EntryPointPackageJson; + expect(pkg.scripts!.prepublishOnly).toContain('This is not allowed'); + expect(pkg.scripts!.prepublishOnly__ivy_ngcc_bak).toBe(prepublishOnly); }); it(`should not keep backup of overwritten 'prepublishOnly' script`, () => { const COMMON_PACKAGE_PATH = _('/node_modules/@angular/common/package.json'); const fs = getFileSystem(); const pkgUpdater = new DirectPackageJsonUpdater(fs); - let pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)); + let pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)) as EntryPointPackageJson; markAsProcessed(pkgUpdater, pkg, COMMON_PACKAGE_PATH, ['fesm2015']); - pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)); - expect(pkg.scripts.prepublishOnly).toContain('This is not allowed'); - expect(pkg.scripts.prepublishOnly__ivy_ngcc_bak).toBeUndefined(); + pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)) as EntryPointPackageJson; + expect(pkg.scripts!.prepublishOnly).toContain('This is not allowed'); + expect(pkg.scripts!.prepublishOnly__ivy_ngcc_bak).toBeUndefined(); // Running again, now that there is `prepublishOnly` script (created by `ngcc`), it should // still not back it up as `prepublishOnly__ivy_ngcc_bak`. markAsProcessed(pkgUpdater, pkg, COMMON_PACKAGE_PATH, ['fesm2015']); - pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)); - expect(pkg.scripts.prepublishOnly).toContain('This is not allowed'); - expect(pkg.scripts.prepublishOnly__ivy_ngcc_bak).toBeUndefined(); + pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH)) as EntryPointPackageJson; + expect(pkg.scripts!.prepublishOnly).toContain('This is not allowed'); + expect(pkg.scripts!.prepublishOnly__ivy_ngcc_bak).toBeUndefined(); }); }); diff --git a/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts b/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts index d8227f6443..fe227ea6f3 100644 --- a/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts +++ b/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts @@ -7,15 +7,15 @@ */ import {createHash} from 'crypto'; -import {absoluteFrom, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, getFileSystem, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {loadTestFiles} from '../../../src/ngtsc/testing'; -import {DEFAULT_NGCC_CONFIG, NgccConfiguration, ProcessLockingConfiguration} from '../../src/packages/configuration'; +import {DEFAULT_NGCC_CONFIG, NgccConfiguration, NgccProjectConfig, ProcessLockingConfiguration, RawNgccPackageConfig} from '../../src/packages/configuration'; runInEachFileSystem(() => { let _Abs: typeof absoluteFrom; - let fs: FileSystem; + let fs: ReadonlyFileSystem; beforeEach(() => { _Abs = absoluteFrom; @@ -567,7 +567,9 @@ runInEachFileSystem(() => { entryPoints: {'./default-level-entry-point': {}}, }; }); - afterEach(() => DEFAULT_NGCC_CONFIG.packages = JSON.parse(originalDefaultConfig)); + afterEach( + () => DEFAULT_NGCC_CONFIG.packages = + JSON.parse(originalDefaultConfig) as NgccProjectConfig['packages']); it('should return configuration for a package found in the default config', () => { const readFileSpy = spyOn(fs, 'readFile').and.callThrough(); 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 32fed41221..adec4b45fc 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 @@ -8,6 +8,7 @@ import {absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {loadTestFiles} from '../../../src/ngtsc/testing'; +import {DtsProcessing} from '../../src/execution/tasks/api'; import {EntryPoint} from '../../src/packages/entry_point'; import {makeEntryPointBundle} from '../../src/packages/entry_point_bundle'; import {createModuleResolutionCache, SharedFileCache} from '../../src/packages/source_file_cache'; @@ -184,7 +185,7 @@ runInEachFileSystem(() => { const moduleResolutionCache = createModuleResolutionCache(fs); const esm5bundle = makeEntryPointBundle( fs, entryPoint, new SharedFileCache(fs), moduleResolutionCache, './index.js', false, - 'esm5', true); + 'esm5', DtsProcessing.Yes); expect(esm5bundle.src.program.getSourceFiles().map(sf => sf.fileName)) .toEqual(jasmine.arrayWithExactContents([ @@ -298,8 +299,7 @@ runInEachFileSystem(() => { const moduleResolutionCache = createModuleResolutionCache(fs); const esm5bundle = makeEntryPointBundle( fs, entryPoint, new SharedFileCache(fs), moduleResolutionCache, './index.js', false, - 'esm5', - /* transformDts */ true, + 'esm5', DtsProcessing.Yes, /* pathMappings */ undefined, /* mirrorDtsFromSrc */ true); expect(esm5bundle.src.program.getSourceFiles().map(sf => _(sf.fileName))) @@ -338,8 +338,7 @@ runInEachFileSystem(() => { const moduleResolutionCache = createModuleResolutionCache(fs); const esm5bundle = makeEntryPointBundle( fs, entryPoint, new SharedFileCache(fs), moduleResolutionCache, './index.js', false, - 'esm5', - /* transformDts */ true, + 'esm5', DtsProcessing.Yes, /* pathMappings */ undefined, /* mirrorDtsFromSrc */ true); expect(esm5bundle.src.program.getSourceFiles().map(sf => sf.fileName)) .toContain(absoluteFrom('/node_modules/test/internal.js')); @@ -364,8 +363,7 @@ runInEachFileSystem(() => { const moduleResolutionCache = createModuleResolutionCache(fs); const esm5bundle = makeEntryPointBundle( fs, entryPoint, new SharedFileCache(fs), moduleResolutionCache, - './esm2015/index.js', false, 'esm2015', - /* transformDts */ true, + './esm2015/index.js', false, 'esm2015', DtsProcessing.Yes, /* pathMappings */ undefined, /* mirrorDtsFromSrc */ true); expect(esm5bundle.src.program.getSourceFiles().map(sf => sf.fileName)) .toContain(absoluteFrom('/node_modules/internal/esm2015/src/internal.js')); @@ -390,8 +388,7 @@ runInEachFileSystem(() => { const moduleResolutionCache = createModuleResolutionCache(fs); const esm5bundle = makeEntryPointBundle( fs, entryPoint, new SharedFileCache(fs), moduleResolutionCache, './index.js', false, - 'esm5', - /* transformDts */ true, + 'esm5', DtsProcessing.Yes, /* pathMappings */ undefined, /* mirrorDtsFromSrc */ false); expect(esm5bundle.src.program.getSourceFiles().map(sf => sf.fileName)) .toContain(absoluteFrom('/node_modules/test/internal.js')); @@ -417,8 +414,7 @@ runInEachFileSystem(() => { const moduleResolutionCache = createModuleResolutionCache(fs); const bundle = makeEntryPointBundle( fs, entryPoint, new SharedFileCache(fs), moduleResolutionCache, './index.js', false, - 'esm2015', - /* transformDts */ true, + 'esm2015', DtsProcessing.Yes, /* pathMappings */ undefined, /* mirrorDtsFromSrc */ true); expect(bundle.rootDirs).toEqual([absoluteFrom('/node_modules/primary')]); }); 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 95b2cb3a20..9a608c15f1 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 @@ -225,7 +225,7 @@ runInEachFileSystem(() => { spyOn(config, 'getPackageConfig') .and.returnValue( - new ProcessedNgccPackageConfig(_Abs('/project/node_modules/some_package'), { + new ProcessedNgccPackageConfig(fs, _Abs('/project/node_modules/some_package'), { entryPoints: { './ignored_entry_point': {ignore: true}, }, @@ -265,16 +265,18 @@ runInEachFileSystem(() => { it('should write the ngcc version', () => { fs.writeFile(_Abs('/project/yarn.lock'), 'LOCK FILE CONTENTS'); manifest.writeEntryPointManifest(_Abs('/project/node_modules'), []); - const file: EntryPointManifestFile = - JSON.parse(fs.readFile(_Abs('/project/node_modules/__ngcc_entry_points__.json'))); + const file = + JSON.parse(fs.readFile(_Abs('/project/node_modules/__ngcc_entry_points__.json'))) as + EntryPointManifestFile; expect(file.ngccVersion).toEqual(NGCC_VERSION); }); it('should write a hash of the yarn.lock file', () => { fs.writeFile(_Abs('/project/yarn.lock'), 'LOCK FILE CONTENTS'); manifest.writeEntryPointManifest(_Abs('/project/node_modules'), []); - const file: EntryPointManifestFile = - JSON.parse(fs.readFile(_Abs('/project/node_modules/__ngcc_entry_points__.json'))); + const file = + JSON.parse(fs.readFile(_Abs('/project/node_modules/__ngcc_entry_points__.json'))) as + EntryPointManifestFile; expect(file.lockFileHash) .toEqual(createHash('md5').update('LOCK FILE CONTENTS').digest('hex')); }); @@ -282,8 +284,9 @@ runInEachFileSystem(() => { it('should write a hash of the package-lock.json file', () => { fs.writeFile(_Abs('/project/package-lock.json'), 'LOCK FILE CONTENTS'); manifest.writeEntryPointManifest(_Abs('/project/node_modules'), []); - const file: EntryPointManifestFile = - JSON.parse(fs.readFile(_Abs('/project/node_modules/__ngcc_entry_points__.json'))); + const file = + JSON.parse(fs.readFile(_Abs('/project/node_modules/__ngcc_entry_points__.json'))) as + EntryPointManifestFile; expect(file.lockFileHash) .toEqual(createHash('md5').update('LOCK FILE CONTENTS').digest('hex')); }); @@ -291,8 +294,9 @@ runInEachFileSystem(() => { it('should write a hash of the project config', () => { fs.writeFile(_Abs('/project/package-lock.json'), 'LOCK FILE CONTENTS'); manifest.writeEntryPointManifest(_Abs('/project/node_modules'), []); - const file: EntryPointManifestFile = - JSON.parse(fs.readFile(_Abs('/project/node_modules/__ngcc_entry_points__.json'))); + const file = + JSON.parse(fs.readFile(_Abs('/project/node_modules/__ngcc_entry_points__.json'))) as + EntryPointManifestFile; expect(file.configFileHash).toEqual(config.hash); }); @@ -329,8 +333,9 @@ runInEachFileSystem(() => { } }; manifest.writeEntryPointManifest(_Abs('/project/node_modules'), [entryPoint1, entryPoint2]); - const file: EntryPointManifestFile = - JSON.parse(fs.readFile(_Abs('/project/node_modules/__ngcc_entry_points__.json'))); + const file = + JSON.parse(fs.readFile(_Abs('/project/node_modules/__ngcc_entry_points__.json'))) as + EntryPointManifestFile; expect(file.entryPointPaths).toEqual([ [ 'package-1', 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 5e218b671d..db9ec15c24 100644 --- a/packages/compiler-cli/ngcc/test/packages/entry_point_spec.ts +++ b/packages/compiler-cli/ngcc/test/packages/entry_point_spec.ts @@ -6,18 +6,18 @@ * found in the LICENSE file at https://angular.io/license */ -import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, join, relative} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, getFileSystem, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; import {loadTestFiles} from '../../../src/ngtsc/testing'; import {NgccConfiguration, ProcessedNgccPackageConfig} from '../../src/packages/configuration'; -import {EntryPoint, EntryPointJsonProperty, getEntryPointFormat, getEntryPointInfo, IGNORED_ENTRY_POINT, INCOMPATIBLE_ENTRY_POINT, isEntryPoint, NO_ENTRY_POINT, SUPPORTED_FORMAT_PROPERTIES} from '../../src/packages/entry_point'; +import {EntryPoint, EntryPointJsonProperty, EntryPointPackageJson, getEntryPointFormat, getEntryPointInfo, IGNORED_ENTRY_POINT, INCOMPATIBLE_ENTRY_POINT, isEntryPoint, NO_ENTRY_POINT, SUPPORTED_FORMAT_PROPERTIES} from '../../src/packages/entry_point'; runInEachFileSystem(() => { describe('getEntryPointInfo()', () => { let SOME_PACKAGE: AbsoluteFsPath; let _: typeof absoluteFrom; - let fs: FileSystem; + let fs: ReadonlyFileSystem; beforeEach(() => { _ = absoluteFrom; @@ -71,7 +71,7 @@ runInEachFileSystem(() => { const config = new NgccConfiguration(fs, _('/project')); spyOn(config, 'getPackageConfig') .and.returnValue(new ProcessedNgccPackageConfig( - _('/project/node_modules/some_package'), + fs, _('/project/node_modules/some_package'), {entryPoints: {'./valid_entry_point': {ignore: true}}})); const entryPoint = getEntryPointInfo( fs, config, new MockLogger(), SOME_PACKAGE, @@ -80,7 +80,7 @@ runInEachFileSystem(() => { }); it('should retrieve the entry-point\'s version from the package\'s `package.json`', () => { - const entryPointPath = join(SOME_PACKAGE, 'valid_entry_point'); + const entryPointPath = fs.join(SOME_PACKAGE, 'valid_entry_point'); loadTestFiles([ { @@ -102,28 +102,28 @@ runInEachFileSystem(() => { `, }, { - name: join(SOME_PACKAGE, 'package.json'), + name: fs.join(SOME_PACKAGE, 'package.json'), contents: createPackageJson('', {version: '1.0.0'}), }, { - name: join(entryPointPath, 'package.json'), + name: fs.join(entryPointPath, 'package.json'), contents: createPackageJson('valid_entry_point', {version: '2.0.0'}), }, { - name: join(entryPointPath, 'valid_entry_point.metadata.json'), + name: fs.join(entryPointPath, 'valid_entry_point.metadata.json'), contents: 'some meta data', }, ]); const config = new NgccConfiguration(fs, _('/project')); - const info: EntryPoint = - getEntryPointInfo(fs, config, new MockLogger(), SOME_PACKAGE, entryPointPath) as any; + const info = getEntryPointInfo(fs, config, new MockLogger(), SOME_PACKAGE, entryPointPath) as + EntryPoint; expect(info.packageJson).toEqual(jasmine.objectContaining({packageVersion: '1'})); }); it('should use `null` for version if it cannot be retrieved from a `package.json`', () => { - const entryPointPath = join(SOME_PACKAGE, 'valid_entry_point'); + const entryPointPath = fs.join(SOME_PACKAGE, 'valid_entry_point'); loadTestFiles([ { @@ -145,22 +145,22 @@ runInEachFileSystem(() => { `, }, { - name: join(SOME_PACKAGE, 'package.json'), + name: fs.join(SOME_PACKAGE, 'package.json'), contents: createPackageJson(''), }, { - name: join(entryPointPath, 'package.json'), + name: fs.join(entryPointPath, 'package.json'), contents: createPackageJson('valid_entry_point'), }, { - name: join(entryPointPath, 'valid_entry_point.metadata.json'), + name: fs.join(entryPointPath, 'valid_entry_point.metadata.json'), contents: 'some meta data', }, ]); const config = new NgccConfiguration(fs, _('/project')); - const info: EntryPoint = - getEntryPointInfo(fs, config, new MockLogger(), SOME_PACKAGE, entryPointPath) as any; + const info = getEntryPointInfo(fs, config, new MockLogger(), SOME_PACKAGE, entryPointPath) as + EntryPoint; expect(info.packageJson).toEqual(jasmine.objectContaining({packageVersion: '3'})); }); @@ -184,7 +184,7 @@ runInEachFileSystem(() => { }; spyOn(config, 'getPackageConfig') .and.returnValue(new ProcessedNgccPackageConfig( - _('/project/node_modules/some_package'), + fs, _('/project/node_modules/some_package'), {entryPoints: {'./valid_entry_point': {override}}})); const entryPoint = getEntryPointInfo( fs, config, new MockLogger(), SOME_PACKAGE, @@ -233,10 +233,11 @@ runInEachFileSystem(() => { ]); const config = new NgccConfiguration(fs, _('/project')); const override = - JSON.parse(createPackageJson('missing_package_json', {excludes: ['name']})); + JSON.parse(createPackageJson('missing_package_json', {excludes: ['name']})) as + Partial; spyOn(config, 'getPackageConfig') .and.returnValue(new ProcessedNgccPackageConfig( - _('/project/node_modules/some_package/'), + fs, _('/project/node_modules/some_package/'), {entryPoints: {'./missing_package_json': {override}}})); const entryPoint = getEntryPointInfo( fs, config, new MockLogger(), SOME_PACKAGE, @@ -273,7 +274,7 @@ runInEachFileSystem(() => { // Ensure a `package.json` exists for the entry-point (containing `entryPointName`). loadTestFiles([ { - name: join(entryPointPath, 'package.json'), + name: fs.join(entryPointPath, 'package.json'), contents: JSON.stringify({name: entryPointName, typings: './index.d.ts'}), }, ]); @@ -284,11 +285,11 @@ runInEachFileSystem(() => { // avoid returning `INCOMPATIBLE_ENTRY_POINT` (since there is no `package.json`). loadTestFiles([ { - name: join(packagePath, 'ngcc.config.js'), + name: fs.join(packagePath, 'ngcc.config.js'), contents: ` module.exports = { entryPoints: { - '${relative(packagePath, entryPointPath)}': { + '${fs.relative(packagePath, entryPointPath)}': { override: {typings: './index.d.ts'}, }, }, @@ -321,7 +322,7 @@ runInEachFileSystem(() => { it('for a secondary entry-point with a `package.json`', () => { const packagePath = _(`/project/node_modules/${nameWithScope('on-disk-package-name')}`); - const entryPointPath = join(packagePath, 'some-entry-point'); + const entryPointPath = fs.join(packagePath, 'some-entry-point'); const expectedPackageName = nameWithScope('package-json-package-name'); setUpPackageWithEntryPointPackageJson( @@ -332,7 +333,7 @@ runInEachFileSystem(() => { it('for a secondary entry-point without a `package.json`', () => { const packagePath = _(`/project/node_modules/${nameWithScope('on-disk-package-name')}`); - const entryPointPath = join(packagePath, 'some-entry-point'); + const entryPointPath = fs.join(packagePath, 'some-entry-point'); const expectedPackageName = nameWithScope('on-disk-package-name'); setUpPackageWithoutEntryPointPackageJson(packagePath, entryPointPath); @@ -354,7 +355,7 @@ runInEachFileSystem(() => { it('for a secondary entry-point without a `package.json` in nested `node_modules/`', () => { const packagePath = _(`/project/node_modules/other-package/node_modules/${ nameWithScope('on-disk-package-name')}`); - const entryPointPath = join(packagePath, 'some-entry-point'); + const entryPointPath = fs.join(packagePath, 'some-entry-point'); const expectedPackageName = nameWithScope('on-disk-package-name'); setUpPackageWithoutEntryPointPackageJson(packagePath, entryPointPath); @@ -384,7 +385,7 @@ runInEachFileSystem(() => { it('for a secondary entry-point with a `package.json` outside `node_modules/`', () => { const packagePath = _(`/project/libs/${nameWithScope('on-disk-package-name')}`); - const entryPointPath = join(packagePath, 'some-entry-point'); + const entryPointPath = fs.join(packagePath, 'some-entry-point'); const expectedPackageName = nameWithScope('package-json-package-name'); setUpPackageWithEntryPointPackageJson(expectedPackageName, entryPointPath); @@ -394,7 +395,7 @@ runInEachFileSystem(() => { it('for a secondary entry-point without a `package.json` outside `node_modules/`', () => { const packagePath = _(`/project/libs/${nameWithScope('on-disk-package-name')}`); - const entryPointPath = join(packagePath, 'some-entry-point'); + const entryPointPath = fs.join(packagePath, 'some-entry-point'); const expectedPackageName = nameWithScope('on-disk-package-name'); setUpPackageWithoutEntryPointPackageJson(packagePath, entryPointPath); @@ -522,7 +523,7 @@ runInEachFileSystem(() => { const config = new NgccConfiguration(fs, _('/project')); spyOn(config, 'getPackageConfig') .and.returnValue(new ProcessedNgccPackageConfig( - _('/project/node_modules/some_package'), + fs, _('/project/node_modules/some_package'), {entryPoints: {'./missing_metadata': {}}})); const entryPoint = getEntryPointInfo( fs, config, new MockLogger(), SOME_PACKAGE, @@ -626,7 +627,7 @@ runInEachFileSystem(() => { describe('getEntryPointFormat()', () => { let SOME_PACKAGE: AbsoluteFsPath; let _: typeof absoluteFrom; - let fs: FileSystem; + let fs: ReadonlyFileSystem; let entryPoint: EntryPoint; beforeEach(() => { @@ -762,7 +763,7 @@ export function createPackageJson( typingsIsArray?: boolean, version?: string } = {}): string { - const packageJson: any = { + const packageJson: EntryPointPackageJson = { name: (entryPointName === '') ? 'some_package' : `some_package/${entryPointName}`, version, [typingsProp]: typingsIsArray ? [`./${entryPointName}.d.ts`] : `./${entryPointName}.d.ts`, @@ -781,6 +782,7 @@ export function createPackageJson( return JSON.stringify(packageJson); } -export function loadPackageJson(fs: FileSystem, packagePath: string) { - return JSON.parse(fs.readFile(fs.resolve(packagePath + '/package.json'))); +export function loadPackageJson(fs: ReadonlyFileSystem, packagePath: string) { + return JSON.parse(fs.readFile(fs.resolve(packagePath + '/package.json'))) as + EntryPointPackageJson; } 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 495ff9f2fc..6d3cf14b68 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 @@ -157,7 +157,7 @@ exports.D = D; new DecorationAnalyzer(fs, bundle, host, referencesRegistry).analyzeProgram(); const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host, bundle.entryPoint.packagePath) .analyzeProgram(bundle.src.program); - const renderer = new CommonJsRenderingFormatter(host, false); + const renderer = new CommonJsRenderingFormatter(fs, host, false); const importManager = new ImportManager(new NoopImportRewriter(), 'i'); return { host, @@ -177,8 +177,8 @@ exports.D = D; renderer.addImports( output, [ - {specifier: '@angular/core', qualifier: 'i0'}, - {specifier: '@angular/common', qualifier: 'i1'} + {specifier: '@angular/core', qualifier: ts.createIdentifier('i0')}, + {specifier: '@angular/common', qualifier: ts.createIdentifier('i1')} ], sourceFile); expect(output.toString()).toContain(`/* A copyright notice */ @@ -257,7 +257,8 @@ var A = (function() {`); const file = getSourceFileOrError(program, _('/node_modules/test-package/some/file.js')); const output = new MagicString(PROGRAM.contents); renderer.addConstants(output, 'var x = 3;', file); - renderer.addImports(output, [{specifier: '@angular/core', qualifier: 'i0'}], file); + renderer.addImports( + output, [{specifier: '@angular/core', qualifier: ts.createIdentifier('i0')}], file); expect(output.toString()).toContain(` var core = require('@angular/core'); var i0 = require('@angular/core'); 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 1577026df6..44e4160faa 100644 --- a/packages/compiler-cli/ngcc/test/rendering/dts_renderer_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/dts_renderer_spec.ts @@ -141,7 +141,7 @@ runInEachFileSystem(() => { 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;\n static ɵdir: ɵngcc0.ɵɵDirectiveDefWithMeta'); + 'foo(x: number): number;\n static ɵfac: ɵngcc0.ɵɵFactoryDeclaration;\n static ɵdir: ɵngcc0.ɵɵDirectiveDeclaration'); }); it('should render imports into typings files', () => { 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 039640a149..8da3f9323b 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 @@ -34,7 +34,7 @@ function setup(file: {name: AbsoluteFsPath, contents: string}) { new DecorationAnalyzer(fs, bundle, host, referencesRegistry).analyzeProgram(); const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host, bundle.entryPoint.packagePath) .analyzeProgram(bundle.src.program); - const renderer = new Esm5RenderingFormatter(host, false); + const renderer = new Esm5RenderingFormatter(fs, host, false); const importManager = new ImportManager(new NoopImportRewriter(), IMPORT_PREFIX); return { host, @@ -188,8 +188,8 @@ export { F }; renderer.addImports( output, [ - {specifier: '@angular/core', qualifier: 'i0'}, - {specifier: '@angular/common', qualifier: 'i1'} + {specifier: '@angular/core', qualifier: ts.createIdentifier('i0')}, + {specifier: '@angular/common', qualifier: ts.createIdentifier('i1')} ], sourceFile); expect(output.toString()).toContain(`/* A copyright notice */ @@ -263,7 +263,8 @@ var A = (function() {`); const file = getSourceFileOrError(program, _('/node_modules/test-package/some/file.js')); const output = new MagicString(PROGRAM.contents); renderer.addConstants(output, 'var x = 3;', file); - renderer.addImports(output, [{specifier: '@angular/core', qualifier: 'i0'}], file); + renderer.addImports( + output, [{specifier: '@angular/core', qualifier: ts.createIdentifier('i0')}], file); expect(output.toString()).toContain(` import {Directive} from '@angular/core'; import * as i0 from '@angular/core'; 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 0ac6299d7d..0f7749108e 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 @@ -40,7 +40,7 @@ function setup(files: TestFile[], dtsFiles?: TestFile[]) { new DecorationAnalyzer(fs, bundle, host, referencesRegistry).analyzeProgram(); const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host, bundle.entryPoint.packagePath) .analyzeProgram(bundle.src.program); - const renderer = new EsmRenderingFormatter(host, false); + const renderer = new EsmRenderingFormatter(fs, host, false); const importManager = new ImportManager(new NoopImportRewriter(), IMPORT_PREFIX); return { host, @@ -156,8 +156,8 @@ runInEachFileSystem(() => { renderer.addImports( output, [ - {specifier: '@angular/core', qualifier: 'i0'}, - {specifier: '@angular/common', qualifier: 'i1'} + {specifier: '@angular/core', qualifier: ts.createIdentifier('i0')}, + {specifier: '@angular/common', qualifier: ts.createIdentifier('i1')} ], sourceFile); expect(output.toString()).toContain(`/* A copyright notice */ @@ -231,7 +231,8 @@ const x = 3; const file = getSourceFileOrError(program, _('/node_modules/test-package/some/file.js')); const output = new MagicString(PROGRAM.contents); renderer.addConstants(output, 'const x = 3;', file); - renderer.addImports(output, [{specifier: '@angular/core', qualifier: 'i0'}], file); + renderer.addImports( + output, [{specifier: '@angular/core', qualifier: ts.createIdentifier('i0')}], file); expect(output.toString()).toContain(` import {Directive} from '@angular/core'; import * as i0 from '@angular/core'; diff --git a/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts b/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts index 98e7a04856..5ea2e0ce07 100644 --- a/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts @@ -66,7 +66,7 @@ class TestRenderingFormatter implements RenderingFormatter { printStatement(stmt: Statement, sourceFile: ts.SourceFile, importManager: ImportManager): string { const node = translateStatement( stmt, importManager, - {downlevelLocalizedStrings: this.isEs5, downlevelVariableDeclarations: this.isEs5}); + {downlevelTaggedTemplates: this.isEs5, downlevelVariableDeclarations: this.isEs5}); const code = this.printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); return `// TRANSPILED\n${code}`; @@ -246,7 +246,7 @@ runInEachFileSystem(() => { expect(addDefinitionsSpy.calls.first().args[2]).toEqual(`// TRANSPILED A.ɵfac = function A_Factory(t) { return new (t || A)(); }; // TRANSPILED -A.ɵcmp = ɵngcc0.ɵɵdefineComponent({ type: A, selectors: [["a"]], decls: 1, vars: 1, template: function A_Template(rf, ctx) { if (rf & 1) { +A.ɵcmp = /*@__PURE__*/ ɵngcc0.ɵɵdefineComponent({ type: A, selectors: [["a"]], decls: 1, vars: 1, template: function A_Template(rf, ctx) { if (rf & 1) { ɵngcc0.ɵɵtext(0); } if (rf & 2) { ɵngcc0.ɵɵtextInterpolate(ctx.person.name); @@ -254,7 +254,7 @@ A.ɵcmp = ɵngcc0.ɵɵdefineComponent({ type: A, selectors: [["a"]], decls: 1, v const addAdjacentStatementsSpy = testFormatter.addAdjacentStatements as jasmine.Spy; expect(addAdjacentStatementsSpy.calls.first().args[2]).toEqual(`// TRANSPILED -/*@__PURE__*/ (function () { ɵngcc0.ɵsetClassMetadata(A, [{ +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && ɵngcc0.ɵsetClassMetadata(A, [{ type: Component, args: [{ selector: 'a', template: '{{ person!.name }}' }] }], null, null); })();`); @@ -276,7 +276,7 @@ A.ɵcmp = ɵngcc0.ɵɵdefineComponent({ type: A, selectors: [["a"]], decls: 1, v const addImportsSpy = testFormatter.addImports as jasmine.Spy; expect(addImportsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS); expect(addImportsSpy.calls.first().args[1]).toEqual([ - {specifier: '@angular/core', qualifier: 'ɵngcc0'} + {specifier: '@angular/core', qualifier: jasmine.objectContaining({text: 'ɵngcc0'})} ]); }); @@ -300,7 +300,7 @@ A.ɵcmp = ɵngcc0.ɵɵdefineComponent({ type: A, selectors: [["a"]], decls: 1, v expect(addDefinitionsSpy.calls.first().args[2]).toEqual(`// TRANSPILED A.ɵfac = function A_Factory(t) { return new (t || A)(); }; // TRANSPILED -A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });`); +A.ɵdir = /*@__PURE__*/ ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });`); }); it('should call addAdjacentStatements with the source code, the analyzed class and the rendered statements', @@ -321,7 +321,7 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });` .toEqual(jasmine.objectContaining( {name: 'A', decorators: [jasmine.objectContaining({name: 'Directive'})]})); expect(addAdjacentStatementsSpy.calls.first().args[2]).toEqual(`// TRANSPILED -/*@__PURE__*/ (function () { ɵngcc0.ɵsetClassMetadata(A, [{ +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && ɵngcc0.ɵsetClassMetadata(A, [{ type: Directive, args: [{ selector: '[a]' }] }], null, null); })();`); @@ -368,8 +368,8 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });` decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addDefinitionsSpy = testFormatter.addDefinitions as jasmine.Spy; const definitions: string = addDefinitionsSpy.calls.first().args[2]; - expect(definitions).toContain('A.ɵmod = ɵngcc0.ɵɵdefineNgModule('); - expect(definitions).toContain('A.ɵinj = ɵngcc0.ɵɵdefineInjector('); + expect(definitions).toContain('A.ɵmod = /*@__PURE__*/ ɵngcc0.ɵɵdefineNgModule('); + expect(definitions).toContain('A.ɵinj = /*@__PURE__*/ ɵngcc0.ɵɵdefineInjector('); }); it('should render adjacent statements', () => { @@ -569,8 +569,8 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });` expect(addDefinitionsSpy.calls.first().args[2]).toEqual(`// TRANSPILED UndecoratedBase.ɵfac = function UndecoratedBase_Factory(t) { return new (t || UndecoratedBase)(); }; // TRANSPILED -UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, viewQuery: function UndecoratedBase_Query(rf, ctx) { if (rf & 1) { - ɵngcc0.ɵɵstaticViewQuery(_c0, true); +UndecoratedBase.ɵdir = /*@__PURE__*/ ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, viewQuery: function UndecoratedBase_Query(rf, ctx) { if (rf & 1) { + ɵngcc0.ɵɵviewQuery(_c0, 7); } if (rf & 2) { let _t; ɵngcc0.ɵɵqueryRefresh(_t = ɵngcc0.ɵɵloadQuery()) && (ctx.test = _t.first); @@ -641,7 +641,8 @@ UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, vie expect(sourceFile.contents) .toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('file.js.map')); expect(mapFile.path).toEqual(_('/node_modules/test-package/src/file.js.map')); - expect(JSON.parse(mapFile.contents)).toEqual(MERGED_OUTPUT_PROGRAM_MAP.toObject()); + expect(JSON.parse(mapFile.contents) as any) + .toEqual(MERGED_OUTPUT_PROGRAM_MAP.toObject()); }); @@ -688,10 +689,11 @@ UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, vie decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addAdjacentStatementsSpy = testFormatter.addAdjacentStatements as jasmine.Spy; expect(addAdjacentStatementsSpy.calls.first().args[2]) - .toContain(`/*@__PURE__*/ (function () { ɵngcc0.setClassMetadata(`); + .toContain( + `function () { (typeof ngDevMode === "undefined" || ngDevMode) && ɵngcc0.setClassMetadata(`); const addImportsSpy = testFormatter.addImports as jasmine.Spy; expect(addImportsSpy.calls.first().args[1]).toEqual([ - {specifier: './r3_symbols', qualifier: 'ɵngcc0'} + {specifier: './r3_symbols', qualifier: jasmine.objectContaining({text: 'ɵngcc0'})} ]); }); @@ -713,7 +715,8 @@ UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, vie decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addAdjacentStatementsSpy = testFormatter.addAdjacentStatements as jasmine.Spy; expect(addAdjacentStatementsSpy.calls.first().args[2]) - .toContain(`/*@__PURE__*/ (function () { setClassMetadata(`); + .toContain( + `function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(`); const addImportsSpy = testFormatter.addImports as jasmine.Spy; expect(addImportsSpy.calls.first().args[1]).toEqual([]); }); 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 a667fa7bde..9872271c37 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 @@ -34,7 +34,7 @@ function setup(file: TestFile) { new DecorationAnalyzer(fs, bundle, host, referencesRegistry).analyzeProgram(); const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host, bundle.entryPoint.packagePath).analyzeProgram(src.program); - const renderer = new UmdRenderingFormatter(host, false); + const renderer = new UmdRenderingFormatter(fs, host, false); const importManager = new ImportManager(new NoopImportRewriter(), 'i'); return { decorationAnalyses, @@ -200,8 +200,8 @@ typeof define === 'function' && define.amd ? define('file', ['exports','/tslib', renderer.addImports( output, [ - {specifier: '@angular/core', qualifier: 'i0'}, - {specifier: '@angular/common', qualifier: 'i1'} + {specifier: '@angular/core', qualifier: ts.createIdentifier('i0')}, + {specifier: '@angular/common', qualifier: ts.createIdentifier('i1')} ], file); expect(output.toString()) @@ -217,8 +217,8 @@ typeof define === 'function' && define.amd ? define('file', ['exports','/tslib', renderer.addImports( output, [ - {specifier: '@angular/core', qualifier: 'i0'}, - {specifier: '@angular/common', qualifier: 'i1'} + {specifier: '@angular/core', qualifier: ts.createIdentifier('i0')}, + {specifier: '@angular/common', qualifier: ts.createIdentifier('i1')} ], file); expect(output.toString()) @@ -233,8 +233,8 @@ typeof define === 'function' && define.amd ? define('file', ['exports','/tslib', renderer.addImports( output, [ - {specifier: '@angular/core', qualifier: 'i0'}, - {specifier: '@angular/common', qualifier: 'i1'} + {specifier: '@angular/core', qualifier: ts.createIdentifier('i0')}, + {specifier: '@angular/common', qualifier: ts.createIdentifier('i1')} ], file); expect(output.toString()) @@ -249,10 +249,12 @@ typeof define === 'function' && define.amd ? define('file', ['exports','/tslib', renderer.addImports( output, [ - {specifier: '@ngrx/store', qualifier: 'i0'}, - {specifier: '@angular/platform-browser-dynamic', qualifier: 'i1'}, - {specifier: '@angular/common/testing', qualifier: 'i2'}, - {specifier: '@angular-foo/package', qualifier: 'i3'} + {specifier: '@ngrx/store', qualifier: ts.createIdentifier('i0')}, { + specifier: '@angular/platform-browser-dynamic', + qualifier: ts.createIdentifier('i1') + }, + {specifier: '@angular/common/testing', qualifier: ts.createIdentifier('i2')}, + {specifier: '@angular-foo/package', qualifier: ts.createIdentifier('i3')} ], file); expect(output.toString()) @@ -270,8 +272,8 @@ typeof define === 'function' && define.amd ? define('file', ['exports','/tslib', renderer.addImports( output, [ - {specifier: '@angular/core', qualifier: 'i0'}, - {specifier: '@angular/common', qualifier: 'i1'} + {specifier: '@angular/core', qualifier: ts.createIdentifier('i0')}, + {specifier: '@angular/common', qualifier: ts.createIdentifier('i1')} ], file); expect(output.toString()) @@ -287,8 +289,8 @@ typeof define === 'function' && define.amd ? define('file', ['exports','/tslib', renderer.addImports( output, [ - {specifier: '@angular/core', qualifier: 'i0'}, - {specifier: '@angular/common', qualifier: 'i1'} + {specifier: '@angular/core', qualifier: ts.createIdentifier('i0')}, + {specifier: '@angular/common', qualifier: ts.createIdentifier('i1')} ], file); expect(output.toString()) @@ -315,8 +317,8 @@ typeof define === 'function' && define.amd ? define('file', ['exports','/tslib', renderer.addImports( output, [ - {specifier: '@angular/core', qualifier: 'i0'}, - {specifier: '@angular/common', qualifier: 'i1'} + {specifier: '@angular/core', qualifier: ts.createIdentifier('i0')}, + {specifier: '@angular/common', qualifier: ts.createIdentifier('i1')} ], file); const outputSrc = output.toString(); @@ -361,8 +363,8 @@ typeof define === 'function' && define.amd ? define('file', ['exports','/tslib', renderer.addImports( output, [ - {specifier: '@angular/core', qualifier: 'i0'}, - {specifier: '@angular/common', qualifier: 'i1'} + {specifier: '@angular/core', qualifier: ts.createIdentifier('i0')}, + {specifier: '@angular/common', qualifier: ts.createIdentifier('i1')} ], file); const outputSrc = output.toString(); diff --git a/packages/compiler-cli/ngcc/test/utils_spec.ts b/packages/compiler-cli/ngcc/test/utils_spec.ts index 14aa5fbabe..6d288c7497 100644 --- a/packages/compiler-cli/ngcc/test/utils_spec.ts +++ b/packages/compiler-cli/ngcc/test/utils_spec.ts @@ -108,6 +108,22 @@ describe('getTsHelperFnFromDeclaration()', () => { expect(getTsHelperFnFromDeclaration(decl2)).toBe(KnownDeclaration.TsHelperSpreadArrays); }); + it('should recognize the `__spreadArray` helper as function declaration', () => { + const decl1 = createFunctionDeclaration('__spreadArray'); + const decl2 = createFunctionDeclaration('__spreadArray$42'); + + expect(getTsHelperFnFromDeclaration(decl1)).toBe(KnownDeclaration.TsHelperSpreadArray); + expect(getTsHelperFnFromDeclaration(decl2)).toBe(KnownDeclaration.TsHelperSpreadArray); + }); + + it('should recognize the `__spreadArray` helper as variable declaration', () => { + const decl1 = createVariableDeclaration('__spreadArray'); + const decl2 = createVariableDeclaration('__spreadArray$42'); + + expect(getTsHelperFnFromDeclaration(decl1)).toBe(KnownDeclaration.TsHelperSpreadArray); + expect(getTsHelperFnFromDeclaration(decl2)).toBe(KnownDeclaration.TsHelperSpreadArray); + }); + it('should return null for unrecognized helpers', () => { const decl1 = createFunctionDeclaration('__foo'); const decl2 = createVariableDeclaration('spread'); @@ -158,6 +174,14 @@ describe('getTsHelperFnFromIdentifier()', () => { expect(getTsHelperFnFromIdentifier(id2)).toBe(KnownDeclaration.TsHelperSpreadArrays); }); + it('should recognize the `__spreadArray` helper', () => { + const id1 = ts.createIdentifier('__spreadArray'); + const id2 = ts.createIdentifier('__spreadArray$42'); + + expect(getTsHelperFnFromIdentifier(id1)).toBe(KnownDeclaration.TsHelperSpreadArray); + expect(getTsHelperFnFromIdentifier(id2)).toBe(KnownDeclaration.TsHelperSpreadArray); + }); + it('should return null for unrecognized helpers', () => { const id1 = ts.createIdentifier('__foo'); const id2 = ts.createIdentifier('spread'); 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 7fc1ff9de1..8964593af5 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 @@ -47,7 +47,7 @@ runInEachFileSystem(() => { fs.ensureDir(fs.dirname(packageJsonPath)); fs.writeFile(packageJsonPath, JSON.stringify(packageJson)); strategy.clean(packageJsonPath, fs.basename(packageJsonPath)); - const newPackageJson: EntryPointPackageJson = JSON.parse(fs.readFile(packageJsonPath)); + const newPackageJson = JSON.parse(fs.readFile(packageJsonPath)) as EntryPointPackageJson; expect(newPackageJson).toEqual({name: 'test-package'}); }); @@ -60,7 +60,7 @@ runInEachFileSystem(() => { fs.ensureDir(fs.dirname(packageJsonPath)); fs.writeFile(packageJsonPath, JSON.stringify(packageJson)); strategy.clean(packageJsonPath, fs.basename(packageJsonPath)); - const newPackageJson: EntryPointPackageJson = JSON.parse(fs.readFile(packageJsonPath)); + const newPackageJson = JSON.parse(fs.readFile(packageJsonPath)) as EntryPointPackageJson; expect(newPackageJson).toEqual({name: 'test-package'}); }); @@ -73,7 +73,7 @@ runInEachFileSystem(() => { fs.ensureDir(fs.dirname(packageJsonPath)); fs.writeFile(packageJsonPath, JSON.stringify(packageJson)); strategy.clean(packageJsonPath, fs.basename(packageJsonPath)); - const newPackageJson: EntryPointPackageJson = JSON.parse(fs.readFile(packageJsonPath)); + const newPackageJson = JSON.parse(fs.readFile(packageJsonPath)) as EntryPointPackageJson; expect(newPackageJson).toEqual({name: 'test-package'}); }); @@ -87,7 +87,7 @@ runInEachFileSystem(() => { fs.ensureDir(fs.dirname(packageJsonPath)); fs.writeFile(packageJsonPath, JSON.stringify(packageJson)); strategy.clean(packageJsonPath, fs.basename(packageJsonPath)); - const newPackageJson: EntryPointPackageJson = JSON.parse(fs.readFile(packageJsonPath)); + const newPackageJson = JSON.parse(fs.readFile(packageJsonPath)) as EntryPointPackageJson; expect(newPackageJson).toEqual({ name: 'test-package', scripts: {test: 'do testing'}, @@ -109,7 +109,8 @@ runInEachFileSystem(() => { fs.ensureDir(fs.dirname(packageJsonPath)); fs.writeFile(packageJsonPath, JSON.stringify(packageJson)); strategy.clean(packageJsonPath, fs.basename(packageJsonPath)); - const newPackageJson: EntryPointPackageJson = JSON.parse(fs.readFile(packageJsonPath)); + const newPackageJson = + JSON.parse(fs.readFile(packageJsonPath)) as EntryPointPackageJson; expect(newPackageJson).toEqual({ name: 'test-package', scripts: {prepublishOnly: 'original', test: 'do testing'}, @@ -129,7 +130,7 @@ runInEachFileSystem(() => { fs.ensureDir(fs.dirname(packageJsonPath)); fs.writeFile(packageJsonPath, JSON.stringify(packageJson)); strategy.clean(packageJsonPath, fs.basename(packageJsonPath)); - const newPackageJson: EntryPointPackageJson = JSON.parse(fs.readFile(packageJsonPath)); + const newPackageJson = JSON.parse(fs.readFile(packageJsonPath)) as EntryPointPackageJson; expect(newPackageJson).toEqual({ name: 'test-package', scripts: { 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 97748efd08..758f108d3f 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,13 +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 {absoluteFrom, FileSystem, getFileSystem, join} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {MockLogger} from '../../../src/ngtsc/logging/testing'; +import {RawSourceMap} from '../../../src/ngtsc/sourcemaps'; import {loadTestFiles} from '../../../src/ngtsc/testing'; +import {DtsProcessing} from '../../src/execution/tasks/api'; import {NgccConfiguration} from '../../src/packages/configuration'; import {EntryPoint, EntryPointFormat, EntryPointJsonProperty, getEntryPointInfo, isEntryPoint} from '../../src/packages/entry_point'; import {EntryPointBundle, makeEntryPointBundle} from '../../src/packages/entry_point_bundle'; +import {NewEntryPointPropertiesMap} from '../../src/packages/entry_point_manifest'; import {createModuleResolutionCache, SharedFileCache} from '../../src/packages/source_file_cache'; import {FileWriter} from '../../src/writing/file_writer'; import {NewEntryPointFileWriter} from '../../src/writing/new_entry_point_file_writer'; @@ -51,6 +54,12 @@ runInEachFileSystem(() => { {name: _('/node_modules/test/esm5.js'), contents: 'export function FooTop() {}'}, {name: _('/node_modules/test/esm5.js.map'), contents: 'ORIGINAL MAPPING DATA'}, {name: _('/node_modules/test/es2015/index.js'), contents: 'export {FooTop} from "./foo";'}, + { + name: _('/node_modules/test/es2015/index.js.map'), + contents: + '{"version":3,"file":"index.js","sources":["../src/index.ts"],"mappings":"AAAA"}' + }, + {name: _('/node_modules/test/src/index.ts'), contents: 'export {FooTop} from "./foo";'}, {name: _('/node_modules/test/es2015/foo.js'), contents: 'export class FooTop {}'}, { name: _('/node_modules/test/a/package.json'), @@ -154,6 +163,89 @@ runInEachFileSystem(() => { .toEqual('export {FooTop} from "./foo";'); }); + it('should copy any source-map for unmodified files in the program (adding missing sourceRoot)', + () => { + // Ensure source-mapping for a non-processed source file `index.js`. + const sourceMap = { + version: 3, + file: 'index.js', + sources: ['../src/index.ts'], + mappings: 'AAAA', + }; + loadTestFiles([ + { + name: _('/node_modules/test/es2015/index.js.map'), + contents: JSON.stringify(sourceMap) + }, + {name: _('/node_modules/test/src/index.ts'), contents: 'export {FooTop} from "./foo";'} + ]); + + // Simulate that only the `foo.js` file was modified + const modifiedFiles = [{ + path: _('/node_modules/test/es2015/foo.js'), + contents: 'export class FooTop {} // MODIFIED' + }]; + fileWriter.writeBundle(esm2015bundle, modifiedFiles, ['es2015']); + + expect( + JSON.parse(fs.readFile(_('/node_modules/test/__ivy_ngcc__/es2015/index.js.map'))) as + Partial) + .toEqual({...sourceMap, sourceRoot: '../../es2015'}); + }); + + it('should copy any source-map for unmodified files in the program (updating sourceRoot)', + () => { + // Ensure source-mapping for a non-processed source file `index.js`. + const sourceMap = { + version: 3, + file: 'index.js', + sourceRoot: '../src', + sources: ['index.ts'], + mappings: 'AAAA', + }; + loadTestFiles([ + { + name: _('/node_modules/test/es2015/index.js.map'), + contents: JSON.stringify(sourceMap) + }, + {name: _('/node_modules/test/src/index.ts'), contents: 'export {FooTop} from "./foo";'} + ]); + + // Simulate that only the `foo.js` file was modified + const modifiedFiles = [{ + path: _('/node_modules/test/es2015/foo.js'), + contents: 'export class FooTop {} // MODIFIED' + }]; + fileWriter.writeBundle(esm2015bundle, modifiedFiles, ['es2015']); + + expect( + JSON.parse(fs.readFile(_('/node_modules/test/__ivy_ngcc__/es2015/index.js.map'))) as + Partial) + .toEqual({...sourceMap, sourceRoot: '../../src'}); + }); + + it('should ignore (with a warning) any invalid source-map for unmodified files in the program', + () => { + // Ensure source-mapping for a non-processed source file `index.js`. + loadTestFiles([ + {name: _('/node_modules/test/es2015/index.js.map'), contents: 'INVALID JSON STRING'}, + {name: _('/node_modules/test/src/index.ts'), contents: 'export {FooTop} from "./foo";'} + ]); + + // Simulate that only the `foo.js` file was modified + const modifiedFiles = [{ + path: _('/node_modules/test/es2015/foo.js'), + contents: 'export class FooTop {} // MODIFIED' + }]; + fileWriter.writeBundle(esm2015bundle, modifiedFiles, ['es2015']); + + expect(fs.exists(_('/node_modules/test/__ivy_ngcc__/es2015/index.js.map'))).toBe(false); + expect(logger.logs.warn).toEqual([ + [`Failed to process source-map at ${_('/node_modules/test/es2015/index.js.map')}`], + ['Unexpected token I in JSON at position 0'], + ]); + }); + it('should update the package.json properties', () => { fileWriter.writeBundle( esm5bundle, @@ -578,7 +670,7 @@ runInEachFileSystem(() => { it('should revert changes to `package.json`', () => { const entryPoint = esm5bundle.entryPoint; - const packageJsonPath = join(entryPoint.packagePath, 'package.json'); + const packageJsonPath = fs.join(entryPoint.packagePath, 'package.json'); fileWriter.writeBundle( esm5bundle, @@ -593,7 +685,8 @@ runInEachFileSystem(() => { }, ], ['fesm5', 'module']); - const packageJsonFromFile1 = JSON.parse(fs.readFile(packageJsonPath)); + const packageJsonFromFile1 = + JSON.parse(fs.readFile(packageJsonPath)) as NewEntryPointPropertiesMap; expect(entryPoint.packageJson).toEqual(jasmine.objectContaining({ fesm5_ivy_ngcc: '__ivy_ngcc__/esm5.js', @@ -613,7 +706,8 @@ runInEachFileSystem(() => { esm5bundle.entryPoint, [_('/node_modules/test/index.d.ts'), _('/node_modules/test/index.d.ts.map')], ['fesm5', 'module']); - const packageJsonFromFile2 = JSON.parse(fs.readFile(packageJsonPath)); + const packageJsonFromFile2 = + JSON.parse(fs.readFile(packageJsonPath)) as NewEntryPointPropertiesMap; expect(entryPoint.packageJson).toEqual(jasmine.objectContaining({ fesm5: './esm5.js', @@ -638,6 +732,6 @@ runInEachFileSystem(() => { const moduleResolutionCache = createModuleResolutionCache(fs); return makeEntryPointBundle( fs, entryPoint, new SharedFileCache(fs), moduleResolutionCache, - entryPoint.packageJson[formatProperty]!, false, format, true); + entryPoint.packageJson[formatProperty]!, false, format, DtsProcessing.Yes); } }); 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 e1981c9a85..2c7ff81879 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 @@ -18,7 +18,7 @@ runInEachFileSystem(() => { let updater: PackageJsonUpdater; // Helpers - const readJson = (path: AbsoluteFsPath) => JSON.parse(fs.readFile(path)); + const readJson = (path: AbsoluteFsPath) => JSON.parse(fs.readFile(path)) as T; beforeEach(() => { _ = absoluteFrom; @@ -55,7 +55,7 @@ runInEachFileSystem(() => { {name: jsonPath, contents: '{"foo": true, "bar": {"baz": "OK"}}'}, ]); - const pkg = readJson(jsonPath); + const pkg = readJson<{foo: boolean, bar: {baz: string | number}}>(jsonPath); const update = updater.createUpdate().addChange(['foo'], false).addChange(['bar', 'baz'], 42); // Not updated yet. @@ -95,7 +95,7 @@ runInEachFileSystem(() => { {name: jsonPath, contents: '{"foo": {}}'}, ]); - const pkg = readJson(jsonPath); + const pkg = readJson<{foo: {bar: {baz: {qux: string}}}}>(jsonPath); updater.createUpdate() .addChange(['foo', 'bar', 'baz', 'qux'], 'updated') .writeChanges(jsonPath, pkg); diff --git a/packages/compiler-cli/package.json b/packages/compiler-cli/package.json index d47c5f2da5..a6c2a4c3c3 100644 --- a/packages/compiler-cli/package.json +++ b/packages/compiler-cli/package.json @@ -24,12 +24,12 @@ "semver": "^6.3.0", "source-map": "^0.6.1", "sourcemap-codec": "^1.4.8", - "tslib": "^2.0.0", - "yargs": "^16.1.1" + "tslib": "^2.1.0", + "yargs": "^16.2.0" }, "peerDependencies": { "@angular/compiler": "0.0.0-PLACEHOLDER", - "typescript": ">=4.0 <4.2" + "typescript": ">=4.2.3 <4.3" }, "engines": { "node": ">=10.0" diff --git a/packages/compiler-cli/src/extract_i18n.ts b/packages/compiler-cli/src/extract_i18n.ts index 650d12adb4..530d17296f 100644 --- a/packages/compiler-cli/src/extract_i18n.ts +++ b/packages/compiler-cli/src/extract_i18n.ts @@ -41,6 +41,7 @@ function readXi18nCommandLineAndConfiguration(args: string[]): ParsedConfigurati // Entry point if (require.main === module) { + process.title = 'Angular i18n Message Extractor (ng-xi18n)'; const args = process.argv.slice(2); // We are running the real compiler so run against the real file-system setFileSystem(new NodeJSFileSystem()); diff --git a/packages/compiler-cli/src/main.ts b/packages/compiler-cli/src/main.ts index 88facc5587..2ad8f6af3e 100644 --- a/packages/compiler-cli/src/main.ts +++ b/packages/compiler-cli/src/main.ts @@ -233,6 +233,7 @@ function printDiagnostics( // CLI entry point if (require.main === module) { + process.title = 'Angular Compiler (ngc)'; const args = process.argv.slice(2); // We are running the real compiler so run against the real file-system setFileSystem(new NodeJSFileSystem()); diff --git a/packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel b/packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel index 7e089a796f..b10a14584e 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel @@ -14,9 +14,11 @@ ts_library( "//packages/compiler-cli/src/ngtsc/file_system", "//packages/compiler-cli/src/ngtsc/imports", "//packages/compiler-cli/src/ngtsc/incremental:api", + "//packages/compiler-cli/src/ngtsc/incremental/semantic_graph", "//packages/compiler-cli/src/ngtsc/indexer", "//packages/compiler-cli/src/ngtsc/metadata", "//packages/compiler-cli/src/ngtsc/partial_evaluator", + "//packages/compiler-cli/src/ngtsc/perf", "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/routing", "//packages/compiler-cli/src/ngtsc/scope", diff --git a/packages/compiler-cli/src/ngtsc/annotations/index.ts b/packages/compiler-cli/src/ngtsc/annotations/index.ts index f36b2a1a28..98f12928a1 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/index.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/index.ts @@ -8,7 +8,7 @@ /// -export {ResourceLoader} from './src/api'; +export {ResourceLoader, ResourceLoaderContext} from './src/api'; export {ComponentDecoratorHandler} from './src/component'; export {DirectiveDecoratorHandler} from './src/directive'; export {InjectableDecoratorHandler} from './src/injectable'; diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/api.ts b/packages/compiler-cli/src/ngtsc/annotations/src/api.ts index 544bbdf461..2ff84162ea 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/api.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/api.ts @@ -21,6 +21,11 @@ export interface ResourceLoader { */ canPreload: boolean; + /** + * If true, the resource loader is able to preprocess inline resources. + */ + canPreprocess: boolean; + /** * Resolve the url of a resource relative to the file that contains the reference to it. * The return value of this method can be used in the `load()` and `preload()` methods. @@ -37,11 +42,22 @@ export interface ResourceLoader { * should be cached so it can be accessed synchronously via the `load()` method. * * @param resolvedUrl The url (resolved by a call to `resolve()`) of the resource to preload. + * @param context Information regarding the resource such as the type and containing file. * @returns A Promise that is resolved once the resource has been loaded or `undefined` * if the file has already been loaded. * @throws An Error if pre-loading is not available. */ - preload(resolvedUrl: string): Promise|undefined; + preload(resolvedUrl: string, context: ResourceLoaderContext): Promise|undefined; + + /** + * Preprocess the content data of an inline resource, asynchronously. + * + * @param data The existing content data from the inline resource. + * @param context Information regarding the resource such as the type and containing file. + * @returns A Promise that resolves to the processed data. If no processing occurs, the + * same data string that was passed to the function will be resolved. + */ + preprocessInline(data: string, context: ResourceLoaderContext): Promise; /** * Load the resource at the given url, synchronously. @@ -53,3 +69,22 @@ export interface ResourceLoader { */ load(resolvedUrl: string): string; } + +/** + * Contextual information used by members of the ResourceLoader interface. + */ +export interface ResourceLoaderContext { + /** + * The type of the component resource. + * * Resources referenced via a component's `styles` or `styleUrls` properties are of + * type `style`. + * * Resources referenced via a component's `template` or `templateUrl` properties are of type + * `template`. + */ + type: 'style'|'template'; + + /** + * The absolute path to the file that contains the resource or reference to the resource. + */ + containingFile: string; +} diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index 53253cacb0..dd58ad5b56 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -6,32 +6,33 @@ * found in the LICENSE file at https://angular.io/license */ -import {compileComponentFromMetadata, compileDeclareComponentFromMetadata, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Expression, ExternalExpr, Identifiers, InterpolationConfig, LexerRange, makeBindingParser, ParsedTemplate, ParseSourceFile, parseTemplate, R3ComponentDef, R3ComponentMetadata, R3FactoryTarget, R3TargetBinder, R3UsedDirectiveMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr} from '@angular/compiler'; +import {compileComponentFromMetadata, compileDeclareComponentFromMetadata, ConstantPool, CssSelector, DeclarationListEmitMode, DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Expression, ExternalExpr, FactoryTarget, InterpolationConfig, LexerRange, makeBindingParser, ParsedTemplate, ParseSourceFile, parseTemplate, R3ComponentMetadata, R3FactoryMetadata, R3TargetBinder, R3UsedDirectiveMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; -import {CycleAnalyzer} from '../../cycles'; -import {ErrorCode, FatalDiagnosticError, ngErrorCode} from '../../diagnostics'; -import {absoluteFrom, relative, resolve} from '../../file_system'; -import {DefaultImportRecorder, ModuleResolver, Reference, ReferenceEmitter} from '../../imports'; +import {Cycle, CycleAnalyzer, CycleHandlingStrategy} from '../../cycles'; +import {ErrorCode, FatalDiagnosticError, makeRelatedInformation} from '../../diagnostics'; +import {absoluteFrom, relative} from '../../file_system'; +import {DefaultImportRecorder, ImportedFile, ModuleResolver, Reference, ReferenceEmitter} from '../../imports'; import {DependencyTracker} from '../../incremental/api'; +import {extractSemanticTypeParameters, isArrayEqual, isReferenceEqual, SemanticDepGraphUpdater, SemanticReference, SemanticSymbol} from '../../incremental/semantic_graph'; import {IndexingContext} from '../../indexer'; import {ClassPropertyMapping, ComponentResources, DirectiveMeta, DirectiveTypeCheckMeta, extractDirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, Resource, ResourceRegistry} from '../../metadata'; -import {EnumValue, PartialEvaluator} from '../../partial_evaluator'; +import {EnumValue, PartialEvaluator, ResolvedValue} from '../../partial_evaluator'; +import {PerfEvent, PerfRecorder} from '../../perf'; import {ClassDeclaration, DeclarationNode, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; -import {ComponentScopeReader, LocalModuleScopeRegistry} from '../../scope'; +import {ComponentScopeReader, LocalModuleScopeRegistry, TypeCheckScopeRegistry} from '../../scope'; import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFlags, HandlerPrecedence, ResolveResult} from '../../transform'; import {TemplateSourceMapping, TypeCheckContext} from '../../typecheck/api'; -import {getTemplateId, makeTemplateDiagnostic} from '../../typecheck/diagnostics'; import {tsSourceMapBug29300Fixed} from '../../util/src/ts_source_map_bug_29300'; import {SubsetOfKeys} from '../../util/src/typescript'; import {ResourceLoader} from './api'; import {createValueHasWrongTypeError, getDirectiveDiagnostics, getProviderDiagnostics} from './diagnostics'; -import {extractDirectiveMetadata, parseFieldArrayValue} from './directive'; -import {compileNgFactoryDefField} from './factory'; +import {DirectiveSymbol, extractDirectiveMetadata, parseFieldArrayValue} from './directive'; +import {compileDeclareFactory, compileNgFactoryDefField} from './factory'; import {generateSetClassMetadataCall} from './metadata'; -import {TypeCheckScopes} from './typecheck_scopes'; -import {findAngularDecorator, isAngularCoreReference, isExpressionForwardReference, readBaseClass, resolveProvidersRequiringFactory, unwrapExpression, wrapFunctionExpressionsInParens} from './util'; +import {NgModuleSymbol} from './ng_module'; +import {compileResults, findAngularDecorator, isAngularCoreReference, isExpressionForwardReference, readBaseClass, resolveProvidersRequiringFactory, toFactoryMetadata, unwrapExpression, wrapFunctionExpressionsInParens} from './util'; const EMPTY_MAP = new Map(); const EMPTY_ARRAY: any[] = []; @@ -43,7 +44,7 @@ const EMPTY_ARRAY: any[] = []; * be included here. */ export type ComponentMetadataResolvedFields = - SubsetOfKeys; + SubsetOfKeys; export interface ComponentAnalysisData { /** @@ -72,33 +73,146 @@ export interface ComponentAnalysisData { viewProvidersRequiringFactory: Set>|null; resources: ComponentResources; + + /** + * `styleUrls` extracted from the decorator, if present. + */ + styleUrls: StyleUrlMeta[]|null; + + /** + * Inline stylesheets extracted from the decorator, if present. + */ + inlineStyles: string[]|null; + + isPoisoned: boolean; } export type ComponentResolutionData = Pick; +/** + * The literal style url extracted from the decorator, along with metadata for diagnostics. + */ +export interface StyleUrlMeta { + url: string; + nodeForError: ts.Node; + source: ResourceTypeForDiagnostics.StylesheetFromTemplate| + ResourceTypeForDiagnostics.StylesheetFromDecorator; +} + +/** + * Information about the origin of a resource in the application code. This is used for creating + * diagnostics, so we can point to the root cause of an error in the application code. + * + * A template resource comes from the `templateUrl` property on the component decorator. + * + * Stylesheets resources can come from either the `styleUrls` property on the component decorator, + * or from inline `style` tags and style links on the external template. + */ +export const enum ResourceTypeForDiagnostics { + Template, + StylesheetFromTemplate, + StylesheetFromDecorator, +} + +/** + * Represents an Angular component. + */ +export class ComponentSymbol extends DirectiveSymbol { + usedDirectives: SemanticReference[] = []; + usedPipes: SemanticReference[] = []; + isRemotelyScoped = false; + + isEmitAffected(previousSymbol: SemanticSymbol, publicApiAffected: Set): boolean { + if (!(previousSymbol instanceof ComponentSymbol)) { + return true; + } + + // Create an equality function that considers symbols equal if they represent the same + // declaration, but only if the symbol in the current compilation does not have its public API + // affected. + const isSymbolUnaffected = (current: SemanticReference, previous: SemanticReference) => + isReferenceEqual(current, previous) && !publicApiAffected.has(current.symbol); + + // The emit of a component is affected if either of the following is true: + // 1. The component used to be remotely scoped but no longer is, or vice versa. + // 2. The list of used directives has changed or any of those directives have had their public + // API changed. If the used directives have been reordered but not otherwise affected then + // the component must still be re-emitted, as this may affect directive instantiation order. + // 3. The list of used pipes has changed, or any of those pipes have had their public API + // changed. + return this.isRemotelyScoped !== previousSymbol.isRemotelyScoped || + !isArrayEqual(this.usedDirectives, previousSymbol.usedDirectives, isSymbolUnaffected) || + !isArrayEqual(this.usedPipes, previousSymbol.usedPipes, isSymbolUnaffected); + } + + isTypeCheckBlockAffected( + previousSymbol: SemanticSymbol, typeCheckApiAffected: Set): boolean { + if (!(previousSymbol instanceof ComponentSymbol)) { + return true; + } + + // To verify that a used directive is not affected we need to verify that its full inheritance + // chain is not present in `typeCheckApiAffected`. + const isInheritanceChainAffected = (symbol: SemanticSymbol): boolean => { + let currentSymbol: SemanticSymbol|null = symbol; + while (currentSymbol instanceof DirectiveSymbol) { + if (typeCheckApiAffected.has(currentSymbol)) { + return true; + } + currentSymbol = currentSymbol.baseClass; + } + + return false; + }; + + // Create an equality function that considers directives equal if they represent the same + // declaration and if the symbol and all symbols it inherits from in the current compilation + // do not have their type-check API affected. + const isDirectiveUnaffected = (current: SemanticReference, previous: SemanticReference) => + isReferenceEqual(current, previous) && !isInheritanceChainAffected(current.symbol); + + // Create an equality function that considers pipes equal if they represent the same + // declaration and if the symbol in the current compilation does not have its type-check + // API affected. + const isPipeUnaffected = (current: SemanticReference, previous: SemanticReference) => + isReferenceEqual(current, previous) && !typeCheckApiAffected.has(current.symbol); + + // The emit of a type-check block of a component is affected if either of the following is true: + // 1. The list of used directives has changed or any of those directives have had their + // type-check API changed. + // 2. The list of used pipes has changed, or any of those pipes have had their type-check API + // changed. + return !isArrayEqual( + this.usedDirectives, previousSymbol.usedDirectives, isDirectiveUnaffected) || + !isArrayEqual(this.usedPipes, previousSymbol.usedPipes, isPipeUnaffected); + } +} + /** * `DecoratorHandler` which handles the `@Component` annotation. */ export class ComponentDecoratorHandler implements - DecoratorHandler { + DecoratorHandler { constructor( private reflector: ReflectionHost, private evaluator: PartialEvaluator, private metaRegistry: MetadataRegistry, private metaReader: MetadataReader, private scopeReader: ComponentScopeReader, private scopeRegistry: LocalModuleScopeRegistry, + private typeCheckScopeRegistry: TypeCheckScopeRegistry, private resourceRegistry: ResourceRegistry, private isCore: boolean, private resourceLoader: ResourceLoader, private rootDirs: ReadonlyArray, private defaultPreserveWhitespaces: boolean, private i18nUseExternalIds: boolean, - private enableI18nLegacyMessageIdFormat: boolean, + private enableI18nLegacyMessageIdFormat: boolean, private usePoisonedData: boolean, private i18nNormalizeLineEndingsInICUs: boolean|undefined, private moduleResolver: ModuleResolver, private cycleAnalyzer: CycleAnalyzer, - private refEmitter: ReferenceEmitter, private defaultImportRecorder: DefaultImportRecorder, + private cycleHandlingStrategy: CycleHandlingStrategy, private refEmitter: ReferenceEmitter, + private defaultImportRecorder: DefaultImportRecorder, private depTracker: DependencyTracker|null, private injectableRegistry: InjectableClassRegistry, - private annotateForClosureCompiler: boolean) {} + private semanticDepGraphUpdater: SemanticDepGraphUpdater|null, + private annotateForClosureCompiler: boolean, private perf: PerfRecorder) {} private literalCache = new Map(); private elementSchemaRegistry = new DomElementSchemaRegistry(); - private typeCheckScopes = new TypeCheckScopes(this.scopeReader, this.metaReader); /** * During the asynchronous preanalyze phase, it's necessary to parse the template to extract @@ -106,6 +220,7 @@ export class ComponentDecoratorHandler implements * thrown away, and the parsed template is reused during the analyze phase. */ private preanalyzeTemplateCache = new Map(); + private preanalyzeStylesCache = new Map(); readonly precedence = HandlerPrecedence.PRIMARY; readonly name = ComponentDecoratorHandler.name; @@ -147,40 +262,67 @@ export class ComponentDecoratorHandler implements const component = reflectObjectLiteral(meta); const containingFile = node.getSourceFile().fileName; - // Convert a styleUrl string into a Promise to preload it. - const resolveStyleUrl = (styleUrl: string): Promise => { - const resourceUrl = this.resourceLoader.resolve(styleUrl, containingFile); - const promise = this.resourceLoader.preload(resourceUrl); - return promise || Promise.resolve(); - }; + const resolveStyleUrl = + (styleUrl: string, nodeForError: ts.Node, + resourceType: ResourceTypeForDiagnostics): Promise|undefined => { + const resourceUrl = + this._resolveResourceOrThrow(styleUrl, containingFile, nodeForError, resourceType); + return this.resourceLoader.preload(resourceUrl, {type: 'style', containingFile}); + }; // A Promise that waits for the template and all ed styles within it to be preloaded. const templateAndTemplateStyleResources = - this._preloadAndParseTemplate(node, decorator, component, containingFile).then(template => { - if (template === null) { - return undefined; - } else { - return Promise.all(template.styleUrls.map(resolveStyleUrl)).then(() => undefined); - } - }); + this._preloadAndParseTemplate(node, decorator, component, containingFile) + .then((template: ParsedTemplateWithSource|null): Promise|undefined => { + if (template === null) { + return undefined; + } + + const nodeForError = getTemplateDeclarationNodeForError(template.declaration); + return Promise + .all(template.styleUrls.map( + styleUrl => resolveStyleUrl( + styleUrl, nodeForError, + ResourceTypeForDiagnostics.StylesheetFromTemplate))) + .then(() => undefined); + }); // Extract all the styleUrls in the decorator. - const styleUrls = this._extractStyleUrls(component, []); + const componentStyleUrls = this._extractComponentStyleUrls(component); - if (styleUrls === null) { - // A fast path exists if there are no styleUrls, to just wait for - // templateAndTemplateStyleResources. - return templateAndTemplateStyleResources; - } else { - // Wait for both the template and all styleUrl resources to resolve. - return Promise.all([templateAndTemplateStyleResources, ...styleUrls.map(resolveStyleUrl)]) - .then(() => undefined); + // Extract inline styles, process, and cache for use in synchronous analyze phase + let inlineStyles; + if (component.has('styles')) { + const litStyles = parseFieldArrayValue(component, 'styles', this.evaluator); + if (litStyles === null) { + this.preanalyzeStylesCache.set(node, null); + } else { + inlineStyles = Promise + .all(litStyles.map( + style => this.resourceLoader.preprocessInline( + style, {type: 'style', containingFile}))) + .then(styles => { + this.preanalyzeStylesCache.set(node, styles); + }); + } } + + // Wait for both the template and all styleUrl resources to resolve. + return Promise + .all([ + templateAndTemplateStyleResources, inlineStyles, + ...componentStyleUrls.map( + styleUrl => resolveStyleUrl( + styleUrl.url, styleUrl.nodeForError, + ResourceTypeForDiagnostics.StylesheetFromDecorator)) + ]) + .then(() => undefined); } analyze( node: ClassDeclaration, decorator: Readonly, flags: HandlerFlags = HandlerFlags.NONE): AnalysisOutput { + this.perf.eventCount(PerfEvent.AnalyzeComponent); const containingFile = node.getSourceFile().fileName; this.literalCache.delete(decorator); @@ -246,83 +388,66 @@ export class ComponentDecoratorHandler implements 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 templateUrl = this.evaluator.evaluate(templateUrlExpr); - if (typeof templateUrl !== 'string') { - throw createValueHasWrongTypeError( - templateUrlExpr, templateUrl, 'templateUrl must be a string'); - } - const resourceUrl = this.resourceLoader.resolve(templateUrl, containingFile); - template = this._extractExternalTemplate(node, component, templateUrlExpr, resourceUrl); - } else { - // Expect an inline template to be present. - template = this._extractInlineTemplate(node, decorator, component, containingFile); - } - } - const templateResource = template.isInline ? - {path: null, expression: component.get('template')!} : - {path: absoluteFrom(template.templateUrl), expression: template.sourceMapping.node}; - - let diagnostics: ts.Diagnostic[]|undefined = undefined; - - if (template.errors !== null) { - // If there are any template parsing errors, convert them to `ts.Diagnostic`s for display. - const id = getTemplateId(node); - diagnostics = template.errors.map(error => { - const span = error.span; - - if (span.start.offset === span.end.offset) { - // Template errors can contain zero-length spans, if the error occurs at a single point. - // However, TypeScript does not handle displaying a zero-length diagnostic very well, so - // increase the ending offset by 1 for such errors, to ensure the position is shown in the - // diagnostic. - span.end.offset++; - } - - return makeTemplateDiagnostic( - id, template.sourceMapping, span, ts.DiagnosticCategory.Error, - ngErrorCode(ErrorCode.TEMPLATE_PARSE_ERROR), error.msg); - }); + const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile); + template = this.extractTemplate(node, templateDecl); } + const templateResource = + template.isInline ? {path: null, expression: component.get('template')!} : { + path: absoluteFrom(template.declaration.resolvedTemplateUrl), + expression: template.sourceMapping.node + }; // Figure out the set of styles. The ordering here is important: external resources (styleUrls) // precede inline styles, and styles defined in the template override styles defined in the // component. - let styles: string[]|null = null; + let styles: string[] = []; const styleResources = this._extractStyleResources(component, containingFile); - const styleUrls = this._extractStyleUrls(component, template.styleUrls); - if (styleUrls !== null) { - if (styles === null) { - styles = []; - } - for (const styleUrl of styleUrls) { - const resourceUrl = this.resourceLoader.resolve(styleUrl, containingFile); - const resourceStr = this.resourceLoader.load(resourceUrl); - styles.push(resourceStr); - if (this.depTracker !== null) { - this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(resourceUrl)); - } + const styleUrls: StyleUrlMeta[] = [ + ...this._extractComponentStyleUrls(component), ...this._extractTemplateStyleUrls(template) + ]; + + for (const styleUrl of styleUrls) { + const resourceType = styleUrl.source === ResourceTypeForDiagnostics.StylesheetFromDecorator ? + ResourceTypeForDiagnostics.StylesheetFromDecorator : + ResourceTypeForDiagnostics.StylesheetFromTemplate; + const resourceUrl = this._resolveResourceOrThrow( + styleUrl.url, containingFile, styleUrl.nodeForError, resourceType); + const resourceStr = this.resourceLoader.load(resourceUrl); + + styles.push(resourceStr); + if (this.depTracker !== null) { + this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(resourceUrl)); } } - if (component.has('styles')) { - const litStyles = parseFieldArrayValue(component, 'styles', this.evaluator); - if (litStyles !== null) { - if (styles === null) { - styles = litStyles; - } else { + + // If inline styles were preprocessed use those + let inlineStyles: string[]|null = null; + if (this.preanalyzeStylesCache.has(node)) { + inlineStyles = this.preanalyzeStylesCache.get(node)!; + this.preanalyzeStylesCache.delete(node); + if (inlineStyles !== null) { + styles.push(...inlineStyles); + } + } else { + // Preprocessing is only supported asynchronously + // If no style cache entry is present asynchronous preanalyze was not executed. + // This protects against accidental differences in resource contents when preanalysis + // is not used with a provided transformResource hook on the ResourceHost. + if (this.resourceLoader.canPreprocess) { + throw new Error('Inline resource processing requires asynchronous preanalyze.'); + } + + if (component.has('styles')) { + const litStyles = parseFieldArrayValue(component, 'styles', this.evaluator); + if (litStyles !== null) { + inlineStyles = [...litStyles]; styles.push(...litStyles); } } } if (template.styles.length > 0) { - if (styles === null) { - styles = template.styles; - } else { - styles.push(...template.styles); - } + styles.push(...template.styles); } const encapsulation: number = @@ -349,7 +474,7 @@ export class ComponentDecoratorHandler implements }, encapsulation, interpolation: template.interpolationConfig ?? DEFAULT_INTERPOLATION_CONFIG, - styles: styles || [], + styles, // These will be replaced during the compilation step, after all `NgModule`s have been // analyzed and the full compilation scope for the component can be realized. @@ -365,12 +490,14 @@ export class ComponentDecoratorHandler implements template, providersRequiringFactory, viewProvidersRequiringFactory, + inlineStyles, + styleUrls, resources: { styles: styleResources, template: templateResource, }, + isPoisoned: false, }, - diagnostics, }; if (changeDetection !== null) { output.analysis!.meta.changeDetection = changeDetection; @@ -378,6 +505,14 @@ export class ComponentDecoratorHandler implements return output; } + symbol(node: ClassDeclaration, analysis: Readonly): ComponentSymbol { + const typeParameters = extractSemanticTypeParameters(node); + + return new ComponentSymbol( + node, analysis.meta.selector, analysis.inputs, analysis.outputs, analysis.meta.exportAs, + analysis.typeCheckMeta, typeParameters); + } + register(node: ClassDeclaration, analysis: ComponentAnalysisData): void { // Register this component's information with the `MetadataRegistry`. This ensures that // the information about the component is available during the compile() phase. @@ -393,6 +528,8 @@ export class ComponentDecoratorHandler implements isComponent: true, baseClass: analysis.baseClass, ...analysis.typeCheckMeta, + isPoisoned: analysis.isPoisoned, + isStructural: false, }); this.resourceRegistry.registerResources(analysis.resources, node); @@ -401,15 +538,19 @@ export class ComponentDecoratorHandler implements index( context: IndexingContext, node: ClassDeclaration, analysis: Readonly) { + if (analysis.isPoisoned && !this.usePoisonedData) { + return null; + } const scope = this.scopeReader.getScopeForComponent(node); const selector = analysis.meta.selector; const matcher = new SelectorMatcher(); - if (scope === 'error') { - // Don't bother indexing components which had erroneous scopes. - return null; - } - if (scope !== null) { + if ((scope.compilation.isPoisoned || scope.exported.isPoisoned) && !this.usePoisonedData) { + // Don't bother indexing components which had erroneous scopes, unless specifically + // requested. + return null; + } + for (const directive of scope.compilation.directives) { if (directive.selector !== null) { matcher.addSelectables(CssSelector.parse(directive.selector), directive); @@ -432,24 +573,36 @@ export class ComponentDecoratorHandler implements typeCheck(ctx: TypeCheckContext, node: ClassDeclaration, meta: Readonly): void { - if (!ts.isClassDeclaration(node)) { + if (this.typeCheckScopeRegistry === null || !ts.isClassDeclaration(node)) { return; } - const scope = this.typeCheckScopes.getTypeCheckScope(node); - if (scope === 'error') { - // Don't type-check components that had errors in their scopes. + if (meta.isPoisoned && !this.usePoisonedData) { + return; + } + const scope = this.typeCheckScopeRegistry.getTypeCheckScope(node); + if (scope.isPoisoned && !this.usePoisonedData) { + // Don't type-check components that had errors in their scopes, unless requested. return; } const binder = new R3TargetBinder(scope.matcher); ctx.addTemplate( new Reference(node), binder, meta.template.diagNodes, scope.pipes, scope.schemas, - meta.template.sourceMapping, meta.template.file); + meta.template.sourceMapping, meta.template.file, meta.template.errors); } - resolve(node: ClassDeclaration, analysis: Readonly): - ResolveResult { + resolve( + node: ClassDeclaration, analysis: Readonly, + symbol: ComponentSymbol): ResolveResult { + if (this.semanticDepGraphUpdater !== null && analysis.baseClass instanceof Reference) { + symbol.baseClass = this.semanticDepGraphUpdater.getSymbol(analysis.baseClass.node); + } + + if (analysis.isPoisoned && !this.usePoisonedData) { + return {}; + } + const context = node.getSourceFile(); // Check whether this component was registered with an NgModule. If so, it should be compiled // under that module's compilation scope. @@ -459,10 +612,10 @@ export class ComponentDecoratorHandler implements const data: ComponentResolutionData = { directives: EMPTY_ARRAY, pipes: EMPTY_MAP, - wrapDirectivesAndPipesInClosure: false, + declarationListEmitMode: DeclarationListEmitMode.Direct, }; - if (scope !== null && scope !== 'error') { + if (scope !== null && (!scope.compilation.isPoisoned || this.usePoisonedData)) { // Replace the empty components and directives from the analyze() step with a fully expanded // scope. This is possible now because during resolve() the whole compilation unit has been // fully analyzed. @@ -476,17 +629,17 @@ export class ComponentDecoratorHandler implements // Determining this is challenging, because the TemplateDefinitionBuilder is responsible for // matching directives and pipes in the template; however, that doesn't run until the actual // compile() step. It's not possible to run template compilation sooner as it requires the - // ConstantPool for the overall file being compiled (which isn't available until the transform - // step). + // ConstantPool for the overall file being compiled (which isn't available until the + // transform step). // - // Instead, directives/pipes are matched independently here, using the R3TargetBinder. This is - // an alternative implementation of template matching which is used for template type-checking - // and will eventually replace matching in the TemplateDefinitionBuilder. + // Instead, directives/pipes are matched independently here, using the R3TargetBinder. This + // is an alternative implementation of template matching which is used for template + // type-checking and will eventually replace matching in the TemplateDefinitionBuilder. - // Set up the R3TargetBinder, as well as a 'directives' array and a 'pipes' map that are later - // fed to the TemplateDefinitionBuilder. First, a SelectorMatcher is constructed to match - // directives that are in scope. + // Set up the R3TargetBinder, as well as a 'directives' array and a 'pipes' map that are + // later fed to the TemplateDefinitionBuilder. First, a SelectorMatcher is constructed to + // match directives that are in scope. type MatchedDirective = DirectiveMeta&{selector: string}; const matcher = new SelectorMatcher(); @@ -506,49 +659,83 @@ export class ComponentDecoratorHandler implements const bound = binder.bind({template: metadata.template.nodes}); // The BoundTarget knows which directives and pipes matched the template. - type UsedDirective = R3UsedDirectiveMetadata&{ref: Reference}; + type UsedDirective = + R3UsedDirectiveMetadata&{ref: Reference, importedFile: ImportedFile}; const usedDirectives: UsedDirective[] = bound.getUsedDirectives().map(directive => { + const type = this.refEmitter.emit(directive.ref, context); return { ref: directive.ref, - type: this.refEmitter.emit(directive.ref, context), + type: type.expression, + importedFile: type.importedFile, selector: directive.selector, inputs: directive.inputs.propertyNames, outputs: directive.outputs.propertyNames, exportAs: directive.exportAs, + isComponent: directive.isComponent, }; }); - const usedPipes: {ref: Reference, pipeName: string, expression: Expression}[] = []; + type UsedPipe = { + ref: Reference, + pipeName: string, + expression: Expression, + importedFile: ImportedFile, + }; + const usedPipes: UsedPipe[] = []; for (const pipeName of bound.getUsedPipes()) { if (!pipes.has(pipeName)) { continue; } const pipe = pipes.get(pipeName)!; + const type = this.refEmitter.emit(pipe, context); usedPipes.push({ ref: pipe, pipeName, - expression: this.refEmitter.emit(pipe, context), + expression: type.expression, + importedFile: type.importedFile, }); } + if (this.semanticDepGraphUpdater !== null) { + symbol.usedDirectives = usedDirectives.map( + dir => this.semanticDepGraphUpdater!.getSemanticReference(dir.ref.node, dir.type)); + symbol.usedPipes = usedPipes.map( + pipe => + this.semanticDepGraphUpdater!.getSemanticReference(pipe.ref.node, pipe.expression)); + } // Scan through the directives/pipes actually used in the template and check whether any // import which needs to be generated would create a cycle. - const cycleDetected = usedDirectives.some(dir => this._isCyclicImport(dir.type, context)) || - usedPipes.some(pipe => this._isCyclicImport(pipe.expression, context)); + const cyclesFromDirectives = new Map(); + for (const usedDirective of usedDirectives) { + const cycle = + this._checkForCyclicImport(usedDirective.importedFile, usedDirective.type, context); + if (cycle !== null) { + cyclesFromDirectives.set(usedDirective, cycle); + } + } + const cyclesFromPipes = new Map(); + for (const usedPipe of usedPipes) { + const cycle = + this._checkForCyclicImport(usedPipe.importedFile, usedPipe.expression, context); + if (cycle !== null) { + cyclesFromPipes.set(usedPipe, cycle); + } + } + const cycleDetected = cyclesFromDirectives.size !== 0 || cyclesFromPipes.size !== 0; if (!cycleDetected) { // No cycle was detected. Record the imports that need to be created in the cycle detector // so that future cyclic import checks consider their production. - for (const {type} of usedDirectives) { - this._recordSyntheticImport(type, context); + for (const {type, importedFile} of usedDirectives) { + this._recordSyntheticImport(importedFile, type, context); } - for (const {expression} of usedPipes) { - this._recordSyntheticImport(expression, context); + for (const {expression, importedFile} of usedPipes) { + this._recordSyntheticImport(importedFile, expression, context); } // Check whether the directive/pipe arrays in ɵcmp need to be wrapped in closures. - // This is required if any directive/pipe reference is to a declaration in the same file but - // declared after this component. + // This is required if any directive/pipe reference is to a declaration in the same file + // but declared after this component. const wrapDirectivesAndPipesInClosure = usedDirectives.some( dir => isExpressionForwardReference(dir.type, node.name, context)) || @@ -557,13 +744,47 @@ export class ComponentDecoratorHandler implements data.directives = usedDirectives; data.pipes = new Map(usedPipes.map(pipe => [pipe.pipeName, pipe.expression])); - data.wrapDirectivesAndPipesInClosure = wrapDirectivesAndPipesInClosure; + data.declarationListEmitMode = wrapDirectivesAndPipesInClosure ? + DeclarationListEmitMode.Closure : + DeclarationListEmitMode.Direct; } else { - // Declaring the directiveDefs/pipeDefs arrays directly would require imports that would - // create a cycle. Instead, mark this component as requiring remote scoping, so that the - // NgModule file will take care of setting the directives for the component. - this.scopeRegistry.setComponentRemoteScope( - node, usedDirectives.map(dir => dir.ref), usedPipes.map(pipe => pipe.ref)); + if (this.cycleHandlingStrategy === CycleHandlingStrategy.UseRemoteScoping) { + // Declaring the directiveDefs/pipeDefs arrays directly would require imports that would + // create a cycle. Instead, mark this component as requiring remote scoping, so that the + // NgModule file will take care of setting the directives for the component. + this.scopeRegistry.setComponentRemoteScope( + node, usedDirectives.map(dir => dir.ref), usedPipes.map(pipe => pipe.ref)); + symbol.isRemotelyScoped = true; + + // If a semantic graph is being tracked, record the fact that this component is remotely + // scoped with the declaring NgModule symbol as the NgModule's emit becomes dependent on + // the directive/pipe usages of this component. + if (this.semanticDepGraphUpdater !== null) { + const moduleSymbol = this.semanticDepGraphUpdater.getSymbol(scope.ngModule); + if (!(moduleSymbol instanceof NgModuleSymbol)) { + throw new Error( + `AssertionError: Expected ${scope.ngModule.name} to be an NgModuleSymbol.`); + } + + moduleSymbol.addRemotelyScopedComponent( + symbol, symbol.usedDirectives, symbol.usedPipes); + } + } else { + // We are not able to handle this cycle so throw an error. + const relatedMessages: ts.DiagnosticRelatedInformation[] = []; + for (const [dir, cycle] of cyclesFromDirectives) { + relatedMessages.push( + makeCyclicImportInfo(dir.ref, dir.isComponent ? 'component' : 'directive', cycle)); + } + for (const [pipe, cycle] of cyclesFromPipes) { + relatedMessages.push(makeCyclicImportInfo(pipe.ref, 'pipe', cycle)); + } + throw new FatalDiagnosticError( + ErrorCode.IMPORT_CYCLE_DETECTED, node, + 'One or more import cycles would need to be created to compile this component, ' + + 'which is not supported by the current compiler configuration.', + relatedMessages); + } } } @@ -598,41 +819,64 @@ export class ComponentDecoratorHandler implements return {data}; } + updateResources(node: ClassDeclaration, analysis: ComponentAnalysisData): void { + const containingFile = node.getSourceFile().fileName; + + // If the template is external, re-parse it. + const templateDecl = analysis.template.declaration; + if (!templateDecl.isInline) { + analysis.template = this.extractTemplate(node, templateDecl); + } + + // Update any external stylesheets and rebuild the combined 'styles' list. + // TODO(alxhub): write tests for styles when the primary compiler uses the updateResources path + let styles: string[] = []; + if (analysis.styleUrls !== null) { + for (const styleUrl of analysis.styleUrls) { + const resourceType = + styleUrl.source === ResourceTypeForDiagnostics.StylesheetFromDecorator ? + ResourceTypeForDiagnostics.StylesheetFromDecorator : + ResourceTypeForDiagnostics.StylesheetFromTemplate; + const resolvedStyleUrl = this._resolveResourceOrThrow( + styleUrl.url, containingFile, styleUrl.nodeForError, resourceType); + const styleText = this.resourceLoader.load(resolvedStyleUrl); + styles.push(styleText); + } + } + if (analysis.inlineStyles !== null) { + for (const styleText of analysis.inlineStyles) { + styles.push(styleText); + } + } + for (const styleText of analysis.template.styles) { + styles.push(styleText); + } + + analysis.meta.styles = styles; + } + compileFull( node: ClassDeclaration, analysis: Readonly, resolution: Readonly, pool: ConstantPool): CompileResult[] { + if (analysis.template.errors !== null && analysis.template.errors.length > 0) { + return []; + } const meta: R3ComponentMetadata = {...analysis.meta, ...resolution}; + const fac = compileNgFactoryDefField(toFactoryMetadata(meta, FactoryTarget.Component)); const def = compileComponentFromMetadata(meta, pool, makeBindingParser()); - return this.compileComponent(analysis, def); + return compileResults(fac, def, analysis.metadataStmt, 'ɵcmp'); } compilePartial( node: ClassDeclaration, analysis: Readonly, resolution: Readonly): CompileResult[] { - const meta: R3ComponentMetadata = {...analysis.meta, ...resolution}; - const def = compileDeclareComponentFromMetadata(meta, analysis.template); - return this.compileComponent(analysis, def); - } - - private compileComponent( - analysis: Readonly, - {expression: initializer, type}: R3ComponentDef): CompileResult[] { - const factoryRes = compileNgFactoryDefField({ - ...analysis.meta, - injectFn: Identifiers.directiveInject, - target: R3FactoryTarget.Component, - }); - if (analysis.metadataStmt !== null) { - factoryRes.statements.push(analysis.metadataStmt); + if (analysis.template.errors !== null && analysis.template.errors.length > 0) { + return []; } - return [ - factoryRes, { - name: 'ɵcmp', - initializer, - statements: [], - type, - } - ]; + const meta: R3ComponentMetadata = {...analysis.meta, ...resolution}; + const fac = compileDeclareFactory(toFactoryMetadata(meta, FactoryTarget.Component)); + const def = compileDeclareComponentFromMetadata(meta, analysis.template); + return compileResults(fac, def, analysis.metadataStmt, 'ɵcmp'); } private _resolveLiteral(decorator: Decorator): ts.ObjectLiteralExpression { @@ -671,20 +915,54 @@ export class ComponentDecoratorHandler implements return resolved; } - private _extractStyleUrls(component: Map, extraUrls: string[]): - string[]|null { + private _extractComponentStyleUrls( + component: Map, + ): StyleUrlMeta[] { if (!component.has('styleUrls')) { - return extraUrls.length > 0 ? extraUrls : null; + return []; } - const styleUrlsExpr = component.get('styleUrls')!; - const styleUrls = this.evaluator.evaluate(styleUrlsExpr); - if (!Array.isArray(styleUrls) || !styleUrls.every(url => typeof url === 'string')) { - throw createValueHasWrongTypeError( - styleUrlsExpr, styleUrls, 'styleUrls must be an array of strings'); + return this._extractStyleUrlsFromExpression(component.get('styleUrls')!); + } + + private _extractStyleUrlsFromExpression(styleUrlsExpr: ts.Expression): StyleUrlMeta[] { + const styleUrls: StyleUrlMeta[] = []; + + if (ts.isArrayLiteralExpression(styleUrlsExpr)) { + for (const styleUrlExpr of styleUrlsExpr.elements) { + if (ts.isSpreadElement(styleUrlExpr)) { + styleUrls.push(...this._extractStyleUrlsFromExpression(styleUrlExpr.expression)); + } else { + const styleUrl = this.evaluator.evaluate(styleUrlExpr); + + if (typeof styleUrl !== 'string') { + throw createValueHasWrongTypeError(styleUrlExpr, styleUrl, 'styleUrl must be a string'); + } + + styleUrls.push({ + url: styleUrl, + source: ResourceTypeForDiagnostics.StylesheetFromDecorator, + nodeForError: styleUrlExpr, + }); + } + } + } else { + const evaluatedStyleUrls = this.evaluator.evaluate(styleUrlsExpr); + if (!isStringArray(evaluatedStyleUrls)) { + throw createValueHasWrongTypeError( + styleUrlsExpr, evaluatedStyleUrls, 'styleUrls must be an array of strings'); + } + + for (const styleUrl of evaluatedStyleUrls) { + styleUrls.push({ + url: styleUrl, + source: ResourceTypeForDiagnostics.StylesheetFromDecorator, + nodeForError: styleUrlsExpr, + }); + } } - styleUrls.push(...extraUrls); - return styleUrls as string[]; + + return styleUrls; } private _extractStyleResources(component: Map, containingFile: string): @@ -700,7 +978,9 @@ export class ComponentDecoratorHandler implements const styleUrlsExpr = component.get('styleUrls'); if (styleUrlsExpr !== undefined && ts.isArrayLiteralExpression(styleUrlsExpr)) { for (const expression of stringLiteralElements(styleUrlsExpr)) { - const resourceUrl = this.resourceLoader.resolve(expression.text, containingFile); + const resourceUrl = this._resolveResourceOrThrow( + expression.text, containingFile, expression, + ResourceTypeForDiagnostics.StylesheetFromDecorator); styles.add({path: absoluteFrom(resourceUrl), expression}); } } @@ -717,7 +997,7 @@ export class ComponentDecoratorHandler implements private _preloadAndParseTemplate( node: ClassDeclaration, decorator: Decorator, component: Map, - containingFile: string): Promise { + containingFile: string): Promise { if (component.has('templateUrl')) { // Extract the templateUrl and preload it. const templateUrlExpr = component.get('templateUrl')!; @@ -726,15 +1006,17 @@ export class ComponentDecoratorHandler implements throw createValueHasWrongTypeError( templateUrlExpr, templateUrl, 'templateUrl must be a string'); } - const resourceUrl = this.resourceLoader.resolve(templateUrl, containingFile); - const templatePromise = this.resourceLoader.preload(resourceUrl); + const resourceUrl = this._resolveResourceOrThrow( + templateUrl, containingFile, templateUrlExpr, ResourceTypeForDiagnostics.Template); + const templatePromise = + this.resourceLoader.preload(resourceUrl, {type: 'template', containingFile}); // If the preload worked, then actually load and parse the template, and wait for any style // URLs to resolve. if (templatePromise !== undefined) { return templatePromise.then(() => { - const template = - this._extractExternalTemplate(node, component, templateUrlExpr, resourceUrl); + const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile); + const template = this.extractTemplate(node, templateDecl); this.preanalyzeTemplateCache.set(node, template); return template; }); @@ -742,92 +1024,139 @@ export class ComponentDecoratorHandler implements return Promise.resolve(null); } } else { - const template = this._extractInlineTemplate(node, decorator, component, containingFile); + const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile); + const template = this.extractTemplate(node, templateDecl); this.preanalyzeTemplateCache.set(node, template); return Promise.resolve(template); } } - private _extractExternalTemplate( - node: ClassDeclaration, component: Map, templateUrlExpr: ts.Expression, - resourceUrl: string): ParsedTemplateWithSource { - const templateStr = this.resourceLoader.load(resourceUrl); - if (this.depTracker !== null) { - this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(resourceUrl)); - } + private extractTemplate(node: ClassDeclaration, template: TemplateDeclaration): + ParsedTemplateWithSource { + if (template.isInline) { + let templateStr: string; + let templateLiteral: ts.Node|null = null; + let templateUrl: string = ''; + let templateRange: LexerRange|null = null; + let sourceMapping: TemplateSourceMapping; + let escapedString = false; + // We only support SourceMaps for inline templates that are simple string literals. + if (ts.isStringLiteral(template.expression) || + ts.isNoSubstitutionTemplateLiteral(template.expression)) { + // the start and end of the `templateExpr` node includes the quotation marks, which we must + // strip + templateRange = getTemplateRange(template.expression); + templateStr = template.expression.getSourceFile().text; + templateLiteral = template.expression; + templateUrl = template.templateUrl; + escapedString = true; + sourceMapping = { + type: 'direct', + node: template.expression, + }; + } else { + const resolvedTemplate = this.evaluator.evaluate(template.expression); + if (typeof resolvedTemplate !== 'string') { + throw createValueHasWrongTypeError( + template.expression, resolvedTemplate, 'template must be a string'); + } + templateStr = resolvedTemplate; + sourceMapping = { + type: 'indirect', + node: template.expression, + componentClass: node, + template: templateStr, + }; + } - const template = this._parseTemplate( - component, templateStr, /* templateLiteral */ null, sourceMapUrl(resourceUrl), - /* templateRange */ undefined, - /* escapedString */ false); - - return { - ...template, - sourceMapping: { - type: 'external', - componentClass: node, - node: templateUrlExpr, - template: templateStr, - templateUrl: resourceUrl, - }, - }; - } - - private _extractInlineTemplate( - node: ClassDeclaration, decorator: Decorator, component: Map, - containingFile: string): ParsedTemplateWithSource { - if (!component.has('template')) { - throw new FatalDiagnosticError( - ErrorCode.COMPONENT_MISSING_TEMPLATE, Decorator.nodeForError(decorator), - 'component is missing a template'); - } - const templateExpr = component.get('template')!; - - let templateStr: string; - let templateLiteral: ts.Node|null = null; - let templateUrl: string = ''; - let templateRange: LexerRange|undefined = undefined; - let sourceMapping: TemplateSourceMapping; - let escapedString = false; - // We only support SourceMaps for inline templates that are simple string literals. - if (ts.isStringLiteral(templateExpr) || ts.isNoSubstitutionTemplateLiteral(templateExpr)) { - // the start and end of the `templateExpr` node includes the quotation marks, which we - // must - // strip - templateRange = getTemplateRange(templateExpr); - templateStr = templateExpr.getSourceFile().text; - templateLiteral = templateExpr; - templateUrl = containingFile; - escapedString = true; - sourceMapping = { - type: 'direct', - node: templateExpr as (ts.StringLiteral | ts.NoSubstitutionTemplateLiteral), + return { + ...this._parseTemplate(template, templateStr, templateRange, escapedString), + sourceMapping, + declaration: template, }; } else { - const resolvedTemplate = this.evaluator.evaluate(templateExpr); - if (typeof resolvedTemplate !== 'string') { - throw createValueHasWrongTypeError( - templateExpr, resolvedTemplate, 'template must be a string'); + const templateStr = this.resourceLoader.load(template.resolvedTemplateUrl); + if (this.depTracker !== null) { + this.depTracker.addResourceDependency( + node.getSourceFile(), absoluteFrom(template.resolvedTemplateUrl)); } - templateStr = resolvedTemplate; - sourceMapping = { - type: 'indirect', - node: templateExpr, - componentClass: node, - template: templateStr, + + return { + ...this._parseTemplate( + template, templateStr, /* templateRange */ null, + /* escapedString */ false), + sourceMapping: { + type: 'external', + componentClass: node, + // TODO(alxhub): TS in g3 is unable to make this inference on its own, so cast it here + // until g3 is able to figure this out. + node: (template as ExternalTemplateDeclaration).templateUrlExpression, + template: templateStr, + templateUrl: template.resolvedTemplateUrl, + }, + declaration: template, }; } - - const template = this._parseTemplate( - component, templateStr, templateLiteral, templateUrl, templateRange, escapedString); - - return {...template, sourceMapping}; } private _parseTemplate( - component: Map, templateStr: string, templateLiteral: ts.Node|null, - templateUrl: string, templateRange: LexerRange|undefined, + template: TemplateDeclaration, templateStr: string, templateRange: LexerRange|null, escapedString: boolean): ParsedComponentTemplate { + // We always normalize line endings if the template has been escaped (i.e. is inline). + const i18nNormalizeLineEndingsInICUs = escapedString || this.i18nNormalizeLineEndingsInICUs; + + const parsedTemplate = parseTemplate(templateStr, template.sourceMapUrl, { + preserveWhitespaces: template.preserveWhitespaces, + interpolationConfig: template.interpolationConfig, + range: templateRange ?? undefined, + escapedString, + enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat, + i18nNormalizeLineEndingsInICUs, + isInline: template.isInline, + alwaysAttemptHtmlToR3AstConversion: this.usePoisonedData, + }); + + // 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 + // errors, etc. There are three main problems: + // + // 1. `preserveWhitespaces: false` annihilates the correctness of template source mapping, as + // the whitespace transformation changes the contents of HTML text nodes before they're + // parsed into Angular expressions. + // 2. `preserveLineEndings: false` causes growing misalignments in templates that use '\r\n' + // line endings, by normalizing them to '\n'. + // 3. By default, the template parser strips leading trivia characters (like spaces, tabs, and + // newlines). This also destroys source mapping information. + // + // In order to guarantee the correctness of diagnostics, templates are parsed a second time + // with the above options set to preserve source mappings. + + const {nodes: diagNodes} = parseTemplate(templateStr, template.sourceMapUrl, { + preserveWhitespaces: true, + preserveLineEndings: true, + interpolationConfig: template.interpolationConfig, + range: templateRange ?? undefined, + escapedString, + enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat, + i18nNormalizeLineEndingsInICUs, + leadingTriviaChars: [], + isInline: template.isInline, + alwaysAttemptHtmlToR3AstConversion: this.usePoisonedData, + }); + + return { + ...parsedTemplate, + diagNodes, + template: template.isInline ? new WrappedNodeExpr(template.expression) : templateStr, + templateUrl: template.resolvedTemplateUrl, + isInline: template.isInline, + file: new ParseSourceFile(templateStr, template.resolvedTemplateUrl), + }; + } + + private parseTemplateDeclaration( + decorator: Decorator, component: Map, + containingFile: string): TemplateDeclaration { let preserveWhitespaces: boolean = this.defaultPreserveWhitespaces; if (component.has('preserveWhitespaces')) { const expr = component.get('preserveWhitespaces')!; @@ -850,55 +1179,53 @@ export class ComponentDecoratorHandler implements interpolationConfig = InterpolationConfig.fromArray(value as [string, string]); } - // We always normalize line endings if the template has been escaped (i.e. is inline). - const i18nNormalizeLineEndingsInICUs = escapedString || this.i18nNormalizeLineEndingsInICUs; + if (component.has('templateUrl')) { + const templateUrlExpr = component.get('templateUrl')!; + const templateUrl = this.evaluator.evaluate(templateUrlExpr); + if (typeof templateUrl !== 'string') { + throw createValueHasWrongTypeError( + templateUrlExpr, templateUrl, 'templateUrl must be a string'); + } + const resourceUrl = this._resolveResourceOrThrow( + templateUrl, containingFile, templateUrlExpr, ResourceTypeForDiagnostics.Template); - const isInline = component.has('template'); - const parsedTemplate = parseTemplate(templateStr, templateUrl, { - preserveWhitespaces, - interpolationConfig, - range: templateRange, - escapedString, - enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat, - i18nNormalizeLineEndingsInICUs, - isInline, - }); - - // 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 - // errors, etc. There are two main problems: - // - // 1. `preserveWhitespaces: false` annihilates the correctness of template source mapping, as - // the whitespace transformation changes the contents of HTML text nodes before they're - // parsed into Angular expressions. - // 2. By default, the template parser strips leading trivia characters (like spaces, tabs, and - // newlines). This also destroys source mapping information. - // - // In order to guarantee the correctness of diagnostics, templates are parsed a second time with - // the above options set to preserve source mappings. - - const {nodes: diagNodes} = parseTemplate(templateStr, templateUrl, { - preserveWhitespaces: true, - interpolationConfig, - range: templateRange, - escapedString, - enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat, - i18nNormalizeLineEndingsInICUs, - leadingTriviaChars: [], - isInline, - }); - - return { - ...parsedTemplate, - diagNodes, - template: templateLiteral !== null ? new WrappedNodeExpr(templateLiteral) : templateStr, - templateUrl, - isInline, - file: new ParseSourceFile(templateStr, templateUrl), - }; + return { + isInline: false, + interpolationConfig, + preserveWhitespaces, + templateUrl, + templateUrlExpression: templateUrlExpr, + resolvedTemplateUrl: resourceUrl, + sourceMapUrl: sourceMapUrl(resourceUrl), + }; + } else if (component.has('template')) { + return { + isInline: true, + interpolationConfig, + preserveWhitespaces, + expression: component.get('template')!, + templateUrl: containingFile, + resolvedTemplateUrl: containingFile, + sourceMapUrl: containingFile, + }; + } else { + throw new FatalDiagnosticError( + ErrorCode.COMPONENT_MISSING_TEMPLATE, Decorator.nodeForError(decorator), + 'component is missing a template'); + } } - private _expressionToImportedFile(expr: Expression, origin: ts.SourceFile): ts.SourceFile|null { + private _resolveImportedFile(importedFile: ImportedFile, expr: Expression, origin: ts.SourceFile): + ts.SourceFile|null { + // If `importedFile` is not 'unknown' then it accurately reflects the source file that is + // being imported. + if (importedFile !== 'unknown') { + return importedFile; + } + + // Otherwise `expr` has to be inspected to determine the file that is being imported. If `expr` + // is not an `ExternalExpr` then it does not correspond with an import, so return null in that + // case. if (!(expr instanceof ExternalExpr)) { return null; } @@ -907,24 +1234,70 @@ export class ComponentDecoratorHandler implements return this.moduleResolver.resolveModule(expr.value.moduleName!, origin.fileName); } - private _isCyclicImport(expr: Expression, origin: ts.SourceFile): boolean { - const imported = this._expressionToImportedFile(expr, origin); + /** + * Check whether adding an import from `origin` to the source-file corresponding to `expr` would + * create a cyclic import. + * + * @returns a `Cycle` object if a cycle would be created, otherwise `null`. + */ + private _checkForCyclicImport( + importedFile: ImportedFile, expr: Expression, origin: ts.SourceFile): Cycle|null { + const imported = this._resolveImportedFile(importedFile, expr, origin); if (imported === null) { - return false; + return null; } - // Check whether the import is legal. return this.cycleAnalyzer.wouldCreateCycle(origin, imported); } - private _recordSyntheticImport(expr: Expression, origin: ts.SourceFile): void { - const imported = this._expressionToImportedFile(expr, origin); + private _recordSyntheticImport( + importedFile: ImportedFile, expr: Expression, origin: ts.SourceFile): void { + const imported = this._resolveImportedFile(importedFile, expr, origin); if (imported === null) { return; } this.cycleAnalyzer.recordSyntheticImport(origin, imported); } + + /** + * Resolve the url of a resource relative to the file that contains the reference to it. + * + * Throws a FatalDiagnosticError when unable to resolve the file. + */ + private _resolveResourceOrThrow( + file: string, basePath: string, nodeForError: ts.Node, + resourceType: ResourceTypeForDiagnostics): string { + try { + return this.resourceLoader.resolve(file, basePath); + } catch (e) { + let errorText: string; + switch (resourceType) { + case ResourceTypeForDiagnostics.Template: + errorText = `Could not find template file '${file}'.`; + break; + case ResourceTypeForDiagnostics.StylesheetFromTemplate: + errorText = `Could not find stylesheet file '${file}' linked from the template.`; + break; + case ResourceTypeForDiagnostics.StylesheetFromDecorator: + errorText = `Could not find stylesheet file '${file}'.`; + break; + } + + throw new FatalDiagnosticError( + ErrorCode.COMPONENT_RESOURCE_NOT_FOUND, nodeForError, errorText); + } + } + + private _extractTemplateStyleUrls(template: ParsedTemplateWithSource): StyleUrlMeta[] { + if (template.styleUrls === null) { + return []; + } + + const nodeForError = getTemplateDeclarationNodeForError(template.declaration); + return template.styleUrls.map( + url => ({url, source: ResourceTypeForDiagnostics.StylesheetFromTemplate, nodeForError})); + } } function getTemplateRange(templateExpr: ts.Expression) { @@ -950,6 +1323,23 @@ function sourceMapUrl(resourceUrl: string): string { } } +/** Determines if the result of an evaluation is a string array. */ +function isStringArray(resolvedValue: ResolvedValue): resolvedValue is string[] { + return Array.isArray(resolvedValue) && resolvedValue.every(elem => typeof elem === 'string'); +} + +/** Determines the node to use for debugging purposes for the given TemplateDeclaration. */ +function getTemplateDeclarationNodeForError(declaration: TemplateDeclaration): ts.Node { + // TODO(zarend): Change this to if/else when that is compatible with g3. This uses a switch + // because if/else fails to compile on g3. That is because g3 compiles this in non-strict mode + // where type inference does not work correctly. + switch (declaration.isInline) { + case true: + return declaration.expression; + case false: + return declaration.templateUrlExpression; + } +} /** * Information about the template which was extracted during parsing. @@ -958,14 +1348,6 @@ function sourceMapUrl(resourceUrl: string): string { * some of which might be useful for re-parsing the template with different options. */ export interface ParsedComponentTemplate extends ParsedTemplate { - /** - * A full path to the file which contains the template. - * - * This can be either the original .ts file if the template is inline, or the .html file if an - * external file was used. - */ - templateUrl: string; - /** * True if the original template was stored inline; * False if the template was in an external file. @@ -987,4 +1369,54 @@ export interface ParsedComponentTemplate extends ParsedTemplate { export interface ParsedTemplateWithSource extends ParsedComponentTemplate { sourceMapping: TemplateSourceMapping; + declaration: TemplateDeclaration; +} + +/** + * Common fields extracted from the declaration of a template. + */ +interface CommonTemplateDeclaration { + preserveWhitespaces: boolean; + interpolationConfig: InterpolationConfig; + templateUrl: string; + resolvedTemplateUrl: string; + sourceMapUrl: string; +} + +/** + * Information extracted from the declaration of an inline template. + */ +interface InlineTemplateDeclaration extends CommonTemplateDeclaration { + isInline: true; + expression: ts.Expression; +} + +/** + * Information extracted from the declaration of an external template. + */ +interface ExternalTemplateDeclaration extends CommonTemplateDeclaration { + isInline: false; + templateUrlExpression: ts.Expression; +} + +/** + * The declaration of a template extracted from a component decorator. + * + * This data is extracted and stored separately to faciliate re-interpreting the template + * declaration whenever the compiler is notified of a change to a template file. With this + * information, `ComponentDecoratorHandler` is able to re-read the template and update the component + * record without needing to parse the original decorator again. + */ +type TemplateDeclaration = InlineTemplateDeclaration|ExternalTemplateDeclaration; + +/** + * Generate a diagnostic related information object that describes a potential cyclic import path. + */ +function makeCyclicImportInfo( + ref: Reference, type: string, cycle: Cycle): ts.DiagnosticRelatedInformation { + const name = ref.debugName || '(unknown)'; + const path = cycle.getPath().map(sf => sf.fileName).join(' -> '); + const message = + `The ${type} '${name}' is used in the template but importing it would create a cycle: `; + return makeRelatedInformation(ref.node, message + path); } diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts index 304da8b032..78a775b519 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts @@ -6,22 +6,25 @@ * found in the LICENSE file at https://angular.io/license */ -import {compileDeclareDirectiveFromMetadata, compileDirectiveFromMetadata, ConstantPool, Expression, Identifiers, makeBindingParser, ParsedHostBindings, ParseError, parseHostBindings, R3DependencyMetadata, R3DirectiveDef, R3DirectiveMetadata, R3FactoryTarget, R3QueryMetadata, Statement, verifyHostBindings, WrappedNodeExpr} from '@angular/compiler'; +import {compileDeclareDirectiveFromMetadata, compileDirectiveFromMetadata, ConstantPool, Expression, ExternalExpr, FactoryTarget, getSafePropertyAccessString, makeBindingParser, ParsedHostBindings, ParseError, parseHostBindings, R3DirectiveMetadata, R3FactoryMetadata, R3QueryMetadata, Statement, verifyHostBindings, WrappedNodeExpr} from '@angular/compiler'; +import {emitDistinctChangesOnlyDefaultValue} from '@angular/compiler/src/core'; import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {DefaultImportRecorder, Reference} from '../../imports'; -import {ClassPropertyMapping, DirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata'; +import {areTypeParametersEqual, extractSemanticTypeParameters, isArrayEqual, isSetEqual, isSymbolEqual, SemanticDepGraphUpdater, SemanticSymbol, SemanticTypeParameter} from '../../incremental/semantic_graph'; +import {BindingPropertyName, ClassPropertyMapping, ClassPropertyName, DirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, TemplateGuardMeta} from '../../metadata'; import {extractDirectiveTypeCheckMeta} from '../../metadata/src/util'; import {DynamicValue, EnumValue, PartialEvaluator} from '../../partial_evaluator'; +import {PerfEvent, PerfRecorder} from '../../perf'; 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 {createValueHasWrongTypeError, getDirectiveDiagnostics, getProviderDiagnostics, getUndecoratedClassWithAngularFeaturesDiagnostic} from './diagnostics'; -import {compileNgFactoryDefField} from './factory'; +import {compileDeclareFactory, compileNgFactoryDefField} from './factory'; import {generateSetClassMetadataCall} from './metadata'; -import {createSourceSpan, findAngularDecorator, getConstructorDependencies, isAngularDecorator, readBaseClass, resolveProvidersRequiringFactory, unwrapConstructorDependencies, unwrapExpression, unwrapForwardRef, validateConstructorDependencies, wrapFunctionExpressionsInParens, wrapTypeReference} from './util'; +import {compileResults, createSourceSpan, findAngularDecorator, getConstructorDependencies, isAngularDecorator, readBaseClass, resolveProvidersRequiringFactory, toFactoryMetadata, unwrapConstructorDependencies, unwrapExpression, unwrapForwardRef, validateConstructorDependencies, wrapFunctionExpressionsInParens, wrapTypeReference} from './util'; const EMPTY_OBJECT: {[key: string]: string} = {}; const FIELD_DECORATORS = [ @@ -41,17 +44,144 @@ export interface DirectiveHandlerData { providersRequiringFactory: Set>|null; inputs: ClassPropertyMapping; outputs: ClassPropertyMapping; + isPoisoned: boolean; + isStructural: boolean; +} + +/** + * Represents an Angular directive. Components are represented by `ComponentSymbol`, which inherits + * from this symbol. + */ +export class DirectiveSymbol extends SemanticSymbol { + baseClass: SemanticSymbol|null = null; + + constructor( + decl: ClassDeclaration, public readonly selector: string|null, + public readonly inputs: ClassPropertyMapping, public readonly outputs: ClassPropertyMapping, + public readonly exportAs: string[]|null, + public readonly typeCheckMeta: DirectiveTypeCheckMeta, + public readonly typeParameters: SemanticTypeParameter[]|null) { + super(decl); + } + + isPublicApiAffected(previousSymbol: SemanticSymbol): boolean { + // Note: since components and directives have exactly the same items contributing to their + // public API, it is okay for a directive to change into a component and vice versa without + // the API being affected. + if (!(previousSymbol instanceof DirectiveSymbol)) { + return true; + } + + // Directives and components have a public API of: + // 1. Their selector. + // 2. The binding names of their inputs and outputs; a change in ordering is also considered + // to be a change in public API. + // 3. The list of exportAs names and its ordering. + return this.selector !== previousSymbol.selector || + !isArrayEqual(this.inputs.propertyNames, previousSymbol.inputs.propertyNames) || + !isArrayEqual(this.outputs.propertyNames, previousSymbol.outputs.propertyNames) || + !isArrayEqual(this.exportAs, previousSymbol.exportAs); + } + + isTypeCheckApiAffected(previousSymbol: SemanticSymbol): boolean { + // If the public API of the directive has changed, then so has its type-check API. + if (this.isPublicApiAffected(previousSymbol)) { + return true; + } + + if (!(previousSymbol instanceof DirectiveSymbol)) { + return true; + } + + // The type-check block also depends on the class property names, as writes property bindings + // directly into the backing fields. + if (!isArrayEqual( + Array.from(this.inputs), Array.from(previousSymbol.inputs), isInputMappingEqual) || + !isArrayEqual( + Array.from(this.outputs), Array.from(previousSymbol.outputs), isInputMappingEqual)) { + return true; + } + + // The type parameters of a directive are emitted into the type constructors in the type-check + // block of a component, so if the type parameters are not considered equal then consider the + // type-check API of this directive to be affected. + if (!areTypeParametersEqual(this.typeParameters, previousSymbol.typeParameters)) { + return true; + } + + // The type-check metadata is used during TCB code generation, so any changes should invalidate + // prior type-check files. + if (!isTypeCheckMetaEqual(this.typeCheckMeta, previousSymbol.typeCheckMeta)) { + return true; + } + + // Changing the base class of a directive means that its inputs/outputs etc may have changed, + // so the type-check block of components that use this directive needs to be regenerated. + if (!isBaseClassEqual(this.baseClass, previousSymbol.baseClass)) { + return true; + } + + return false; + } +} + +function isInputMappingEqual( + current: [ClassPropertyName, BindingPropertyName], + previous: [ClassPropertyName, BindingPropertyName]): boolean { + return current[0] === previous[0] && current[1] === previous[1]; +} + +function isTypeCheckMetaEqual( + current: DirectiveTypeCheckMeta, previous: DirectiveTypeCheckMeta): boolean { + if (current.hasNgTemplateContextGuard !== previous.hasNgTemplateContextGuard) { + return false; + } + if (current.isGeneric !== previous.isGeneric) { + // Note: changes in the number of type parameters is also considered in `areTypeParametersEqual` + // so this check is technically not needed; it is done anyway for completeness in terms of + // whether the `DirectiveTypeCheckMeta` struct itself compares equal or not. + return false; + } + if (!isArrayEqual(current.ngTemplateGuards, previous.ngTemplateGuards, isTemplateGuardEqual)) { + return false; + } + if (!isSetEqual(current.coercedInputFields, previous.coercedInputFields)) { + return false; + } + if (!isSetEqual(current.restrictedInputFields, previous.restrictedInputFields)) { + return false; + } + if (!isSetEqual(current.stringLiteralInputFields, previous.stringLiteralInputFields)) { + return false; + } + if (!isSetEqual(current.undeclaredInputFields, previous.undeclaredInputFields)) { + return false; + } + return true; +} + +function isTemplateGuardEqual(current: TemplateGuardMeta, previous: TemplateGuardMeta): boolean { + return current.inputName === previous.inputName && current.type === previous.type; +} + +function isBaseClassEqual(current: SemanticSymbol|null, previous: SemanticSymbol|null): boolean { + if (current === null || previous === null) { + return current === previous; + } + + return isSymbolEqual(current, previous); } export class DirectiveDecoratorHandler implements - DecoratorHandler { + DecoratorHandler { constructor( private reflector: ReflectionHost, private evaluator: PartialEvaluator, private metaRegistry: MetadataRegistry, private scopeRegistry: LocalModuleScopeRegistry, private metaReader: MetadataReader, private defaultImportRecorder: DefaultImportRecorder, private injectableRegistry: InjectableClassRegistry, private isCore: boolean, + private semanticDepGraphUpdater: SemanticDepGraphUpdater|null, private annotateForClosureCompiler: boolean, - private compileUndecoratedClassesWithAngularFeatures: boolean) {} + private compileUndecoratedClassesWithAngularFeatures: boolean, private perf: PerfRecorder) {} readonly precedence = HandlerPrecedence.PRIMARY; readonly name = DirectiveDecoratorHandler.name; @@ -82,6 +212,8 @@ export class DirectiveDecoratorHandler implements return {diagnostics: [getUndecoratedClassWithAngularFeaturesDiagnostic(node)]}; } + this.perf.eventCount(PerfEvent.AnalyzeDirective); + const directiveResult = extractDirectiveMetadata( node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore, flags, this.annotateForClosureCompiler); @@ -106,11 +238,21 @@ export class DirectiveDecoratorHandler implements this.annotateForClosureCompiler), baseClass: readBaseClass(node, this.reflector, this.evaluator), typeCheckMeta: extractDirectiveTypeCheckMeta(node, directiveResult.inputs, this.reflector), - providersRequiringFactory + providersRequiringFactory, + isPoisoned: false, + isStructural: directiveResult.isStructural, } }; } + symbol(node: ClassDeclaration, analysis: Readonly): DirectiveSymbol { + const typeParameters = extractSemanticTypeParameters(node); + + return new DirectiveSymbol( + node, analysis.meta.selector, analysis.inputs, analysis.outputs, analysis.meta.exportAs, + analysis.typeCheckMeta, typeParameters); + } + register(node: ClassDeclaration, analysis: Readonly): void { // Register this directive's information with the `MetadataRegistry`. This ensures that // the information about the directive is available during the compile() phase. @@ -126,14 +268,20 @@ export class DirectiveDecoratorHandler implements isComponent: false, baseClass: analysis.baseClass, ...analysis.typeCheckMeta, + isPoisoned: analysis.isPoisoned, + isStructural: analysis.isStructural, }); this.injectableRegistry.registerInjectable(node); } - resolve(node: ClassDeclaration, analysis: DirectiveHandlerData): ResolveResult { - const diagnostics: ts.Diagnostic[] = []; + resolve(node: ClassDeclaration, analysis: DirectiveHandlerData, symbol: DirectiveSymbol): + ResolveResult { + if (this.semanticDepGraphUpdater !== null && analysis.baseClass instanceof Reference) { + symbol.baseClass = this.semanticDepGraphUpdater.getSymbol(analysis.baseClass.node); + } + const diagnostics: ts.Diagnostic[] = []; if (analysis.providersRequiringFactory !== null && analysis.meta.providers instanceof WrappedNodeExpr) { const providerDiagnostics = getProviderDiagnostics( @@ -154,36 +302,17 @@ export class DirectiveDecoratorHandler implements compileFull( node: ClassDeclaration, analysis: Readonly, resolution: Readonly, pool: ConstantPool): CompileResult[] { + const fac = compileNgFactoryDefField(toFactoryMetadata(analysis.meta, FactoryTarget.Directive)); const def = compileDirectiveFromMetadata(analysis.meta, pool, makeBindingParser()); - return this.compileDirective(analysis, def); + return compileResults(fac, def, analysis.metadataStmt, 'ɵdir'); } compilePartial( node: ClassDeclaration, analysis: Readonly, resolution: Readonly): CompileResult[] { + const fac = compileDeclareFactory(toFactoryMetadata(analysis.meta, FactoryTarget.Directive)); const def = compileDeclareDirectiveFromMetadata(analysis.meta); - return this.compileDirective(analysis, def); - } - - private compileDirective( - analysis: Readonly, - {expression: initializer, type}: R3DirectiveDef): CompileResult[] { - const factoryRes = compileNgFactoryDefField({ - ...analysis.meta, - injectFn: Identifiers.directiveInject, - target: R3FactoryTarget.Directive, - }); - if (analysis.metadataStmt !== null) { - factoryRes.statements.push(analysis.metadataStmt); - } - return [ - factoryRes, { - name: 'ɵdir', - initializer, - statements: [], - type, - } - ]; + return compileResults(fac, def, analysis.metadataStmt, 'ɵdir'); } /** @@ -223,6 +352,7 @@ export function extractDirectiveMetadata( metadata: R3DirectiveMetadata, inputs: ClassPropertyMapping, outputs: ClassPropertyMapping, + isStructural: boolean; }|undefined { let directive: Map; if (decorator === null || decorator.args === null || decorator.args.length === 0) { @@ -338,16 +468,19 @@ export function extractDirectiveMetadata( } const rawCtorDeps = getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore); - let ctorDeps: R3DependencyMetadata[]|'invalid'|null; // Non-abstract directives (those with a selector) require valid constructor dependencies, whereas // abstract directives are allowed to have invalid dependencies, given that a subclass may call // the constructor explicitly. - if (selector !== null) { - ctorDeps = validateConstructorDependencies(clazz, rawCtorDeps); - } else { - ctorDeps = unwrapConstructorDependencies(rawCtorDeps); - } + const ctorDeps = selector !== null ? validateConstructorDependencies(clazz, rawCtorDeps) : + unwrapConstructorDependencies(rawCtorDeps); + + // Structural directives must have a `TemplateRef` dependency. + const isStructural = ctorDeps !== null && ctorDeps !== 'invalid' && + ctorDeps.some( + dep => (dep.token instanceof ExternalExpr) && + dep.token.value.moduleName === '@angular/core' && + dep.token.value.name === 'TemplateRef'); // Detect if the component inherits from another class const usesInheritance = reflector.hasBaseClass(clazz); @@ -383,6 +516,7 @@ export function extractDirectiveMetadata( metadata, inputs, outputs, + isStructural, }; } @@ -417,6 +551,7 @@ export function extractQueryMetadata( let read: Expression|null = null; // The default value for descendants is true for every decorator except @ContentChildren. let descendants: boolean = name !== 'ContentChildren'; + let emitDistinctChangesOnly: boolean = emitDistinctChangesOnlyDefaultValue; if (args.length === 2) { const optionsExpr = unwrapExpression(args[1]); if (!ts.isObjectLiteralExpression(optionsExpr)) { @@ -439,6 +574,17 @@ export function extractQueryMetadata( descendants = descendantsValue; } + if (options.has('emitDistinctChangesOnly')) { + const emitDistinctChangesOnlyExpr = options.get('emitDistinctChangesOnly')!; + const emitDistinctChangesOnlyValue = evaluator.evaluate(emitDistinctChangesOnlyExpr); + if (typeof emitDistinctChangesOnlyValue !== 'boolean') { + throw createValueHasWrongTypeError( + emitDistinctChangesOnlyExpr, emitDistinctChangesOnlyValue, + `@${name} options.emitDistinctChangesOnly must be a boolean`); + } + emitDistinctChangesOnly = emitDistinctChangesOnlyValue; + } + if (options.has('static')) { const staticValue = evaluator.evaluate(options.get('static')!); if (typeof staticValue !== 'boolean') { @@ -461,6 +607,7 @@ export function extractQueryMetadata( descendants, read, static: isStatic, + emitDistinctChangesOnly, }; } @@ -719,7 +866,11 @@ export function extractHostBindings( hostPropertyName = resolved; } - bindings.properties[hostPropertyName] = member.name; + // Since this is a decorator, we know that the value is a class member. Always access it + // through `this` so that further down the line it can't be confused for a literal value + // (e.g. if there's a property called `true`). There is no size penalty, because all + // values (except literals) are converted to `ctx.propName` eventually. + bindings.properties[hostPropertyName] = getSafePropertyAccessString('this', member.name); }); }); diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/factory.ts b/packages/compiler-cli/src/ngtsc/annotations/src/factory.ts index 40368957ff..ae6c44e7ed 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/factory.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/factory.ts @@ -6,11 +6,16 @@ * found in the LICENSE file at https://angular.io/license */ -import {compileFactoryFunction, R3FactoryMetadata} from '@angular/compiler'; +import {compileDeclareFactoryFunction, compileFactoryFunction, R3FactoryMetadata} from '@angular/compiler'; import {CompileResult} from '../../transform'; export function compileNgFactoryDefField(metadata: R3FactoryMetadata): CompileResult { const res = compileFactoryFunction(metadata); - return {name: 'ɵfac', initializer: res.factory, statements: res.statements, type: res.type}; + return {name: 'ɵfac', initializer: res.expression, statements: res.statements, type: res.type}; +} + +export function compileDeclareFactory(metadata: R3FactoryMetadata): CompileResult { + const res = compileDeclareFactoryFunction(metadata); + return {name: 'ɵfac', initializer: res.expression, statements: res.statements, type: res.type}; } diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts b/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts index a9fbbe86ce..63636a2ce6 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts @@ -6,18 +6,19 @@ * found in the LICENSE file at https://angular.io/license */ -import {compileInjectable as compileIvyInjectable, Expression, Identifiers, LiteralExpr, R3DependencyMetadata, R3FactoryTarget, R3InjectableMetadata, R3ResolvedDependencyType, Statement, WrappedNodeExpr} from '@angular/compiler'; +import {compileInjectable as compileIvyInjectable, Expression, FactoryTarget, LiteralExpr, R3DependencyMetadata, R3FactoryMetadata, R3InjectableMetadata, Statement, WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {DefaultImportRecorder} from '../../imports'; import {InjectableClassRegistry} from '../../metadata'; +import {PerfEvent, PerfRecorder} from '../../perf'; import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../transform'; -import {compileNgFactoryDefField} from './factory'; +import {compileDeclareFactory, compileNgFactoryDefField} from './factory'; import {generateSetClassMetadataCall} from './metadata'; -import {findAngularDecorator, getConstructorDependencies, getValidConstructorDependencies, isAngularCore, unwrapConstructorDependencies, unwrapForwardRef, validateConstructorDependencies, wrapTypeReference} from './util'; +import {findAngularDecorator, getConstructorDependencies, getValidConstructorDependencies, isAngularCore, toFactoryMetadata, unwrapConstructorDependencies, unwrapForwardRef, validateConstructorDependencies, wrapTypeReference} from './util'; export interface InjectableHandlerData { meta: R3InjectableMetadata; @@ -30,11 +31,11 @@ export interface InjectableHandlerData { * Adapts the `compileIvyInjectable` compiler for `@Injectable` decorators to the Ivy compiler. */ export class InjectableDecoratorHandler implements - DecoratorHandler { + DecoratorHandler { constructor( private reflector: ReflectionHost, private defaultImportRecorder: DefaultImportRecorder, private isCore: boolean, private strictCtorDeps: boolean, - private injectableRegistry: InjectableClassRegistry, + private injectableRegistry: InjectableClassRegistry, private perf: PerfRecorder, /** * What to do if the injectable already contains a ɵprov property. * @@ -64,6 +65,8 @@ export class InjectableDecoratorHandler implements analyze(node: ClassDeclaration, decorator: Readonly): AnalysisOutput { + this.perf.eventCount(PerfEvent.AnalyzeInjectable); + const meta = extractInjectableMetadata(node, decorator, this.reflector); const decorators = this.reflector.getDecoratorsOfDeclaration(node); @@ -83,6 +86,10 @@ export class InjectableDecoratorHandler implements }; } + symbol(): null { + return null; + } + register(node: ClassDeclaration): void { this.injectableRegistry.registerInjectable(node); } @@ -94,15 +101,8 @@ export class InjectableDecoratorHandler implements if (analysis.needsFactory) { const meta = analysis.meta; - const factoryRes = compileNgFactoryDefField({ - name: meta.name, - type: meta.type, - internalType: meta.internalType, - typeArgumentCount: meta.typeArgumentCount, - deps: analysis.ctorDeps, - injectFn: Identifiers.inject, - target: R3FactoryTarget.Injectable, - }); + const factoryRes = compileNgFactoryDefField( + toFactoryMetadata({...meta, deps: analysis.ctorDeps}, FactoryTarget.Injectable)); if (analysis.metadataStmt !== null) { factoryRes.statements.push(analysis.metadataStmt); } @@ -122,6 +122,37 @@ export class InjectableDecoratorHandler implements } + return results; + } + + compilePartial(node: ClassDeclaration, analysis: Readonly): + CompileResult[] { + const res = compileIvyInjectable(analysis.meta); + const statements = res.statements; + const results: CompileResult[] = []; + + if (analysis.needsFactory) { + const meta = analysis.meta; + const factoryRes = compileDeclareFactory( + toFactoryMetadata({...meta, deps: analysis.ctorDeps}, FactoryTarget.Injectable)); + if (analysis.metadataStmt !== null) { + factoryRes.statements.push(analysis.metadataStmt); + } + results.push(factoryRes); + } + + const ɵprov = this.reflector.getMembersOfClass(node).find(member => member.name === 'ɵprov'); + if (ɵprov !== undefined && this.errorOnDuplicateProv) { + throw new FatalDiagnosticError( + ErrorCode.INJECTABLE_DUPLICATE_PROV, ɵprov.nameNode || ɵprov.node || node, + 'Injectables cannot contain a static ɵprov property, because the compiler is going to generate one.'); + } + + if (ɵprov === undefined) { + // Only add a new ɵprov if there is not one already + results.push({name: 'ɵprov', initializer: res.expression, statements, type: res.type}); + } + return results; } } @@ -277,9 +308,8 @@ function extractInjectableCtorDeps( function getDep(dep: ts.Expression, reflector: ReflectionHost): R3DependencyMetadata { const meta: R3DependencyMetadata = { token: new WrappedNodeExpr(dep), - attribute: null, + attributeNameType: null, host: false, - resolved: R3ResolvedDependencyType.Token, optional: false, self: false, skipSelf: false, diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts b/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts index 36701e189d..69056e6161 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, literalMap, NONE_TYPE, ReturnStatement, Statement, WrappedNodeExpr} from '@angular/compiler'; +import {devOnlyGuardedExpression, 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'; @@ -17,7 +17,8 @@ import {valueReferenceToExpression, wrapFunctionExpressionsInParens} from './uti /** * Given a class declaration, generate a call to `setClassMetadata` with the Angular metadata - * present on the class or its member fields. + * present on the class or its member fields. An ngDevMode guard is used to allow the call to be + * tree-shaken away, as the `setClassMetadata` invocation is only needed for testing purposes. * * If no such metadata is present, this function returns `null`. Otherwise, the call is returned * as a `Statement` for inclusion along with the class. @@ -93,14 +94,8 @@ export function generateSetClassMetadataCall( metaCtorParameters, new WrappedNodeExpr(metaPropDecorators), ]); - const iifeFn = new FunctionExpr([], [fnCall.toStmt()], NONE_TYPE); - const iife = new InvokeFunctionExpr( - /* fn */ iifeFn, - /* args */[], - /* type */ undefined, - /* sourceSpan */ undefined, - /* pure */ true); - return iife.toStmt(); + const iife = new FunctionExpr([], [devOnlyGuardedExpression(fnCall).toStmt()]); + return iife.callFn([]).toStmt(); } /** 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 67ad6373dd..d23d88fbd8 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts @@ -6,14 +6,16 @@ * found in the LICENSE file at https://angular.io/license */ -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 {compileDeclareInjectorFromMetadata, compileDeclareNgModuleFromMetadata, compileInjector, compileNgModule, CUSTOM_ELEMENTS_SCHEMA, Expression, ExternalExpr, FactoryTarget, Identifiers as R3, InvokeFunctionExpr, LiteralArrayExpr, LiteralExpr, NO_ERRORS_SCHEMA, R3CompiledExpression, R3FactoryMetadata, R3Identifiers, R3InjectorMetadata, R3NgModuleMetadata, R3Reference, SchemaMetadata, Statement, STRING_TYPE, WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError, makeDiagnostic, makeRelatedInformation} from '../../diagnostics'; import {DefaultImportRecorder, Reference, ReferenceEmitter} from '../../imports'; +import {isArrayEqual, isReferenceEqual, isSymbolEqual, SemanticReference, SemanticSymbol} from '../../incremental/semantic_graph'; import {InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata'; import {PartialEvaluator, ResolvedValue} from '../../partial_evaluator'; -import {ClassDeclaration, DeclarationNode, Decorator, isNamedClassDeclaration, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../reflection'; +import {PerfEvent, PerfRecorder} from '../../perf'; +import {ClassDeclaration, Decorator, isNamedClassDeclaration, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../reflection'; import {NgModuleRouteAnalyzer} from '../../routing'; import {LocalModuleScopeRegistry, ScopeData} from '../../scope'; import {FactoryTracker} from '../../shims/api'; @@ -21,6 +23,7 @@ import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPr import {getSourceFile} from '../../util/src/typescript'; import {createValueHasWrongTypeError, getProviderDiagnostics} from './diagnostics'; +import {compileDeclareFactory, compileNgFactoryDefField} from './factory'; import {generateSetClassMetadataCall} from './metadata'; import {ReferencesRegistry} from './references_registry'; import {combineResolvers, findAngularDecorator, forwardRefResolver, getValidConstructorDependencies, isExpressionForwardReference, resolveProvidersRequiringFactory, toR3Reference, unwrapExpression, wrapFunctionExpressionsInParens, wrapTypeReference} from './util'; @@ -28,6 +31,7 @@ import {combineResolvers, findAngularDecorator, forwardRefResolver, getValidCons export interface NgModuleAnalysis { mod: R3NgModuleMetadata; inj: R3InjectorMetadata; + fac: R3FactoryMetadata; metadataStmt: Statement|null; declarations: Reference[]; rawDeclarations: ts.Expression|null; @@ -44,13 +48,82 @@ export interface NgModuleResolution { injectorImports: Expression[]; } +/** + * Represents an Angular NgModule. + */ +export class NgModuleSymbol extends SemanticSymbol { + private remotelyScopedComponents: { + component: SemanticSymbol, + usedDirectives: SemanticReference[], + usedPipes: SemanticReference[] + }[] = []; + + isPublicApiAffected(previousSymbol: SemanticSymbol): boolean { + if (!(previousSymbol instanceof NgModuleSymbol)) { + return true; + } + + // NgModules don't have a public API that could affect emit of Angular decorated classes. + return false; + } + + isEmitAffected(previousSymbol: SemanticSymbol): boolean { + if (!(previousSymbol instanceof NgModuleSymbol)) { + return true; + } + + // compare our remotelyScopedComponents to the previous symbol + if (previousSymbol.remotelyScopedComponents.length !== this.remotelyScopedComponents.length) { + return true; + } + + for (const currEntry of this.remotelyScopedComponents) { + const prevEntry = previousSymbol.remotelyScopedComponents.find(prevEntry => { + return isSymbolEqual(prevEntry.component, currEntry.component); + }); + + if (prevEntry === undefined) { + // No previous entry was found, which means that this component became remotely scoped and + // hence this NgModule needs to be re-emitted. + return true; + } + + if (!isArrayEqual(currEntry.usedDirectives, prevEntry.usedDirectives, isReferenceEqual)) { + // The list of used directives or their order has changed. Since this NgModule emits + // references to the list of used directives, it should be re-emitted to update this list. + // Note: the NgModule does not have to be re-emitted when any of the directives has had + // their public API changed, as the NgModule only emits a reference to the symbol by its + // name. Therefore, testing for symbol equality is sufficient. + return true; + } + + if (!isArrayEqual(currEntry.usedPipes, prevEntry.usedPipes, isReferenceEqual)) { + return true; + } + } + return false; + } + + isTypeCheckApiAffected(previousSymbol: SemanticSymbol): boolean { + if (!(previousSymbol instanceof NgModuleSymbol)) { + return true; + } + + return false; + } + + addRemotelyScopedComponent( + component: SemanticSymbol, usedDirectives: SemanticReference[], + usedPipes: SemanticReference[]): void { + this.remotelyScopedComponents.push({component, usedDirectives, usedPipes}); + } +} + /** * Compiles @NgModule annotations to ngModuleDef fields. - * - * TODO(alxhub): handle injector side of things as well. */ export class NgModuleDecoratorHandler implements - DecoratorHandler { + DecoratorHandler { constructor( private reflector: ReflectionHost, private evaluator: PartialEvaluator, private metaReader: MetadataReader, private metaRegistry: MetadataRegistry, @@ -60,7 +133,8 @@ export class NgModuleDecoratorHandler implements private factoryTracker: FactoryTracker|null, private defaultImportRecorder: DefaultImportRecorder, private annotateForClosureCompiler: boolean, - private injectableRegistry: InjectableClassRegistry, private localeId?: string) {} + private injectableRegistry: InjectableClassRegistry, private perf: PerfRecorder, + private localeId?: string) {} readonly precedence = HandlerPrecedence.PRIMARY; readonly name = NgModuleDecoratorHandler.name; @@ -83,6 +157,8 @@ export class NgModuleDecoratorHandler implements analyze(node: ClassDeclaration, decorator: Readonly): AnalysisOutput { + this.perf.eventCount(PerfEvent.AnalyzeNgModule); + const name = node.name.text; if (decorator.args === null || decorator.args.length > 1) { throw new FatalDiagnosticError( @@ -227,7 +303,7 @@ export class NgModuleDecoratorHandler implements const internalType = new WrappedNodeExpr(this.reflector.getInternalNameOfClass(node)); const adjacentType = new WrappedNodeExpr(this.reflector.getAdjacentNameOfClass(node)); - const ngModuleDef: R3NgModuleMetadata = { + const ngModuleMetadata: R3NgModuleMetadata = { type, internalType, adjacentType, @@ -261,22 +337,31 @@ export class NgModuleDecoratorHandler implements this.routeAnalyzer.add(node.getSourceFile(), name, rawImports, rawExports, rawProviders); } - const ngInjectorDef: R3InjectorMetadata = { + const injectorMetadata: R3InjectorMetadata = { name, type, internalType, - deps: getValidConstructorDependencies( - node, this.reflector, this.defaultImportRecorder, this.isCore), providers: wrapperProviders, imports: injectorImports, }; + const factoryMetadata: R3FactoryMetadata = { + name, + type, + internalType, + typeArgumentCount: 0, + deps: getValidConstructorDependencies( + node, this.reflector, this.defaultImportRecorder, this.isCore), + target: FactoryTarget.NgModule, + }; + return { analysis: { id, - schemas: schemas, - mod: ngModuleDef, - inj: ngInjectorDef, + schemas, + mod: ngModuleMetadata, + inj: injectorMetadata, + fac: factoryMetadata, declarations: declarationRefs, rawDeclarations, imports: importRefs, @@ -293,6 +378,10 @@ export class NgModuleDecoratorHandler implements }; } + symbol(node: ClassDeclaration): NgModuleSymbol { + return new NgModuleSymbol(node); + } + register(node: ClassDeclaration, analysis: NgModuleAnalysis): void { // Register this module's information with the LocalModuleScopeRegistry. This ensures that // during the compile() phase, the module's metadata is available for selector scope @@ -336,13 +425,13 @@ export class NgModuleDecoratorHandler implements injectorImports: [], }; - if (scope !== null && scope !== 'error') { + if (scope !== null && !scope.compilation.isPoisoned) { // Using the scope information, extend the injector's imports using the modules that are // specified as module exports. const context = getSourceFile(node); for (const exportRef of analysis.exports) { if (isNgModule(exportRef.node, scope.compilation)) { - data.injectorImports.push(this.refEmitter.emit(exportRef, context)); + data.injectorImports.push(this.refEmitter.emit(exportRef, context).expression); } } @@ -361,7 +450,8 @@ export class NgModuleDecoratorHandler implements return {diagnostics}; } - if (scope === null || scope === 'error' || scope.reexports === null) { + if (scope === null || scope.compilation.isPoisoned || scope.exported.isPoisoned || + scope.reexports === null) { return {data}; } else { return { @@ -372,29 +462,66 @@ export class NgModuleDecoratorHandler implements } compileFull( - node: ClassDeclaration, analysis: Readonly, - resolution: Readonly): CompileResult[] { - // Merge the injector imports (which are 'exports' that were later found to be NgModules) - // computed during resolution with the ones from analysis. - const ngInjectorDef = compileInjector({ - ...analysis.inj, - imports: [...analysis.inj.imports, ...resolution.injectorImports], - }); - const ngModuleDef = compileNgModule(analysis.mod); - const ngModuleStatements = ngModuleDef.additionalStatements; - if (analysis.metadataStmt !== null) { - ngModuleStatements.push(analysis.metadataStmt); + node: ClassDeclaration, + {inj, mod, fac, metadataStmt, declarations}: Readonly, + {injectorImports}: Readonly): CompileResult[] { + const factoryFn = compileNgFactoryDefField(fac); + const ngInjectorDef = compileInjector(this.mergeInjectorImports(inj, injectorImports)); + const ngModuleDef = compileNgModule(mod); + const statements = ngModuleDef.statements; + this.insertMetadataStatement(statements, metadataStmt); + this.appendRemoteScopingStatements(statements, node, declarations); + + return this.compileNgModule(factoryFn, ngInjectorDef, ngModuleDef); + } + + compilePartial( + node: ClassDeclaration, {inj, fac, mod, metadataStmt}: Readonly, + {injectorImports}: Readonly): CompileResult[] { + const factoryFn = compileDeclareFactory(fac); + const injectorDef = + compileDeclareInjectorFromMetadata(this.mergeInjectorImports(inj, injectorImports)); + const ngModuleDef = compileDeclareNgModuleFromMetadata(mod); + this.insertMetadataStatement(ngModuleDef.statements, metadataStmt); + // NOTE: no remote scoping required as this is banned in partial compilation. + return this.compileNgModule(factoryFn, injectorDef, ngModuleDef); + } + + /** + * Merge the injector imports (which are 'exports' that were later found to be NgModules) + * computed during resolution with the ones from analysis. + */ + private mergeInjectorImports(inj: R3InjectorMetadata, injectorImports: Expression[]): + R3InjectorMetadata { + return {...inj, imports: [...inj.imports, ...injectorImports]}; + } + + /** + * Add class metadata statements, if provided, to the `ngModuleStatements`. + */ + private insertMetadataStatement(ngModuleStatements: Statement[], metadataStmt: Statement|null): + void { + if (metadataStmt !== null) { + ngModuleStatements.unshift(metadataStmt); } + } + + /** + * Add remote scoping statements, as needed, to the `ngModuleStatements`. + */ + private appendRemoteScopingStatements( + ngModuleStatements: Statement[], node: ClassDeclaration, + declarations: Reference[]): void { const context = getSourceFile(node); - for (const decl of analysis.declarations) { + for (const decl of declarations) { const remoteScope = this.scopeRegistry.getRemoteScope(decl.node); if (remoteScope !== null) { - const directives = - remoteScope.directives.map(directive => this.refEmitter.emit(directive, context)); - const pipes = remoteScope.pipes.map(pipe => this.refEmitter.emit(pipe, context)); + const directives = remoteScope.directives.map( + directive => this.refEmitter.emit(directive, context).expression); + const pipes = remoteScope.pipes.map(pipe => this.refEmitter.emit(pipe, context).expression); const directiveArray = new LiteralArrayExpr(directives); const pipesArray = new LiteralArrayExpr(pipes); - const declExpr = this.refEmitter.emit(decl, context)!; + const declExpr = this.refEmitter.emit(decl, context).expression; const setComponentScope = new ExternalExpr(R3Identifiers.setComponentScope); const callExpr = new InvokeFunctionExpr(setComponentScope, [declExpr, directiveArray, pipesArray]); @@ -402,22 +529,29 @@ export class NgModuleDecoratorHandler implements ngModuleStatements.push(callExpr.toStmt()); } } + } + + private compileNgModule( + factoryFn: CompileResult, injectorDef: R3CompiledExpression, + ngModuleDef: R3CompiledExpression): CompileResult[] { const res: CompileResult[] = [ + factoryFn, { name: 'ɵmod', initializer: ngModuleDef.expression, - statements: ngModuleStatements, + statements: ngModuleDef.statements, type: ngModuleDef.type, }, { name: 'ɵinj', - initializer: ngInjectorDef.expression, - statements: ngInjectorDef.statements, - type: ngInjectorDef.type, - } + initializer: injectorDef.expression, + statements: injectorDef.statements, + type: injectorDef.type, + }, ]; if (this.localeId) { + // QUESTION: can this stuff be removed? res.push({ name: 'ɵloc', initializer: new LiteralExpr(this.localeId), diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts b/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts index 2bbc29719a..76fc19aa11 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts @@ -6,33 +6,58 @@ * found in the LICENSE file at https://angular.io/license */ -import {compilePipeFromMetadata, Identifiers, R3FactoryTarget, R3PipeMetadata, Statement, WrappedNodeExpr} from '@angular/compiler'; +import {compileDeclarePipeFromMetadata, compilePipeFromMetadata, FactoryTarget, R3PipeMetadata, Statement, WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {DefaultImportRecorder, Reference} from '../../imports'; +import {SemanticSymbol} from '../../incremental/semantic_graph'; import {InjectableClassRegistry, MetadataRegistry} from '../../metadata'; import {PartialEvaluator} from '../../partial_evaluator'; +import {PerfEvent, PerfRecorder} from '../../perf'; import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; import {LocalModuleScopeRegistry} from '../../scope'; import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence, ResolveResult} from '../../transform'; -import {createValueHasWrongTypeError} from './diagnostics'; -import {compileNgFactoryDefField} from './factory'; +import {createValueHasWrongTypeError} from './diagnostics'; +import {compileDeclareFactory, compileNgFactoryDefField} from './factory'; import {generateSetClassMetadataCall} from './metadata'; -import {findAngularDecorator, getValidConstructorDependencies, makeDuplicateDeclarationError, unwrapExpression, wrapTypeReference} from './util'; +import {compileResults, findAngularDecorator, getValidConstructorDependencies, makeDuplicateDeclarationError, toFactoryMetadata, unwrapExpression, wrapTypeReference} from './util'; export interface PipeHandlerData { meta: R3PipeMetadata; metadataStmt: Statement|null; } -export class PipeDecoratorHandler implements DecoratorHandler { +/** + * Represents an Angular pipe. + */ +export class PipeSymbol extends SemanticSymbol { + constructor(decl: ClassDeclaration, public readonly name: string) { + super(decl); + } + + isPublicApiAffected(previousSymbol: SemanticSymbol): boolean { + if (!(previousSymbol instanceof PipeSymbol)) { + return true; + } + + return this.name !== previousSymbol.name; + } + + isTypeCheckApiAffected(previousSymbol: SemanticSymbol): boolean { + return this.isPublicApiAffected(previousSymbol); + } +} + +export class PipeDecoratorHandler implements + DecoratorHandler { constructor( private reflector: ReflectionHost, private evaluator: PartialEvaluator, private metaRegistry: MetadataRegistry, private scopeRegistry: LocalModuleScopeRegistry, private defaultImportRecorder: DefaultImportRecorder, - private injectableRegistry: InjectableClassRegistry, private isCore: boolean) {} + private injectableRegistry: InjectableClassRegistry, private isCore: boolean, + private perf: PerfRecorder) {} readonly precedence = HandlerPrecedence.PRIMARY; readonly name = PipeDecoratorHandler.name; @@ -55,6 +80,8 @@ export class PipeDecoratorHandler implements DecoratorHandler): AnalysisOutput { + this.perf.eventCount(PerfEvent.AnalyzePipe); + const name = clazz.name.text; const type = wrapTypeReference(this.reflector, clazz); const internalType = new WrappedNodeExpr(this.reflector.getInternalNameOfClass(clazz)); @@ -114,6 +141,10 @@ export class PipeDecoratorHandler implements DecoratorHandler): PipeSymbol { + return new PipeSymbol(node, analysis.meta.name); + } + register(node: ClassDeclaration, analysis: Readonly): void { const ref = new Reference(node); this.metaRegistry.registerPipeMetadata({ref, name: analysis.meta.pipeName}); @@ -134,23 +165,14 @@ export class PipeDecoratorHandler implements DecoratorHandler): CompileResult[] { - const meta = analysis.meta; - const res = compilePipeFromMetadata(meta); - const factoryRes = compileNgFactoryDefField({ - ...meta, - injectFn: Identifiers.directiveInject, - target: R3FactoryTarget.Pipe, - }); - if (analysis.metadataStmt !== null) { - factoryRes.statements.push(analysis.metadataStmt); - } - return [ - factoryRes, { - name: 'ɵpipe', - initializer: res.expression, - statements: [], - type: res.type, - } - ]; + const fac = compileNgFactoryDefField(toFactoryMetadata(analysis.meta, FactoryTarget.Pipe)); + const def = compilePipeFromMetadata(analysis.meta); + return compileResults(fac, def, analysis.metadataStmt, 'ɵpipe'); + } + + compilePartial(node: ClassDeclaration, analysis: Readonly): CompileResult[] { + const fac = compileDeclareFactory(toFactoryMetadata(analysis.meta, FactoryTarget.Pipe)); + const def = compileDeclarePipeFromMetadata(analysis.meta); + return compileResults(fac, def, analysis.metadataStmt, 'ɵpipe'); } } diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/typecheck_scopes.ts b/packages/compiler-cli/src/ngtsc/annotations/src/typecheck_scopes.ts deleted file mode 100644 index aada9e0df4..0000000000 --- a/packages/compiler-cli/src/ngtsc/annotations/src/typecheck_scopes.ts +++ /dev/null @@ -1,105 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {CssSelector, SchemaMetadata, SelectorMatcher} from '@angular/compiler'; -import * as ts from 'typescript'; - -import {Reference} from '../../imports'; -import {DirectiveMeta, flattenInheritedDirectiveMetadata, MetadataReader} from '../../metadata'; -import {ClassDeclaration} from '../../reflection'; -import {ComponentScopeReader} from '../../scope'; - -/** - * The scope that is used for type-check code generation of a component template. - */ -export interface TypeCheckScope { - /** - * A `SelectorMatcher` instance that contains the flattened directive metadata of all directives - * that are in the compilation scope of the declaring NgModule. - */ - matcher: SelectorMatcher; - - /** - * The pipes that are available in the compilation scope. - */ - pipes: Map>>; - - /** - * The schemas that are used in this scope. - */ - schemas: SchemaMetadata[]; -} - -/** - * Computes scope information to be used in template type checking. - */ -export class TypeCheckScopes { - /** - * Cache of flattened directive metadata. Because flattened metadata is scope-invariant it's - * cached individually, such that all scopes refer to the same flattened metadata. - */ - private flattenedDirectiveMetaCache = new Map(); - - /** - * Cache of the computed type check scope per NgModule declaration. - */ - private scopeCache = new Map(); - - constructor(private scopeReader: ComponentScopeReader, private metaReader: MetadataReader) {} - - /** - * Computes the type-check scope information for the component declaration. If the NgModule - * contains an error, then 'error' is returned. If the component is not declared in any NgModule, - * an empty type-check scope is returned. - */ - getTypeCheckScope(node: ClassDeclaration): TypeCheckScope|'error' { - const matcher = new SelectorMatcher(); - const pipes = new Map>>(); - - const scope = this.scopeReader.getScopeForComponent(node); - if (scope === null) { - return {matcher, pipes, schemas: []}; - } else if (scope === 'error') { - return scope; - } - - if (this.scopeCache.has(scope.ngModule)) { - return this.scopeCache.get(scope.ngModule)!; - } - - for (const meta of scope.compilation.directives) { - if (meta.selector !== null) { - const extMeta = this.getInheritedDirectiveMetadata(meta.ref); - matcher.addSelectables(CssSelector.parse(meta.selector), extMeta); - } - } - - 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}`); - } - pipes.set(name, ref as Reference>); - } - - const typeCheckScope: TypeCheckScope = {matcher, pipes, schemas: scope.schemas}; - this.scopeCache.set(scope.ngModule, typeCheckScope); - return typeCheckScope; - } - - private getInheritedDirectiveMetadata(ref: Reference): DirectiveMeta { - const clazz = ref.node; - if (this.flattenedDirectiveMetaCache.has(clazz)) { - return this.flattenedDirectiveMetaCache.get(clazz)!; - } - - const meta = flattenInheritedDirectiveMetadata(this.metaReader, ref); - this.flattenedDirectiveMetaCache.set(clazz, meta); - return meta; - } -} diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/util.ts b/packages/compiler-cli/src/ngtsc/annotations/src/util.ts index 4d52eb6b80..5820adbb46 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/util.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/util.ts @@ -6,7 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {Expression, ExternalExpr, LiteralExpr, ParseLocation, ParseSourceFile, ParseSourceSpan, R3DependencyMetadata, R3Reference, R3ResolvedDependencyType, ReadPropExpr, WrappedNodeExpr} from '@angular/compiler'; +import {Expression, ExternalExpr, LiteralExpr, ParseLocation, ParseSourceFile, ParseSourceSpan, R3CompiledExpression, R3DependencyMetadata, R3Reference, ReadPropExpr, Statement, WrappedNodeExpr} from '@angular/compiler'; +import {R3FactoryMetadata} from '@angular/compiler/src/compiler'; +import {FactoryTarget} from '@angular/compiler/src/render3/partial/api'; import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError, makeDiagnostic, makeRelatedInformation} from '../../diagnostics'; @@ -14,6 +16,7 @@ import {DefaultImportRecorder, ImportFlags, Reference, ReferenceEmitter} from '. import {ForeignFunctionResolver, PartialEvaluator} from '../../partial_evaluator'; import {ClassDeclaration, CtorParameter, Decorator, Import, ImportedTypeValueReference, isNamedClassDeclaration, LocalTypeValueReference, ReflectionHost, TypeValueReference, TypeValueReferenceKind, UnavailableValue, ValueUnavailableKind} from '../../reflection'; import {DeclarationData} from '../../scope'; +import {CompileResult} from '../../transform'; export type ConstructorDeps = { deps: R3DependencyMetadata[]; @@ -43,9 +46,8 @@ export function getConstructorDependencies( } ctorParams.forEach((param, idx) => { let token = valueReferenceToExpression(param.typeValueReference, defaultImportRecorder); - let attribute: Expression|null = null; + let attributeNameType: 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; @@ -73,11 +75,11 @@ export function getConstructorDependencies( const attributeName = dec.args[0]; token = new WrappedNodeExpr(attributeName); if (ts.isStringLiteralLike(attributeName)) { - attribute = new LiteralExpr(attributeName.text); + attributeNameType = new LiteralExpr(attributeName.text); } else { - attribute = new WrappedNodeExpr(ts.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)); + attributeNameType = + new WrappedNodeExpr(ts.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)); } - resolved = R3ResolvedDependencyType.Attribute; } else { throw new FatalDiagnosticError( ErrorCode.DECORATOR_UNEXPECTED, Decorator.nodeForError(dec), @@ -85,10 +87,6 @@ export function getConstructorDependencies( } }); - if (token instanceof ExternalExpr && token.value.name === 'ChangeDetectorRef' && - token.value.moduleName === '@angular/core') { - resolved = R3ResolvedDependencyType.ChangeDetectorRef; - } if (token === null) { if (param.typeValueReference.kind !== TypeValueReferenceKind.UNAVAILABLE) { throw new Error( @@ -100,7 +98,7 @@ export function getConstructorDependencies( reason: param.typeValueReference.reason, }); } else { - deps.push({token, attribute, optional, self, skipSelf, host, resolved}); + deps.push({token, attributeNameType, optional, self, skipSelf, host}); } }); if (errors.length === 0) { @@ -267,13 +265,12 @@ function createUnsuitableInjectionTokenError( export function toR3Reference( valueRef: Reference, typeRef: Reference, valueContext: ts.SourceFile, typeContext: ts.SourceFile, refEmitter: ReferenceEmitter): R3Reference { - const value = refEmitter.emit(valueRef, valueContext); - const type = refEmitter.emit( - typeRef, typeContext, ImportFlags.ForceNewImport | ImportFlags.AllowTypeImports); - if (value === null || type === null) { - throw new Error(`Could not refer to ${ts.SyntaxKind[valueRef.node.kind]}`); - } - return {value, type}; + return { + value: refEmitter.emit(valueRef, valueContext).expression, + type: refEmitter + .emit(typeRef, typeContext, ImportFlags.ForceNewImport | ImportFlags.AllowTypeImports) + .expression, + }; } export function isAngularCore(decorator: Decorator): decorator is Decorator&{import: Import} { @@ -516,7 +513,13 @@ export function resolveProvidersRequiringFactory( } } - if (tokenClass !== null && reflector.isClass(tokenClass.node)) { + // TODO(alxhub): there was a bug where `getConstructorParameters` would return `null` for a + // class in a .d.ts file, always, even if the class had a constructor. This was fixed for + // `getConstructorParameters`, but that fix causes more classes to be recognized here as needing + // provider checks, which is a breaking change in g3. Avoid this breakage for now by skipping + // classes from .d.ts files here directly, until g3 can be cleaned up. + if (tokenClass !== null && !tokenClass.node.getSourceFile().isDeclarationFile && + reflector.isClass(tokenClass.node)) { const constructorParameters = reflector.getConstructorParameters(tokenClass.node); // Note that we only want to capture providers with a non-trivial constructor, @@ -558,3 +561,35 @@ export function createSourceSpan(node: ts.Node): ParseSourceSpan { new ParseLocation(parseSf, startOffset, startLine + 1, startCol + 1), new ParseLocation(parseSf, endOffset, endLine + 1, endCol + 1)); } + +/** + * Collate the factory and definition compiled results into an array of CompileResult objects. + */ +export function compileResults( + fac: CompileResult, def: R3CompiledExpression, metadataStmt: Statement|null, + propName: string): CompileResult[] { + const statements = def.statements; + if (metadataStmt !== null) { + statements.push(metadataStmt); + } + return [ + fac, { + name: propName, + initializer: def.expression, + statements: def.statements, + type: def.type, + } + ]; +} + +export function toFactoryMetadata( + meta: Omit, target: FactoryTarget): R3FactoryMetadata { + return { + name: meta.name, + type: meta.type, + internalType: meta.internalType, + typeArgumentCount: meta.typeArgumentCount, + deps: meta.deps, + target + }; +} diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/BUILD.bazel b/packages/compiler-cli/src/ngtsc/annotations/test/BUILD.bazel index 5e8b56b3c7..88a4280c2e 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/annotations/test/BUILD.bazel @@ -17,8 +17,10 @@ ts_library( "//packages/compiler-cli/src/ngtsc/file_system", "//packages/compiler-cli/src/ngtsc/file_system/testing", "//packages/compiler-cli/src/ngtsc/imports", + "//packages/compiler-cli/src/ngtsc/incremental:api", "//packages/compiler-cli/src/ngtsc/metadata", "//packages/compiler-cli/src/ngtsc/partial_evaluator", + "//packages/compiler-cli/src/ngtsc/perf", "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/scope", "//packages/compiler-cli/src/ngtsc/testing", 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 903745ba1c..63d33bd2c3 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts @@ -5,17 +5,22 @@ * 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 {CycleAnalyzer, ImportGraph} from '../../cycles'; + +import {ConstantPool} from '@angular/compiler'; +import * as ts from 'typescript'; + +import {CycleAnalyzer, CycleHandlingStrategy, ImportGraph} from '../../cycles'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {absoluteFrom} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports'; import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, ResourceRegistry} from '../../metadata'; import {PartialEvaluator} from '../../partial_evaluator'; +import {NOOP_PERF_RECORDER} from '../../perf'; import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; -import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; +import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver, TypeCheckScopeRegistry} from '../../scope'; import {getDeclaration, makeProgram} from '../../testing'; -import {ResourceLoader} from '../src/api'; +import {ResourceLoader, ResourceLoaderContext} from '../src/api'; import {ComponentDecoratorHandler} from '../src/component'; export class StubResourceLoader implements ResourceLoader { @@ -23,12 +28,16 @@ export class StubResourceLoader implements ResourceLoader { return v; } canPreload = false; + canPreprocess = false; load(v: string): string { return ''; } preload(): Promise|undefined { throw new Error('Not implemented'); } + preprocessInline(_data: string, _context: ResourceLoaderContext): Promise { + throw new Error('Not implemented'); + } } function setup(program: ts.Program, options: ts.CompilerOptions, host: ts.CompilerHost) { @@ -37,7 +46,7 @@ function setup(program: ts.Program, options: ts.CompilerOptions, host: ts.Compil const evaluator = new PartialEvaluator(reflectionHost, checker, /* dependencyTracker */ null); const moduleResolver = new ModuleResolver(program, options, host, /* moduleResolutionCache */ null); - const importGraph = new ImportGraph(moduleResolver); + const importGraph = new ImportGraph(checker, NOOP_PERF_RECORDER); const cycleAnalyzer = new CycleAnalyzer(importGraph); const metaRegistry = new LocalMetadataRegistry(); const dtsReader = new DtsMetadataReader(checker, reflectionHost); @@ -48,17 +57,38 @@ function setup(program: ts.Program, options: ts.CompilerOptions, host: ts.Compil const refEmitter = new ReferenceEmitter([]); const injectableRegistry = new InjectableClassRegistry(reflectionHost); const resourceRegistry = new ResourceRegistry(); + const typeCheckScopeRegistry = new TypeCheckScopeRegistry(scopeRegistry, metaReader); + const resourceLoader = new StubResourceLoader(); const handler = new ComponentDecoratorHandler( - reflectionHost, evaluator, metaRegistry, metaReader, scopeRegistry, scopeRegistry, + reflectionHost, + evaluator, + metaRegistry, + metaReader, + scopeRegistry, + scopeRegistry, + typeCheckScopeRegistry, resourceRegistry, - /* isCore */ false, new StubResourceLoader(), /* rootDirs */['/'], - /* defaultPreserveWhitespaces */ false, /* i18nUseExternalIds */ true, + /* isCore */ false, + resourceLoader, + /* rootDirs */['/'], + /* defaultPreserveWhitespaces */ false, + /* i18nUseExternalIds */ true, /* enableI18nLegacyMessageIdFormat */ false, - /* i18nNormalizeLineEndingsInICUs */ undefined, moduleResolver, cycleAnalyzer, refEmitter, - NOOP_DEFAULT_IMPORT_RECORDER, /* depTracker */ null, injectableRegistry, - /* annotateForClosureCompiler */ false); - return {reflectionHost, handler}; + /* usePoisonedData */ false, + /* i18nNormalizeLineEndingsInICUs */ undefined, + moduleResolver, + cycleAnalyzer, + CycleHandlingStrategy.UseRemoteScoping, + refEmitter, + NOOP_DEFAULT_IMPORT_RECORDER, + /* depTracker */ null, + injectableRegistry, + /* semanticDepGraphUpdater */ null, + /* annotateForClosureCompiler */ false, + NOOP_PERF_RECORDER, + ); + return {reflectionHost, handler, resourceLoader}; } runInEachFileSystem(() => { @@ -199,6 +229,80 @@ runInEachFileSystem(() => { const {analysis} = handler.analyze(TestCmp, detected.metadata); expect(analysis?.resources.styles.size).toBe(3); }); + + it('does not emit a program with template parse errors', () => { + const template = '{{x ? y }}'; + const {program, options, host} = makeProgram([ + { + name: _('/node_modules/@angular/core/index.d.ts'), + contents: 'export const Component: any;', + }, + { + name: _('/entry.ts'), + contents: ` + import {Component} from '@angular/core'; + @Component({ + template: '${template}', + }) class TestCmp {} + ` + }, + ]); + + const {reflectionHost, handler} = setup(program, options, host); + const TestCmp = getDeclaration(program, _('/entry.ts'), 'TestCmp', isNamedClassDeclaration); + const detected = handler.detect(TestCmp, reflectionHost.getDecoratorsOfDeclaration(TestCmp)); + if (detected === undefined) { + return fail('Failed to recognize @Component'); + } + const {analysis} = handler.analyze(TestCmp, detected.metadata); + const symbol = handler.symbol(TestCmp, analysis!); + const resolution = handler.resolve(TestCmp, analysis!, symbol); + + const compileResult = + handler.compileFull(TestCmp, analysis!, resolution.data!, new ConstantPool()); + expect(compileResult).toEqual([]); + }); + + it('should replace inline style content with transformed content', async () => { + const {program, options, host} = makeProgram([ + { + name: _('/node_modules/@angular/core/index.d.ts'), + contents: 'export const Component: any;', + }, + { + name: _('/entry.ts'), + contents: ` + import {Component} from '@angular/core'; + + @Component({ + template: '', + styles: ['.abc {}'] + }) class TestCmp {} + ` + }, + ]); + const {reflectionHost, handler, resourceLoader} = setup(program, options, host); + resourceLoader.canPreload = true; + resourceLoader.canPreprocess = true; + resourceLoader.preprocessInline = async function(data, context) { + expect(data).toBe('.abc {}'); + expect(context.containingFile).toBe(_('/entry.ts').toLowerCase()); + expect(context.type).toBe('style'); + + return '.xyz {}'; + }; + + const TestCmp = getDeclaration(program, _('/entry.ts'), 'TestCmp', isNamedClassDeclaration); + const detected = handler.detect(TestCmp, reflectionHost.getDecoratorsOfDeclaration(TestCmp)); + if (detected === undefined) { + return fail('Failed to recognize @Component'); + } + + await handler.preanalyze(TestCmp, detected.metadata); + + const {analysis} = handler.analyze(TestCmp, detected.metadata); + expect(analysis?.inlineStyles).toEqual(jasmine.arrayWithExactContents(['.xyz {}'])); + }); }); function ivyCode(code: ErrorCode): number { 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 630980dc0d..898a0a28eb 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/directive_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/directive_spec.ts @@ -13,6 +13,7 @@ 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 {NOOP_PERF_RECORDER} from '../../perf'; import {ClassDeclaration, isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; import {getDeclaration, makeProgram} from '../../testing'; @@ -105,6 +106,7 @@ runInEachFileSystem(() => { isComponent: false, name: 'Dir', selector: '[dir]', + isStructural: false, }; matcher.addSelectables(CssSelector.parse('[dir]'), dirMeta); @@ -118,6 +120,30 @@ runInEachFileSystem(() => { // and field names. expect(propBindingConsumer).toBe(dirMeta); }); + + it('should identify a structural directive', () => { + const src = ` + import {Directive, TemplateRef} from '@angular/core'; + + @Directive({selector: 'test-dir'}) + export class TestDir { + constructor(private ref: TemplateRef) {} + } + `; + const {program} = makeProgram([ + { + name: _('/node_modules/@angular/core/index.d.ts'), + contents: 'export const Directive: any; export declare class TemplateRef {}', + }, + { + name: _('/entry.ts'), + contents: src, + }, + ]); + + const analysis = analyzeDirective(program, 'TestDir'); + expect(analysis.isStructural).toBeTrue(); + }); }); // Helpers @@ -144,8 +170,9 @@ runInEachFileSystem(() => { const handler = new DirectiveDecoratorHandler( reflectionHost, evaluator, scopeRegistry, scopeRegistry, metaReader, NOOP_DEFAULT_IMPORT_RECORDER, injectableRegistry, /*isCore*/ false, + /*semanticDepGraphUpdater*/ null, /*annotateForClosureCompiler*/ false, - /*detectUndecoratedClassesWithAngularFeatures*/ false); + /*detectUndecoratedClassesWithAngularFeatures*/ false, NOOP_PERF_RECORDER); const DirNode = getDeclaration(program, _('/entry.ts'), dirName, isNamedClassDeclaration); 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 eab3b82aa6..293fd25533 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts @@ -10,6 +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 {NOOP_PERF_RECORDER} from '../../perf'; import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {getDeclaration, makeProgram} from '../../testing'; import {InjectableDecoratorHandler} from '../src/injectable'; @@ -70,7 +71,7 @@ function setupHandler(errorOnDuplicateProv: boolean) { const injectableRegistry = new InjectableClassRegistry(reflectionHost); const handler = new InjectableDecoratorHandler( reflectionHost, NOOP_DEFAULT_IMPORT_RECORDER, /* isCore */ false, - /* strictCtorDeps */ false, injectableRegistry, errorOnDuplicateProv); + /* strictCtorDeps */ false, injectableRegistry, NOOP_PERF_RECORDER, errorOnDuplicateProv); const TestClass = getDeclaration(program, ENTRY_FILE, 'TestClass', isNamedClassDeclaration); const ɵprov = reflectionHost.getMembersOfClass(TestClass).find(member => member.name === 'ɵprov'); if (ɵprov === undefined) { 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 9a09329bad..fe6a5609ab 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts @@ -24,7 +24,7 @@ runInEachFileSystem(() => { @Component('metadata') class Target {} `); expect(res).toEqual( - `/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(Target, [{ type: Component, args: ['metadata'] }], null, null); })();`); + `(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(Target, [{ type: Component, args: ['metadata'] }], null, null); })();`); }); it('should convert namespaced decorated class metadata', () => { @@ -34,7 +34,7 @@ runInEachFileSystem(() => { @core.Component('metadata') class Target {} `); expect(res).toEqual( - `/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(Target, [{ type: core.Component, args: ['metadata'] }], null, null); })();`); + `(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(Target, [{ type: core.Component, args: ['metadata'] }], null, null); })();`); }); it('should convert decorated class constructor parameter metadata', () => { 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 ff621536bb..5e0387b1db 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,6 +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 {NOOP_PERF_RECORDER} from '../../perf'; import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; import {getDeclaration, makeProgram} from '../../testing'; @@ -71,7 +72,8 @@ runInEachFileSystem(() => { const handler = new NgModuleDecoratorHandler( reflectionHost, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, /* isCore */ false, /* routeAnalyzer */ null, refEmitter, /* factoryTracker */ null, - NOOP_DEFAULT_IMPORT_RECORDER, /* annotateForClosureCompiler */ false, injectableRegistry); + NOOP_DEFAULT_IMPORT_RECORDER, /* annotateForClosureCompiler */ false, injectableRegistry, + NOOP_PERF_RECORDER); const TestModule = getDeclaration(program, _('/entry.ts'), 'TestModule', isNamedClassDeclaration); const detected = diff --git a/packages/compiler-cli/src/ngtsc/core/BUILD.bazel b/packages/compiler-cli/src/ngtsc/core/BUILD.bazel index ce0cef9979..a111461889 100644 --- a/packages/compiler-cli/src/ngtsc/core/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/core/BUILD.bazel @@ -19,6 +19,8 @@ ts_library( "//packages/compiler-cli/src/ngtsc/file_system", "//packages/compiler-cli/src/ngtsc/imports", "//packages/compiler-cli/src/ngtsc/incremental", + "//packages/compiler-cli/src/ngtsc/incremental:api", + "//packages/compiler-cli/src/ngtsc/incremental/semantic_graph", "//packages/compiler-cli/src/ngtsc/indexer", "//packages/compiler-cli/src/ngtsc/metadata", "//packages/compiler-cli/src/ngtsc/modulewithproviders", diff --git a/packages/compiler-cli/src/ngtsc/core/README.md b/packages/compiler-cli/src/ngtsc/core/README.md index 6f41d0a056..b21d4f44b9 100644 --- a/packages/compiler-cli/src/ngtsc/core/README.md +++ b/packages/compiler-cli/src/ngtsc/core/README.md @@ -22,11 +22,22 @@ A compiler which integrates Angular compilation into this process follows a very 1. A `ts.CompilerHost` is created. 2. That `ts.CompilerHost` is wrapped in an `NgCompilerHost`, which adds Angular specific files to the compilation. 3. A `ts.Program` is created from the `NgCompilerHost` and its augmented set of root files. -4. An `NgCompiler` is created using the `ts.Program`. +4. A `CompilationTicket` is created, optionally incorporating any state from a previous compilation run. +4. An `NgCompiler` is created using the `CompilationTicket`. 5. Diagnostics can be gathered from the `ts.Program` as normal, as well as from the `NgCompiler`. 6. Prior to `emit`, `NgCompiler.prepareEmit` is called to retrieve the Angular transformers which need to be fed to `ts.Program.emit`. 7. `emit` is called on the `ts.Program` with the Angular transformers from above, which produces JavaScript code with Angular extensions. +# `NgCompiler` and incremental compilation + +The Angular compiler is capable of incremental compilation, where information from a previous compilation is used to accelerate the next compilation. During compilation, the compiler produces two major kinds of information: local information (such as component and directive metadata) and global information (such as reified NgModule scopes). Incremental compilation is managed in two ways: + +1. For most changes, a new `NgCompiler` can selectively inherit local information from a previous instance, and only needs to recompute it where an underlying TypeScript file has change. Global information is always recomputed from scratch in this case. + +2. For specific changes, such as those in component resources, an `NgCompiler` can be reused in its entirety, and updated to incorporate the effects of such changes without needing to recompute any other information. + +Note that these two modes differ in terms of whether a new `NgCompiler` instance is needed or whether a previous one can reused. To prevent leaking this implementation complexity and shield consumers from having to manage the lifecycle of `NgCompiler` so specifically, this process is abstracted via `CompilationTicket`s. Consumers first obtain a `CompilationTicket` (depending on the nature of the incoming change), and then use this ticket to retrieve an `NgCompiler` instance. In creating the `CompilationTicket`, the compiler can decide whether to reuse an old `NgCompiler` instance or to create a new one. + ## Asynchronous compilation In some compilation environments (such as the Webpack-driven compilation inside the Angular CLI), various inputs to the compilation are only producible in an asynchronous fashion. For example, SASS compilation of `styleUrls` that link to SASS files requires spawning a child Webpack compilation. To support this, Angular has an asynchronous interface for loading such resources. diff --git a/packages/compiler-cli/src/ngtsc/core/api/src/adapter.ts b/packages/compiler-cli/src/ngtsc/core/api/src/adapter.ts index ee93112ee9..b9ba08a2cc 100644 --- a/packages/compiler-cli/src/ngtsc/core/api/src/adapter.ts +++ b/packages/compiler-cli/src/ngtsc/core/api/src/adapter.ts @@ -28,7 +28,7 @@ export type ExtendedCompilerHostMethods = 'getCurrentDirectory'| // Additional methods of `ExtendedTsCompilerHost` related to resource files (e.g. HTML // templates). These are optional. - 'getModifiedResourceFiles'|'readResource'|'resourceNameToFileName'; + 'getModifiedResourceFiles'|'readResource'|'resourceNameToFileName'|'transformResource'; /** * Adapter for `NgCompiler` that allows it to be used in various circumstances, such as 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 36184d8a81..3c3a2f0179 100644 --- a/packages/compiler-cli/src/ngtsc/core/api/src/interfaces.ts +++ b/packages/compiler-cli/src/ngtsc/core/api/src/interfaces.ts @@ -49,6 +49,52 @@ export interface ResourceHost { * or `undefined` if this is not an incremental build. */ getModifiedResourceFiles?(): Set|undefined; + + /** + * Transform an inline or external resource asynchronously. + * It is assumed the consumer of the corresponding `Program` will call + * `loadNgStructureAsync()`. Using outside `loadNgStructureAsync()` will + * cause a diagnostics error or an exception to be thrown. + * Only style resources are currently supported. + * + * @param data The resource data to transform. + * @param context Information regarding the resource such as the type and containing file. + * @returns A promise of either the transformed resource data or null if no transformation occurs. + */ + transformResource? + (data: string, context: ResourceHostContext): Promise; +} + +/** + * Contextual information used by members of the ResourceHost interface. + */ +export interface ResourceHostContext { + /** + * The type of the component resource. Templates are not yet supported. + * * Resources referenced via a component's `styles` or `styleUrls` properties are of + * type `style`. + */ + readonly type: 'style'; + /** + * The absolute path to the resource file. If the resource is inline, the value will be null. + */ + readonly resourceFile: string|null; + /** + * The absolute path to the file that contains the resource or reference to the resource. + */ + readonly containingFile: string; +} + +/** + * The successful transformation result of the `ResourceHost.transformResource` function. + * This interface may be expanded in the future to include diagnostic information and source mapping + * support. + */ +export interface TransformResourceResult { + /** + * The content generated by the transformation. + */ + content: 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 2b168db540..661221d87a 100644 --- a/packages/compiler-cli/src/ngtsc/core/api/src/options.ts +++ b/packages/compiler-cli/src/ngtsc/core/api/src/options.ts @@ -25,23 +25,11 @@ export interface TestOnlyOptions { */ _useHostForImportGeneration?: boolean; - /** - * Turn on template type-checking in the Ivy compiler. - * - * This is an internal flag being used to roll out template type-checking in ngtsc. Turning it on - * by default before it's ready might break other users attempting to test the new compiler's - * behavior. - * - * @internal - */ - ivyTemplateTypeCheck?: boolean; - /** * 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 - * (not all hosts support this mode of operation). + * This should be a path to a JSON file where trace information will be written. This is sensitive + * to the compiler's working directory, and should likely be an absolute path. * * This is currently not exposed to users as the trace format is still unstable. */ diff --git a/packages/compiler-cli/src/ngtsc/core/api/src/public_options.ts b/packages/compiler-cli/src/ngtsc/core/api/src/public_options.ts index 98b41f8f07..115554b7d9 100644 --- a/packages/compiler-cli/src/ngtsc/core/api/src/public_options.ts +++ b/packages/compiler-cli/src/ngtsc/core/api/src/public_options.ts @@ -127,9 +127,9 @@ export interface StrictTemplateOptions { /** * If `true`, implies all template strictness flags below (unless individually disabled). * - * Has no effect unless `fullTemplateTypeCheck` is also enabled. + * This flag is a superset of `fullTemplateTypeCheck`. * - * Defaults to `false`, even if "fullTemplateTypeCheck" is set. + * Defaults to `false`, even if "fullTemplateTypeCheck" is `true`. */ strictTemplates?: boolean; diff --git a/packages/compiler-cli/src/ngtsc/core/index.ts b/packages/compiler-cli/src/ngtsc/core/index.ts index 475e18145a..a9eba8a032 100644 --- a/packages/compiler-cli/src/ngtsc/core/index.ts +++ b/packages/compiler-cli/src/ngtsc/core/index.ts @@ -6,5 +6,5 @@ * found in the LICENSE file at https://angular.io/license */ -export {NgCompiler} from './src/compiler'; -export {NgCompilerHost} from './src/host'; +export * from './src/compiler'; +export {NgCompilerHost} from './src/host'; \ 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 4a26f2f9a8..f99ceb9ebb 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -10,30 +10,34 @@ import {Type} from '@angular/compiler'; 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 {CycleAnalyzer, CycleHandlingStrategy, ImportGraph} from '../../cycles'; +import {COMPILER_ERRORS_WITH_GUIDES, ERROR_DETAILS_PAGE_BASE_URL, ErrorCode, ngErrorCode} from '../../diagnostics'; import {checkForPrivateExports, ReferenceGraph} from '../../entry_point'; import {LogicalFileSystem, resolve} from '../../file_system'; import {AbsoluteModuleStrategy, AliasingHost, AliasStrategy, DefaultImportTracker, ImportRewriter, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NoopImportRewriter, PrivateExportAliasingHost, R3SymbolsImportRewriter, Reference, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesAliasingHost, UnifiedModulesStrategy} from '../../imports'; import {IncrementalBuildStrategy, IncrementalDriver} from '../../incremental'; +import {SemanticSymbol} from '../../incremental/semantic_graph'; import {generateAnalysis, IndexedComponent, IndexingContext} from '../../indexer'; import {ComponentResources, CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, MetadataReader, ResourceRegistry} from '../../metadata'; import {ModuleWithProvidersScanner} from '../../modulewithproviders'; import {PartialEvaluator} from '../../partial_evaluator'; -import {NOOP_PERF_RECORDER, PerfRecorder} from '../../perf'; +import {ActivePerfRecorder} from '../../perf'; +import {PerfCheckpoint, PerfEvent, PerfPhase} from '../../perf/src/api'; +import {DelegatingPerfRecorder} from '../../perf/src/recorder'; import {DeclarationNode, isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {AdapterResourceLoader} from '../../resource'; import {entryPointKeyFor, NgModuleRouteAnalyzer} from '../../routing'; -import {ComponentScopeReader, LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; +import {ComponentScopeReader, LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver, TypeCheckScopeRegistry} from '../../scope'; import {generatedFactoryTransform} from '../../shims'; import {ivySwitchTransform} from '../../switch'; import {aliasTransformFactory, CompilationMode, declarationTransformFactory, DecoratorHandler, DtsTransformRegistry, ivyTransformFactory, TraitCompiler} from '../../transform'; import {TemplateTypeCheckerImpl} from '../../typecheck'; import {OptimizeFor, TemplateTypeChecker, TypeCheckingConfig, TypeCheckingProgramStrategy} from '../../typecheck/api'; -import {isTemplateDiagnostic} from '../../typecheck/diagnostics'; import {getSourceFileOrNull, isDtsPath, resolveModuleName} from '../../util/src/typescript'; import {LazyRoute, NgCompilerAdapter, NgCompilerOptions} from '../api'; +import {compileUndecoratedClassesWithAngularFeatures} from './config'; + /** * State information about a compilation which is only generated once some data is requested from * the `NgCompiler` (for example, by calling `getDiagnostics`). @@ -44,6 +48,7 @@ interface LazyCompilationState { reflector: TypeScriptReflectionHost; metaReader: MetadataReader; scopeRegistry: LocalModuleScopeRegistry; + typeCheckScopeRegistry: TypeCheckScopeRegistry; exportReferenceGraph: ReferenceGraph|null; routeAnalyzer: NgModuleRouteAnalyzer; dtsTransforms: DtsTransformRegistry; @@ -55,6 +60,165 @@ interface LazyCompilationState { resourceRegistry: ResourceRegistry; } + + +/** + * Discriminant type for a `CompilationTicket`. + */ +export enum CompilationTicketKind { + Fresh, + IncrementalTypeScript, + IncrementalResource, +} + +/** + * Begin an Angular compilation operation from scratch. + */ +export interface FreshCompilationTicket { + kind: CompilationTicketKind.Fresh; + options: NgCompilerOptions; + incrementalBuildStrategy: IncrementalBuildStrategy; + typeCheckingProgramStrategy: TypeCheckingProgramStrategy; + enableTemplateTypeChecker: boolean; + usePoisonedData: boolean; + tsProgram: ts.Program; + perfRecorder: ActivePerfRecorder; +} + +/** + * Begin an Angular compilation operation that incorporates changes to TypeScript code. + */ +export interface IncrementalTypeScriptCompilationTicket { + kind: CompilationTicketKind.IncrementalTypeScript; + options: NgCompilerOptions; + oldProgram: ts.Program; + newProgram: ts.Program; + incrementalBuildStrategy: IncrementalBuildStrategy; + typeCheckingProgramStrategy: TypeCheckingProgramStrategy; + newDriver: IncrementalDriver; + enableTemplateTypeChecker: boolean; + usePoisonedData: boolean; + perfRecorder: ActivePerfRecorder; +} + +export interface IncrementalResourceCompilationTicket { + kind: CompilationTicketKind.IncrementalResource; + compiler: NgCompiler; + modifiedResourceFiles: Set; + perfRecorder: ActivePerfRecorder; +} + +/** + * A request to begin Angular compilation, either starting from scratch or from a known prior state. + * + * `CompilationTicket`s are used to initialize (or update) an `NgCompiler` instance, the core of the + * Angular compiler. They abstract the starting state of compilation and allow `NgCompiler` to be + * managed independently of any incremental compilation lifecycle. + */ +export type CompilationTicket = FreshCompilationTicket|IncrementalTypeScriptCompilationTicket| + IncrementalResourceCompilationTicket; + +/** + * Create a `CompilationTicket` for a brand new compilation, using no prior state. + */ +export function freshCompilationTicket( + tsProgram: ts.Program, options: NgCompilerOptions, + incrementalBuildStrategy: IncrementalBuildStrategy, + typeCheckingProgramStrategy: TypeCheckingProgramStrategy, perfRecorder: ActivePerfRecorder|null, + enableTemplateTypeChecker: boolean, usePoisonedData: boolean): CompilationTicket { + return { + kind: CompilationTicketKind.Fresh, + tsProgram, + options, + incrementalBuildStrategy, + typeCheckingProgramStrategy, + enableTemplateTypeChecker, + usePoisonedData, + perfRecorder: perfRecorder ?? ActivePerfRecorder.zeroedToNow(), + }; +} + +/** + * Create a `CompilationTicket` as efficiently as possible, based on a previous `NgCompiler` + * instance and a new `ts.Program`. + */ +export function incrementalFromCompilerTicket( + oldCompiler: NgCompiler, newProgram: ts.Program, + incrementalBuildStrategy: IncrementalBuildStrategy, + typeCheckingProgramStrategy: TypeCheckingProgramStrategy, modifiedResourceFiles: Set, + perfRecorder: ActivePerfRecorder|null): CompilationTicket { + const oldProgram = oldCompiler.getNextProgram(); + const oldDriver = oldCompiler.incrementalStrategy.getIncrementalDriver(oldProgram); + if (oldDriver === null) { + // No incremental step is possible here, since no IncrementalDriver was found for the old + // program. + return freshCompilationTicket( + newProgram, oldCompiler.options, incrementalBuildStrategy, typeCheckingProgramStrategy, + perfRecorder, oldCompiler.enableTemplateTypeChecker, oldCompiler.usePoisonedData); + } + + if (perfRecorder === null) { + perfRecorder = ActivePerfRecorder.zeroedToNow(); + } + + const newDriver = IncrementalDriver.reconcile( + oldProgram, oldDriver, newProgram, modifiedResourceFiles, perfRecorder); + + return { + kind: CompilationTicketKind.IncrementalTypeScript, + enableTemplateTypeChecker: oldCompiler.enableTemplateTypeChecker, + usePoisonedData: oldCompiler.usePoisonedData, + options: oldCompiler.options, + incrementalBuildStrategy, + typeCheckingProgramStrategy, + newDriver, + oldProgram, + newProgram, + perfRecorder, + }; +} + +/** + * Create a `CompilationTicket` directly from an old `ts.Program` and associated Angular compilation + * state, along with a new `ts.Program`. + */ +export function incrementalFromDriverTicket( + oldProgram: ts.Program, oldDriver: IncrementalDriver, newProgram: ts.Program, + options: NgCompilerOptions, incrementalBuildStrategy: IncrementalBuildStrategy, + typeCheckingProgramStrategy: TypeCheckingProgramStrategy, modifiedResourceFiles: Set, + perfRecorder: ActivePerfRecorder|null, enableTemplateTypeChecker: boolean, + usePoisonedData: boolean): CompilationTicket { + if (perfRecorder === null) { + perfRecorder = ActivePerfRecorder.zeroedToNow(); + } + + const newDriver = IncrementalDriver.reconcile( + oldProgram, oldDriver, newProgram, modifiedResourceFiles, perfRecorder); + return { + kind: CompilationTicketKind.IncrementalTypeScript, + oldProgram, + newProgram, + options, + incrementalBuildStrategy, + newDriver, + typeCheckingProgramStrategy, + enableTemplateTypeChecker, + usePoisonedData, + perfRecorder, + }; +} + +export function resourceChangeTicket(compiler: NgCompiler, modifiedResourceFiles: Set): + IncrementalResourceCompilationTicket { + return { + kind: CompilationTicketKind.IncrementalResource, + compiler, + modifiedResourceFiles, + perfRecorder: ActivePerfRecorder.zeroedToNow(), + }; +} + + /** * The heart of the Angular Ivy compiler. * @@ -83,11 +247,12 @@ export class NgCompiler { private constructionDiagnostics: ts.Diagnostic[] = []; /** - * Semantic diagnostics related to the program itself. + * Non-template diagnostics related to the program itself. Does not include template + * diagnostics because the template type checker memoizes them itself. * - * This is set by (and memoizes) `getDiagnostics`. + * This is set by (and memoizes) `getNonTemplateDiagnostics`. */ - private diagnostics: ts.Diagnostic[]|null = null; + private nonTemplateDiagnostics: ts.Diagnostic[]|null = null; private closureCompilerEnabled: boolean; private nextProgram: ts.Program; @@ -95,19 +260,68 @@ export class NgCompiler { private moduleResolver: ModuleResolver; private resourceManager: AdapterResourceLoader; private cycleAnalyzer: CycleAnalyzer; - readonly incrementalDriver: IncrementalDriver; readonly ignoreForDiagnostics: Set; readonly ignoreForEmit: Set; - constructor( + /** + * `NgCompiler` can be reused for multiple compilations (for resource-only changes), and each + * new compilation uses a fresh `PerfRecorder`. Thus, classes created with a lifespan of the + * `NgCompiler` use a `DelegatingPerfRecorder` so the `PerfRecorder` they write to can be updated + * with each fresh compilation. + */ + private delegatingPerfRecorder = new DelegatingPerfRecorder(this.perfRecorder); + + /** + * Convert a `CompilationTicket` into an `NgCompiler` instance for the requested compilation. + * + * Depending on the nature of the compilation request, the `NgCompiler` instance may be reused + * from a previous compilation and updated with any changes, it may be a new instance which + * incrementally reuses state from a previous compilation, or it may represent a fresh + * compilation entirely. + */ + static fromTicket(ticket: CompilationTicket, adapter: NgCompilerAdapter) { + switch (ticket.kind) { + case CompilationTicketKind.Fresh: + return new NgCompiler( + adapter, + ticket.options, + ticket.tsProgram, + ticket.typeCheckingProgramStrategy, + ticket.incrementalBuildStrategy, + IncrementalDriver.fresh(ticket.tsProgram), + ticket.enableTemplateTypeChecker, + ticket.usePoisonedData, + ticket.perfRecorder, + ); + case CompilationTicketKind.IncrementalTypeScript: + return new NgCompiler( + adapter, + ticket.options, + ticket.newProgram, + ticket.typeCheckingProgramStrategy, + ticket.incrementalBuildStrategy, + ticket.newDriver, + ticket.enableTemplateTypeChecker, + ticket.usePoisonedData, + ticket.perfRecorder, + ); + case CompilationTicketKind.IncrementalResource: + const compiler = ticket.compiler; + compiler.updateWithChangedResources(ticket.modifiedResourceFiles, ticket.perfRecorder); + return compiler; + } + } + + private constructor( private adapter: NgCompilerAdapter, - private options: NgCompilerOptions, + readonly options: NgCompilerOptions, private tsProgram: ts.Program, - private typeCheckingProgramStrategy: TypeCheckingProgramStrategy, - private incrementalStrategy: IncrementalBuildStrategy, - private enableTemplateTypeChecker: boolean, - oldProgram: ts.Program|null = null, - private perfRecorder: PerfRecorder = NOOP_PERF_RECORDER, + readonly typeCheckingProgramStrategy: TypeCheckingProgramStrategy, + readonly incrementalStrategy: IncrementalBuildStrategy, + readonly incrementalDriver: IncrementalDriver, + readonly enableTemplateTypeChecker: boolean, + readonly usePoisonedData: boolean, + private livePerfRecorder: ActivePerfRecorder, ) { this.constructionDiagnostics.push(...this.adapter.constructionDiagnostics); const incompatibleTypeCheckOptionsDiagnostic = verifyCompatibleTypeCheckOptions(this.options); @@ -123,9 +337,7 @@ export class NgCompiler { const moduleResolutionCache = ts.createModuleResolutionCache( this.adapter.getCurrentDirectory(), - // Note: this used to be an arrow-function closure. However, JS engines like v8 have some - // strange behaviors with retaining the lexical scope of the closure. Even if this function - // doesn't retain a reference to `this`, if other closures in the constructor here reference + // doen't retain a reference to `this`, if other closures in the constructor here reference // `this` internally then a closure created here would retain them. This can cause major // memory leak issues since the `moduleResolutionCache` is a long-lived object and finds its // way into all kinds of places inside TS internal objects. @@ -133,33 +345,66 @@ export class NgCompiler { this.moduleResolver = new ModuleResolver(tsProgram, this.options, this.adapter, moduleResolutionCache); this.resourceManager = new AdapterResourceLoader(adapter, this.options); - this.cycleAnalyzer = new CycleAnalyzer(new ImportGraph(this.moduleResolver)); - - let modifiedResourceFiles: Set|null = null; - if (this.adapter.getModifiedResourceFiles !== undefined) { - modifiedResourceFiles = this.adapter.getModifiedResourceFiles() || null; - } - - if (oldProgram === null) { - this.incrementalDriver = IncrementalDriver.fresh(tsProgram); - } else { - const oldDriver = this.incrementalStrategy.getIncrementalDriver(oldProgram); - if (oldDriver !== null) { - this.incrementalDriver = - IncrementalDriver.reconcile(oldProgram, oldDriver, tsProgram, modifiedResourceFiles); - } else { - // A previous ts.Program was used to create the current one, but it wasn't from an - // `NgCompiler`. That doesn't hurt anything, but the Angular analysis will have to start - // from a fresh state. - this.incrementalDriver = IncrementalDriver.fresh(tsProgram); - } - } + this.cycleAnalyzer = + new CycleAnalyzer(new ImportGraph(tsProgram.getTypeChecker(), this.delegatingPerfRecorder)); this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, tsProgram); this.ignoreForDiagnostics = new Set(tsProgram.getSourceFiles().filter(sf => this.adapter.isShim(sf))); - this.ignoreForEmit = this.adapter.ignoreForEmit; + + let dtsFileCount = 0; + let nonDtsFileCount = 0; + for (const sf of tsProgram.getSourceFiles()) { + if (sf.isDeclarationFile) { + dtsFileCount++; + } else { + nonDtsFileCount++; + } + } + + livePerfRecorder.eventCount(PerfEvent.InputDtsFile, dtsFileCount); + livePerfRecorder.eventCount(PerfEvent.InputTsFile, nonDtsFileCount); + } + + get perfRecorder(): ActivePerfRecorder { + return this.livePerfRecorder; + } + + private updateWithChangedResources( + changedResources: Set, perfRecorder: ActivePerfRecorder): void { + this.livePerfRecorder = perfRecorder; + this.delegatingPerfRecorder.target = perfRecorder; + + perfRecorder.inPhase(PerfPhase.ResourceUpdate, () => { + if (this.compilation === null) { + // Analysis hasn't happened yet, so no update is necessary - any changes to resources will + // be captured by the inital analysis pass itself. + return; + } + + this.resourceManager.invalidate(); + + const classesToUpdate = new Set(); + for (const resourceFile of changedResources) { + for (const templateClass of this.getComponentsWithTemplateFile(resourceFile)) { + classesToUpdate.add(templateClass); + } + + for (const styleClass of this.getComponentsWithStyleFile(resourceFile)) { + classesToUpdate.add(styleClass); + } + } + + for (const clazz of classesToUpdate) { + this.compilation.traitCompiler.updateResources(clazz); + if (!ts.isClassDeclaration(clazz)) { + continue; + } + + this.compilation.templateTypeChecker.invalidateClass(clazz); + } + }); } /** @@ -173,38 +418,40 @@ export class NgCompiler { return this.incrementalDriver.depGraph.getResourceDependencies(file); } + /** + * Get all Angular-related diagnostics for this compilation. + */ + getDiagnostics(): ts.Diagnostic[] { + return this.addMessageTextDetails( + [...this.getNonTemplateDiagnostics(), ...this.getTemplateDiagnostics()]); + } + /** * Get all Angular-related diagnostics for this compilation. * * If a `ts.SourceFile` is passed, only diagnostics related to that file are returned. */ - getDiagnostics(file?: ts.SourceFile): ts.Diagnostic[] { - if (this.diagnostics === null) { - const compilation = this.ensureAnalyzed(); - this.diagnostics = - [...compilation.traitCompiler.diagnostics, ...this.getTemplateDiagnostics()]; - if (this.entryPoint !== null && compilation.exportReferenceGraph !== null) { - this.diagnostics.push(...checkForPrivateExports( - this.entryPoint, this.tsProgram.getTypeChecker(), compilation.exportReferenceGraph)); - } - } + getDiagnosticsForFile(file: ts.SourceFile, optimizeFor: OptimizeFor): ts.Diagnostic[] { + return this.addMessageTextDetails([ + ...this.getNonTemplateDiagnostics().filter(diag => diag.file === file), + ...this.getTemplateDiagnosticsForFile(file, optimizeFor) + ]); + } - if (file === undefined) { - return this.diagnostics; - } else { - return this.diagnostics.filter(diag => { - if (diag.file === file) { - return true; - } else if (isTemplateDiagnostic(diag) && diag.componentFile === file) { - // Template diagnostics are reported when diagnostics for the component file are - // requested (since no consumer of `getDiagnostics` would ever ask for diagnostics from - // the fake ts.SourceFile for templates). - return true; - } else { - return false; - } - }); - } + /** + * Add Angular.io error guide links to diagnostics for this compilation. + */ + private addMessageTextDetails(diagnostics: ts.Diagnostic[]): ts.Diagnostic[] { + return diagnostics.map(diag => { + if (diag.code && COMPILER_ERRORS_WITH_GUIDES.has(ngErrorCode(diag.code))) { + return { + ...diag, + messageText: diag.messageText + + `. Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/NG${ngErrorCode(diag.code)}` + }; + } + return diag; + }); } /** @@ -281,33 +528,28 @@ export class NgCompiler { if (this.compilation !== null) { return; } - this.compilation = this.makeCompilation(); - const analyzeSpan = this.perfRecorder.start('analyze'); - const promises: Promise[] = []; - for (const sf of this.tsProgram.getSourceFiles()) { - if (sf.isDeclarationFile) { - continue; + await this.perfRecorder.inPhase(PerfPhase.Analysis, async () => { + this.compilation = this.makeCompilation(); + + const promises: Promise[] = []; + for (const sf of this.tsProgram.getSourceFiles()) { + if (sf.isDeclarationFile) { + continue; + } + + let analysisPromise = this.compilation.traitCompiler.analyzeAsync(sf); + this.scanForMwp(sf); + if (analysisPromise !== undefined) { + promises.push(analysisPromise); + } } - const analyzeFileSpan = this.perfRecorder.start('analyzeFile', sf); - let analysisPromise = this.compilation.traitCompiler.analyzeAsync(sf); - this.scanForMwp(sf); - if (analysisPromise === undefined) { - this.perfRecorder.stop(analyzeFileSpan); - } else if (this.perfRecorder.enabled) { - analysisPromise = analysisPromise.then(() => this.perfRecorder.stop(analyzeFileSpan)); - } - if (analysisPromise !== undefined) { - promises.push(analysisPromise); - } - } + await Promise.all(promises); - await Promise.all(promises); - - this.perfRecorder.stop(analyzeSpan); - - this.resolveCompilation(this.compilation.traitCompiler); + this.perfRecorder.memory(PerfCheckpoint.Analysis); + this.resolveCompilation(this.compilation.traitCompiler); + }); } /** @@ -317,9 +559,7 @@ export class NgCompiler { */ listLazyRoutes(entryRoute?: string): LazyRoute[] { if (entryRoute) { - // Note: - // This resolution step is here to match the implementation of the old `AotCompilerHost` (see - // https://github.com/angular/angular/blob/50732e156/packages/compiler-cli/src/transformers/compiler_host.ts#L175-L188). + // htts://github.com/angular/angular/blob/50732e156/packages/compiler-cli/src/transformers/compiler_host.ts#L175-L188). // // `@angular/cli` will always call this API with an absolute path, so the resolution step is // not necessary, but keeping it backwards compatible in case someone else is using the API. @@ -373,7 +613,8 @@ export class NgCompiler { const before = [ ivyTransformFactory( compilation.traitCompiler, compilation.reflector, importRewriter, - compilation.defaultImportTracker, compilation.isCore, this.closureCompilerEnabled), + compilation.defaultImportTracker, this.delegatingPerfRecorder, compilation.isCore, + this.closureCompilerEnabled), aliasTransformFactory(compilation.traitCompiler.exportStatements), compilation.defaultImportTracker.importPreservingTransformer(), ]; @@ -418,30 +659,32 @@ export class NgCompiler { } private analyzeSync(): void { - const analyzeSpan = this.perfRecorder.start('analyze'); - this.compilation = this.makeCompilation(); - for (const sf of this.tsProgram.getSourceFiles()) { - if (sf.isDeclarationFile) { - continue; + this.perfRecorder.inPhase(PerfPhase.Analysis, () => { + this.compilation = this.makeCompilation(); + for (const sf of this.tsProgram.getSourceFiles()) { + if (sf.isDeclarationFile) { + continue; + } + this.compilation.traitCompiler.analyzeSync(sf); + this.scanForMwp(sf); } - const analyzeFileSpan = this.perfRecorder.start('analyzeFile', sf); - this.compilation.traitCompiler.analyzeSync(sf); - this.scanForMwp(sf); - this.perfRecorder.stop(analyzeFileSpan); - } - this.perfRecorder.stop(analyzeSpan); - this.resolveCompilation(this.compilation.traitCompiler); + this.perfRecorder.memory(PerfCheckpoint.Analysis); + + this.resolveCompilation(this.compilation.traitCompiler); + }); } private resolveCompilation(traitCompiler: TraitCompiler): void { - traitCompiler.resolve(); + this.perfRecorder.inPhase(PerfPhase.Resolve, () => { + traitCompiler.resolve(); - this.recordNgModuleScopeDependencies(); + // At this point, analysis is complete and the compiler can now calculate which files need to + // be emitted, so do that. + this.incrementalDriver.recordSuccessfulAnalysis(traitCompiler); - // At this point, analysis is complete and the compiler can now calculate which files need to - // be emitted, so do that. - this.incrementalDriver.recordSuccessfulAnalysis(traitCompiler); + this.perfRecorder.memory(PerfCheckpoint.Resolve); + }); } private get fullTemplateTypeCheck(): boolean { @@ -460,6 +703,8 @@ export class NgCompiler { // is not disabled when `strictTemplates` is enabled. const strictTemplates = !!this.options.strictTemplates; + const useInlineTypeConstructors = this.typeCheckingProgramStrategy.supportsInlineOperations; + // First select a type-checking configuration, based on whether full template type-checking is // requested. let typeCheckingConfig: TypeCheckingConfig; @@ -491,6 +736,11 @@ export class NgCompiler { useContextGenericType: strictTemplates, strictLiteralTypes: true, enableTemplateTypeChecker: this.enableTemplateTypeChecker, + useInlineTypeConstructors, + // Warnings for suboptimal type inference are only enabled if in Language Service mode + // (providing the full TemplateTypeChecker API) and if strict mode is not enabled. In strict + // mode, the user is in full control of type inference. + suggestionsForSuboptimalTypeInference: this.enableTemplateTypeChecker && !strictTemplates, }; } else { typeCheckingConfig = { @@ -515,6 +765,10 @@ export class NgCompiler { useContextGenericType: false, strictLiteralTypes: false, enableTemplateTypeChecker: this.enableTemplateTypeChecker, + useInlineTypeConstructors, + // In "basic" template type-checking mode, no warnings are produced since most things are + // not checked anyways. + suggestionsForSuboptimalTypeInference: false, }; } @@ -558,15 +812,9 @@ export class NgCompiler { } private getTemplateDiagnostics(): ReadonlyArray { - // Skip template type-checking if it's disabled. - if (this.options.ivyTemplateTypeCheck === false && !this.fullTemplateTypeCheck) { - return []; - } - const compilation = this.ensureAnalyzed(); // Get the diagnostics. - const typeCheckSpan = this.perfRecorder.start('typeCheckDiagnostics'); const diagnostics: ts.Diagnostic[] = []; for (const sf of this.tsProgram.getSourceFiles()) { if (sf.isDeclarationFile || this.adapter.isShim(sf)) { @@ -578,79 +826,39 @@ export class NgCompiler { } const program = this.typeCheckingProgramStrategy.getProgram(); - this.perfRecorder.stop(typeCheckSpan); this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, program); this.nextProgram = program; return diagnostics; } - /** - * Reifies the inter-dependencies of NgModules and the components within their compilation scopes - * into the `IncrementalDriver`'s dependency graph. - */ - private recordNgModuleScopeDependencies() { - const recordSpan = this.perfRecorder.start('recordDependencies'); - const depGraph = this.incrementalDriver.depGraph; + private getTemplateDiagnosticsForFile(sf: ts.SourceFile, optimizeFor: OptimizeFor): + ReadonlyArray { + const compilation = this.ensureAnalyzed(); - for (const scope of this.compilation!.scopeRegistry!.getCompilationScopes()) { - const file = scope.declaration.getSourceFile(); - const ngModuleFile = scope.ngModule.getSourceFile(); + // Get the diagnostics. + const diagnostics: ts.Diagnostic[] = []; + if (!sf.isDeclarationFile && !this.adapter.isShim(sf)) { + diagnostics.push(...compilation.templateTypeChecker.getDiagnosticsForFile(sf, optimizeFor)); + } - // A change to any dependency of the declaration causes the declaration to be invalidated, - // which requires the NgModule to be invalidated as well. - depGraph.addTransitiveDependency(ngModuleFile, file); + const program = this.typeCheckingProgramStrategy.getProgram(); + this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, program); + this.nextProgram = program; - // A change to the NgModule file should cause the declaration itself to be invalidated. - depGraph.addDependency(file, ngModuleFile); + return diagnostics; + } - const meta = - 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, - // the module depends not only on the transitive dependencies of the component, but on its - // resources as well. - depGraph.addTransitiveResources(ngModuleFile, file); - - // A change to any directive/pipe in the compilation scope should cause the component to be - // invalidated. - for (const directive of scope.directives) { - // When a directive in scope is updated, the component needs to be recompiled as e.g. a - // selector may have changed. - depGraph.addTransitiveDependency(file, directive.ref.node.getSourceFile()); - } - for (const pipe of scope.pipes) { - // When a pipe in scope is updated, the component needs to be recompiled as e.g. the - // pipe's name may have changed. - depGraph.addTransitiveDependency(file, pipe.ref.node.getSourceFile()); - } - - // Components depend on the entire export scope. In addition to transitive dependencies on - // all directives/pipes in the export scope, they also depend on every NgModule in the - // scope, as changes to a module may add new directives/pipes to the scope. - for (const depModule of scope.ngModules) { - // There is a correctness issue here. To be correct, this should be a transitive - // dependency on the depModule file, since the depModule's exports might change via one of - // its dependencies, even if depModule's file itself doesn't change. However, doing this - // would also trigger recompilation if a non-exported component or directive changed, - // which causes performance issues for rebuilds. - // - // Given the rebuild issue is an edge case, currently we err on the side of performance - // instead of correctness. A correct and performant design would distinguish between - // changes to the depModule which affect its export scope and changes which do not, and - // only add a dependency for the former. This concept is currently in development. - // - // TODO(alxhub): fix correctness issue by understanding the semantics of the dependency. - depGraph.addDependency(file, depModule.getSourceFile()); - } - } else { - // Directives (not components) and pipes only depend on the NgModule which directly declares - // them. - depGraph.addDependency(file, ngModuleFile); + private getNonTemplateDiagnostics(): ts.Diagnostic[] { + if (this.nonTemplateDiagnostics === null) { + const compilation = this.ensureAnalyzed(); + this.nonTemplateDiagnostics = [...compilation.traitCompiler.diagnostics]; + if (this.entryPoint !== null && compilation.exportReferenceGraph !== null) { + this.nonTemplateDiagnostics.push(...checkForPrivateExports( + this.entryPoint, this.tsProgram.getTypeChecker(), compilation.exportReferenceGraph)); } } - this.perfRecorder.stop(recordSpan); + return this.nonTemplateDiagnostics; } private scanForMwp(sf: ts.SourceFile): void { @@ -732,10 +940,12 @@ export class NgCompiler { const scopeRegistry = new LocalModuleScopeRegistry(localMetaReader, depScopeReader, refEmitter, aliasingHost); const scopeReader: ComponentScopeReader = scopeRegistry; + const semanticDepGraphUpdater = this.incrementalDriver.getSemanticDepGraphUpdater(); const metaRegistry = new CompoundMetadataRegistry([localMetaRegistry, scopeRegistry]); const injectableRegistry = new InjectableClassRegistry(reflector); const metaReader = new CompoundMetadataReader([localMetaReader, dtsReader]); + const typeCheckScopeRegistry = new TypeCheckScopeRegistry(scopeReader, metaReader); // If a flat module entrypoint was specified, then track references via a `ReferenceGraph` in @@ -761,52 +971,63 @@ export class NgCompiler { const defaultImportTracker = new DefaultImportTracker(); const resourceRegistry = new ResourceRegistry(); + const compilationMode = + this.options.compilationMode === 'partial' ? CompilationMode.PARTIAL : CompilationMode.FULL; + + // Cycles are handled in full compilation mode by "remote scoping". + // "Remote scoping" does not work well with tree shaking for libraries. + // So in partial compilation mode, when building a library, a cycle will cause an error. + const cycleHandlingStrategy = compilationMode === CompilationMode.FULL ? + CycleHandlingStrategy.UseRemoteScoping : + CycleHandlingStrategy.Error; + // Set up the IvyCompilation, which manages state for the Ivy transformer. - const handlers: DecoratorHandler[] = [ + const handlers: DecoratorHandler[] = [ new ComponentDecoratorHandler( reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, - resourceRegistry, isCore, this.resourceManager, this.adapter.rootDirs, - this.options.preserveWhitespaces || false, this.options.i18nUseExternalIds !== false, - this.options.enableI18nLegacyMessageIdFormat !== false, + typeCheckScopeRegistry, resourceRegistry, isCore, this.resourceManager, + this.adapter.rootDirs, this.options.preserveWhitespaces || false, + this.options.i18nUseExternalIds !== false, + this.options.enableI18nLegacyMessageIdFormat !== false, this.usePoisonedData, this.options.i18nNormalizeLineEndingsInICUs, this.moduleResolver, this.cycleAnalyzer, - refEmitter, defaultImportTracker, this.incrementalDriver.depGraph, injectableRegistry, - this.closureCompilerEnabled), + cycleHandlingStrategy, refEmitter, defaultImportTracker, this.incrementalDriver.depGraph, + injectableRegistry, semanticDepGraphUpdater, this.closureCompilerEnabled, + this.delegatingPerfRecorder), + // TODO(alxhub): understand why the cast here is necessary (something to do with `null` // not being assignable to `unknown` when wrapped in `Readonly`). // clang-format off new DirectiveDecoratorHandler( reflector, evaluator, metaRegistry, scopeRegistry, metaReader, - defaultImportTracker, injectableRegistry, isCore, this.closureCompilerEnabled, - // In ngtsc we no longer want to compile undecorated classes with Angular features. - // Migrations for these patterns ran as part of `ng update` and we want to ensure - // that projects do not regress. See https://hackmd.io/@alx/ryfYYuvzH for more details. - /* compileUndecoratedClassesWithAngularFeatures */ false - ) as Readonly>, + defaultImportTracker, injectableRegistry, isCore, semanticDepGraphUpdater, + this.closureCompilerEnabled, compileUndecoratedClassesWithAngularFeatures, + this.delegatingPerfRecorder, + ) as Readonly>, // clang-format on // Pipe handler must be before injectable handler in list so pipe factories are printed // before injectable factories (so injectable factories can delegate to them) new PipeDecoratorHandler( reflector, evaluator, metaRegistry, scopeRegistry, defaultImportTracker, - injectableRegistry, isCore), + injectableRegistry, isCore, this.delegatingPerfRecorder), new InjectableDecoratorHandler( reflector, defaultImportTracker, isCore, this.options.strictInjectionParameters || false, - injectableRegistry), + injectableRegistry, this.delegatingPerfRecorder), new NgModuleDecoratorHandler( reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, this.adapter.factoryTracker, defaultImportTracker, - this.closureCompilerEnabled, injectableRegistry, this.options.i18nInLocale), + this.closureCompilerEnabled, injectableRegistry, this.delegatingPerfRecorder, + this.options.i18nInLocale), ]; - const compilationMode = - this.options.compilationMode === 'partial' ? CompilationMode.PARTIAL : CompilationMode.FULL; const traitCompiler = new TraitCompiler( - handlers, reflector, this.perfRecorder, this.incrementalDriver, - this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms); + handlers, reflector, this.delegatingPerfRecorder, this.incrementalDriver, + this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms, + semanticDepGraphUpdater); const templateTypeChecker = new TemplateTypeCheckerImpl( this.tsProgram, this.typeCheckingProgramStrategy, traitCompiler, this.getTypeCheckingConfig(), refEmitter, reflector, this.adapter, this.incrementalDriver, - scopeRegistry); + scopeRegistry, typeCheckScopeRegistry, this.delegatingPerfRecorder); return { isCore, @@ -818,6 +1039,7 @@ export class NgCompiler { routeAnalyzer, mwpScanner, metaReader, + typeCheckScopeRegistry, defaultImportTracker, aliasingHost, refEmitter, @@ -873,7 +1095,7 @@ function getR3SymbolsFile(program: ts.Program): ts.SourceFile|null { /** * Since "strictTemplates" is a true superset of type checking capabilities compared to - * "strictTemplateTypeCheck", it is required that the latter is not explicitly disabled if the + * "fullTemplateTypeCheck", it is required that the latter is not explicitly disabled if the * former is enabled. */ function verifyCompatibleTypeCheckOptions(options: NgCompilerOptions): ts.Diagnostic|null { diff --git a/packages/compiler-cli/src/ngtsc/core/src/host.ts b/packages/compiler-cli/src/ngtsc/core/src/host.ts index 24683b131b..b6378b9d2d 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/host.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/host.ts @@ -59,6 +59,7 @@ export class DelegatingCompilerHost implements readDirectory = this.delegateMethod('readDirectory'); readFile = this.delegateMethod('readFile'); readResource = this.delegateMethod('readResource'); + transformResource = this.delegateMethod('transformResource'); realpath = this.delegateMethod('realpath'); resolveModuleNames = this.delegateMethod('resolveModuleNames'); resolveTypeReferenceDirectives = this.delegateMethod('resolveTypeReferenceDirectives'); @@ -101,6 +102,12 @@ export class NgCompilerHost extends DelegatingCompilerHost implements this.constructionDiagnostics = diagnostics; this.inputFiles = [...inputFiles, ...shimAdapter.extraInputFiles]; this.rootDirs = rootDirs; + + if (this.resolveModuleNames === undefined) { + // In order to reuse the module resolution cache during the creation of the type-check + // program, we'll need to provide `resolveModuleNames` if the delegate did not provide one. + this.resolveModuleNames = this.createCachedResolveModuleNamesFunction(); + } } /** @@ -263,4 +270,17 @@ export class NgCompilerHost extends DelegatingCompilerHost implements get unifiedModulesHost(): UnifiedModulesHost|null { return this.fileNameToModuleName !== undefined ? this as UnifiedModulesHost : null; } + + private createCachedResolveModuleNamesFunction(): ts.CompilerHost['resolveModuleNames'] { + const moduleResolutionCache = ts.createModuleResolutionCache( + this.getCurrentDirectory(), this.getCanonicalFileName.bind(this)); + + return (moduleNames, containingFile, reusedNames, redirectedReference, options) => { + return moduleNames.map(moduleName => { + const module = ts.resolveModuleName( + moduleName, containingFile, options, this, moduleResolutionCache, redirectedReference); + return module.resolvedModule; + }); + }; + } } diff --git a/packages/compiler-cli/src/ngtsc/core/test/BUILD.bazel b/packages/compiler-cli/src/ngtsc/core/test/BUILD.bazel index 54dcaad509..92a81665dc 100644 --- a/packages/compiler-cli/src/ngtsc/core/test/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/core/test/BUILD.bazel @@ -17,6 +17,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/incremental", "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/typecheck", + "//packages/compiler-cli/src/ngtsc/typecheck/api", "@npm//typescript", ], ) 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 37be7020c6..e7f59a2103 100644 --- a/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts +++ b/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts @@ -10,15 +10,26 @@ import * as ts from 'typescript'; import {absoluteFrom as _, FileSystem, getFileSystem, getSourceFileOrError, NgtscCompilerHost, setFileSystem} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; -import {NoopIncrementalBuildStrategy} from '../../incremental'; +import {IncrementalBuildStrategy, NoopIncrementalBuildStrategy} from '../../incremental'; import {ClassDeclaration, isNamedClassDeclaration} from '../../reflection'; import {ReusedProgramStrategy} from '../../typecheck'; +import {OptimizeFor, TypeCheckingProgramStrategy} from '../../typecheck/api'; import {NgCompilerOptions} from '../api'; -import {NgCompiler} from '../src/compiler'; +import {freshCompilationTicket, NgCompiler, resourceChangeTicket} from '../src/compiler'; import {NgCompilerHost} from '../src/host'; +function makeFreshCompiler( + host: NgCompilerHost, options: NgCompilerOptions, program: ts.Program, + programStrategy: TypeCheckingProgramStrategy, incrementalStrategy: IncrementalBuildStrategy, + enableTemplateTypeChecker: boolean, usePoisonedData: boolean): NgCompiler { + const ticket = freshCompilationTicket( + program, options, incrementalStrategy, programStrategy, /* perfRecorder */ null, + enableTemplateTypeChecker, usePoisonedData); + return NgCompiler.fromTicket(ticket, host); +} + runInEachFileSystem(() => { describe('NgCompiler', () => { let fs: FileSystem; @@ -49,11 +60,13 @@ runInEachFileSystem(() => { const baseHost = new NgtscCompilerHost(getFileSystem(), options); const host = NgCompilerHost.wrap(baseHost, [COMPONENT], options, /* oldProgram */ null); const program = ts.createProgram({host, options, rootNames: host.inputFiles}); - const compiler = new NgCompiler( + const compiler = makeFreshCompiler( host, options, program, new ReusedProgramStrategy(program, host, options, []), - new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false); + new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, + /* usePoisonedData */ false); - const diags = compiler.getDiagnostics(getSourceFileOrError(program, COMPONENT)); + const diags = compiler.getDiagnosticsForFile( + getSourceFileOrError(program, COMPONENT), OptimizeFor.SingleFile); expect(diags.length).toBe(1); expect(diags[0].messageText).toContain('does_not_exist'); }); @@ -98,9 +111,11 @@ runInEachFileSystem(() => { const program = ts.createProgram({host, options, rootNames: host.inputFiles}); const CmpA = getClass(getSourceFileOrError(program, cmpAFile), 'CmpA'); const CmpC = getClass(getSourceFileOrError(program, cmpCFile), 'CmpC'); - const compiler = new NgCompiler( + + const compiler = makeFreshCompiler( host, options, program, new ReusedProgramStrategy(program, host, options, []), - new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false); + new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, + /* usePoisonedData */ false); const components = compiler.getComponentsWithTemplateFile(templateFile); expect(components).toEqual(new Set([CmpA, CmpC])); }); @@ -149,9 +164,10 @@ runInEachFileSystem(() => { const program = ts.createProgram({host, options, rootNames: host.inputFiles}); const CmpA = getClass(getSourceFileOrError(program, cmpAFile), 'CmpA'); const CmpC = getClass(getSourceFileOrError(program, cmpCFile), 'CmpC'); - const compiler = new NgCompiler( + const compiler = makeFreshCompiler( host, options, program, new ReusedProgramStrategy(program, host, options, []), - new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false); + new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, + /* usePoisonedData */ false); const components = compiler.getComponentsWithStyleFile(styleFile); expect(components).toEqual(new Set([CmpA, CmpC])); }); @@ -182,9 +198,10 @@ runInEachFileSystem(() => { const host = NgCompilerHost.wrap(baseHost, [cmpAFile], options, /* oldProgram */ null); const program = ts.createProgram({host, options, rootNames: host.inputFiles}); const CmpA = getClass(getSourceFileOrError(program, cmpAFile), 'CmpA'); - const compiler = new NgCompiler( + const compiler = makeFreshCompiler( host, options, program, new ReusedProgramStrategy(program, host, options, []), - new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false); + new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, + /* usePoisonedData */ false); const resources = compiler.getComponentResources(CmpA); expect(resources).not.toBeNull(); const {template, styles} = resources!; @@ -217,9 +234,10 @@ runInEachFileSystem(() => { const host = NgCompilerHost.wrap(baseHost, [cmpAFile], options, /* oldProgram */ null); const program = ts.createProgram({host, options, rootNames: host.inputFiles}); const CmpA = getClass(getSourceFileOrError(program, cmpAFile), 'CmpA'); - const compiler = new NgCompiler( + const compiler = makeFreshCompiler( host, options, program, new ReusedProgramStrategy(program, host, options, []), - new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false); + new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, + /* usePoisonedData */ false); const resources = compiler.getComponentResources(CmpA); expect(resources).not.toBeNull(); const {styles} = resources!; @@ -248,9 +266,10 @@ runInEachFileSystem(() => { const baseHost = new NgtscCompilerHost(getFileSystem(), options); const host = NgCompilerHost.wrap(baseHost, [COMPONENT], options, /* oldProgram */ null); const program = ts.createProgram({host, options, rootNames: host.inputFiles}); - const compiler = new NgCompiler( + const compiler = makeFreshCompiler( host, options, program, new ReusedProgramStrategy(program, host, options, []), - new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false); + new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, + /* usePoisonedData */ false); const deps = compiler.getResourceDependencies(getSourceFileOrError(program, COMPONENT)); expect(deps.length).toBe(2); @@ -260,6 +279,53 @@ runInEachFileSystem(() => { ])); }); }); + + describe('resource-only changes', () => { + it('should reuse the full compilation state for a resource-only change', () => { + const COMPONENT = _('/cmp.ts'); + const TEMPLATE = _('/template.html'); + fs.writeFile(COMPONENT, ` + import {Component} from '@angular/core'; + @Component({ + selector: 'test-cmp', + templateUrl: './template.html', + }) + export class Cmp {} + `); + fs.writeFile(TEMPLATE, `

    Resource

    `); + + const options: NgCompilerOptions = { + strictTemplates: true, + }; + const baseHost = new NgtscCompilerHost(getFileSystem(), options); + const host = NgCompilerHost.wrap(baseHost, [COMPONENT], options, /* oldProgram */ null); + const program = ts.createProgram({host, options, rootNames: host.inputFiles}); + const compilerA = makeFreshCompiler( + host, options, program, new ReusedProgramStrategy(program, host, options, []), + new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, + /* usePoisonedData */ false); + + const componentSf = getSourceFileOrError(program, COMPONENT); + + // There should be no diagnostics for the component. + expect(compilerA.getDiagnosticsForFile(componentSf, OptimizeFor.WholeProgram).length) + .toBe(0); + + // Change the resource file and introduce an error. + fs.writeFile(TEMPLATE, `

    Resource

    `); + + // Perform a resource-only incremental step. + const resourceTicket = resourceChangeTicket(compilerA, new Set([TEMPLATE])); + const compilerB = NgCompiler.fromTicket(resourceTicket, host); + + // A resource-only update should reuse the same compiler instance. + expect(compilerB).toBe(compilerA); + + // The new template error should be reported in component diagnostics. + expect(compilerB.getDiagnosticsForFile(componentSf, OptimizeFor.WholeProgram).length) + .toBe(1); + }); + }); }); }); diff --git a/packages/compiler-cli/src/ngtsc/cycles/BUILD.bazel b/packages/compiler-cli/src/ngtsc/cycles/BUILD.bazel index ef45798579..b17665f899 100644 --- a/packages/compiler-cli/src/ngtsc/cycles/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/cycles/BUILD.bazel @@ -9,7 +9,7 @@ ts_library( ]), module_name = "@angular/compiler-cli/src/ngtsc/cycles", deps = [ - "//packages/compiler-cli/src/ngtsc/imports", + "//packages/compiler-cli/src/ngtsc/perf", "@npm//typescript", ], ) diff --git a/packages/compiler-cli/src/ngtsc/cycles/index.ts b/packages/compiler-cli/src/ngtsc/cycles/index.ts index ec2670b62f..251d72e27e 100644 --- a/packages/compiler-cli/src/ngtsc/cycles/index.ts +++ b/packages/compiler-cli/src/ngtsc/cycles/index.ts @@ -6,5 +6,5 @@ * found in the LICENSE file at https://angular.io/license */ -export {CycleAnalyzer} from './src/analyzer'; +export {Cycle, CycleAnalyzer, CycleHandlingStrategy} from './src/analyzer'; export {ImportGraph} from './src/imports'; diff --git a/packages/compiler-cli/src/ngtsc/cycles/src/analyzer.ts b/packages/compiler-cli/src/ngtsc/cycles/src/analyzer.ts index 7bcd1b4457..d9cd9e4533 100644 --- a/packages/compiler-cli/src/ngtsc/cycles/src/analyzer.ts +++ b/packages/compiler-cli/src/ngtsc/cycles/src/analyzer.ts @@ -17,11 +17,17 @@ export class CycleAnalyzer { constructor(private importGraph: ImportGraph) {} /** - * Check whether adding an import from `from` to `to` would create a cycle in the `ts.Program`. + * Check for a cycle to be created in the `ts.Program` by adding an import between `from` and + * `to`. + * + * @returns a `Cycle` object if an import between `from` and `to` would create a cycle; `null` + * otherwise. */ - wouldCreateCycle(from: ts.SourceFile, to: ts.SourceFile): boolean { + wouldCreateCycle(from: ts.SourceFile, to: ts.SourceFile): Cycle|null { // Import of 'from' -> 'to' is illegal if an edge 'to' -> 'from' already exists. - return this.importGraph.transitiveImportsOf(to).has(from); + return this.importGraph.transitiveImportsOf(to).has(from) ? + new Cycle(this.importGraph, from, to) : + null; } /** @@ -34,3 +40,35 @@ export class CycleAnalyzer { this.importGraph.addSyntheticImport(from, to); } } + +/** + * Represents an import cycle between `from` and `to` in the program. + * + * This class allows us to do the work to compute the cyclic path between `from` and `to` only if + * needed. + */ +export class Cycle { + constructor( + private importGraph: ImportGraph, readonly from: ts.SourceFile, readonly to: ts.SourceFile) {} + + /** + * Compute an array of source-files that illustrates the cyclic path between `from` and `to`. + * + * Note that a `Cycle` will not be created unless a path is available between `to` and `from`, + * so `findPath()` will never return `null`. + */ + getPath(): ts.SourceFile[] { + return [this.from, ...this.importGraph.findPath(this.to, this.from)!]; + } +} + + +/** + * What to do if a cycle is detected. + */ +export const enum CycleHandlingStrategy { + /** Add "remote scoping" code to avoid creating a cycle. */ + UseRemoteScoping, + /** Fail the compilation with an error. */ + Error, +} diff --git a/packages/compiler-cli/src/ngtsc/cycles/src/imports.ts b/packages/compiler-cli/src/ngtsc/cycles/src/imports.ts index 988f0da81d..a04f044965 100644 --- a/packages/compiler-cli/src/ngtsc/cycles/src/imports.ts +++ b/packages/compiler-cli/src/ngtsc/cycles/src/imports.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; -import {ModuleResolver} from '../../imports'; +import {PerfPhase, PerfRecorder} from '../../perf'; /** * A cached graph of imports in the `ts.Program`. @@ -19,7 +19,7 @@ import {ModuleResolver} from '../../imports'; export class ImportGraph { private map = new Map>(); - constructor(private resolver: ModuleResolver) {} + constructor(private checker: ts.TypeChecker, private perf: PerfRecorder) {} /** * List the direct (not transitive) imports of a given `ts.SourceFile`. @@ -52,6 +52,44 @@ export class ImportGraph { }); } + /** + * Find an import path from the `start` SourceFile to the `end` SourceFile. + * + * This function implements a breadth first search that results in finding the + * shortest path between the `start` and `end` points. + * + * @param start the starting point of the path. + * @param end the ending point of the path. + * @returns an array of source files that connect the `start` and `end` source files, or `null` if + * no path could be found. + */ + findPath(start: ts.SourceFile, end: ts.SourceFile): ts.SourceFile[]|null { + if (start === end) { + // Escape early for the case where `start` and `end` are the same. + return [start]; + } + + const found = new Set([start]); + const queue: Found[] = [new Found(start, null)]; + + while (queue.length > 0) { + const current = queue.shift()!; + const imports = this.importsOf(current.sourceFile); + for (const importedFile of imports) { + if (!found.has(importedFile)) { + const next = new Found(importedFile, current); + if (next.sourceFile === end) { + // We have hit the target `end` path so we can stop here. + return next.toPath(); + } + found.add(importedFile); + queue.push(next); + } + } + } + return null; + } + /** * Add a record of an import from `sf` to `imported`, that's not present in the original * `ts.Program` but will be remembered by the `ImportGraph`. @@ -63,24 +101,55 @@ export class ImportGraph { } private scanImports(sf: ts.SourceFile): Set { - const imports = new Set(); - // Look through the source file for import statements. - sf.statements.forEach(stmt => { - if ((ts.isImportDeclaration(stmt) || ts.isExportDeclaration(stmt)) && - stmt.moduleSpecifier !== undefined && ts.isStringLiteral(stmt.moduleSpecifier)) { - // Resolve the module to a file, and check whether that file is in the ts.Program. - const moduleName = stmt.moduleSpecifier.text; - const moduleFile = this.resolver.resolveModule(moduleName, sf.fileName); - if (moduleFile !== null && isLocalFile(moduleFile)) { + return this.perf.inPhase(PerfPhase.CycleDetection, () => { + const imports = new Set(); + // Look through the source file for import and export statements. + for (const stmt of sf.statements) { + if ((!ts.isImportDeclaration(stmt) && !ts.isExportDeclaration(stmt)) || + stmt.moduleSpecifier === undefined) { + continue; + } + + const symbol = this.checker.getSymbolAtLocation(stmt.moduleSpecifier); + if (symbol === undefined || symbol.valueDeclaration === undefined) { + // No symbol could be found to skip over this import/export. + continue; + } + const moduleFile = symbol.valueDeclaration; + if (ts.isSourceFile(moduleFile) && isLocalFile(moduleFile)) { // Record this local import. imports.add(moduleFile); } } + return imports; }); - return imports; } } function isLocalFile(sf: ts.SourceFile): boolean { - return !sf.fileName.endsWith('.d.ts'); + return !sf.isDeclarationFile; +} + +/** + * A helper class to track which SourceFiles are being processed when searching for a path in + * `getPath()` above. + */ +class Found { + constructor(readonly sourceFile: ts.SourceFile, readonly parent: Found|null) {} + + /** + * Back track through this found SourceFile and its ancestors to generate an array of + * SourceFiles that form am import path between two SourceFiles. + */ + toPath(): ts.SourceFile[] { + const array: ts.SourceFile[] = []; + let current: Found|null = this; + while (current !== null) { + array.push(current.sourceFile); + current = current.parent; + } + // Pushing and then reversing, O(n), rather than unshifting repeatedly, O(n^2), avoids + // manipulating the array on every iteration: https://stackoverflow.com/a/26370620 + return array.reverse(); + } } diff --git a/packages/compiler-cli/src/ngtsc/cycles/test/BUILD.bazel b/packages/compiler-cli/src/ngtsc/cycles/test/BUILD.bazel index 81efd86e91..21b3aaae06 100644 --- a/packages/compiler-cli/src/ngtsc/cycles/test/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/cycles/test/BUILD.bazel @@ -13,7 +13,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/cycles", "//packages/compiler-cli/src/ngtsc/file_system", "//packages/compiler-cli/src/ngtsc/file_system/testing", - "//packages/compiler-cli/src/ngtsc/imports", + "//packages/compiler-cli/src/ngtsc/perf", "//packages/compiler-cli/src/ngtsc/testing", "@npm//typescript", ], diff --git a/packages/compiler-cli/src/ngtsc/cycles/test/analyzer_spec.ts b/packages/compiler-cli/src/ngtsc/cycles/test/analyzer_spec.ts index f800020cb1..85b058dd8f 100644 --- a/packages/compiler-cli/src/ngtsc/cycles/test/analyzer_spec.ts +++ b/packages/compiler-cli/src/ngtsc/cycles/test/analyzer_spec.ts @@ -8,10 +8,10 @@ import * as ts from 'typescript'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; -import {ModuleResolver} from '../../imports'; -import {CycleAnalyzer} from '../src/analyzer'; +import {NOOP_PERF_RECORDER} from '../../perf'; +import {Cycle, CycleAnalyzer} from '../src/analyzer'; import {ImportGraph} from '../src/imports'; -import {makeProgramFromGraph} from './util'; +import {importPath, makeProgramFromGraph} from './util'; runInEachFileSystem(() => { describe('cycle analyzer', () => { @@ -22,51 +22,61 @@ runInEachFileSystem(() => { const {program, analyzer} = makeAnalyzer('a:b,c;b;c'); const b = getSourceFileOrError(program, (_('/b.ts'))); const c = getSourceFileOrError(program, (_('/c.ts'))); - expect(analyzer.wouldCreateCycle(b, c)).toBe(false); - expect(analyzer.wouldCreateCycle(c, b)).toBe(false); + expect(analyzer.wouldCreateCycle(b, c)).toBe(null); + expect(analyzer.wouldCreateCycle(c, b)).toBe(null); }); it('should detect a simple cycle between two files', () => { const {program, analyzer} = makeAnalyzer('a:b;b'); const a = getSourceFileOrError(program, (_('/a.ts'))); const b = getSourceFileOrError(program, (_('/b.ts'))); - expect(analyzer.wouldCreateCycle(a, b)).toBe(false); - expect(analyzer.wouldCreateCycle(b, a)).toBe(true); + expect(analyzer.wouldCreateCycle(a, b)).toBe(null); + const cycle = analyzer.wouldCreateCycle(b, a); + expect(cycle).toBeInstanceOf(Cycle); + expect(importPath(cycle!.getPath())).toEqual('b,a,b'); }); it('should detect a cycle with a re-export in the chain', () => { const {program, analyzer} = makeAnalyzer('a:*b;b:c;c'); const a = getSourceFileOrError(program, (_('/a.ts'))); + const b = getSourceFileOrError(program, (_('/b.ts'))); const c = getSourceFileOrError(program, (_('/c.ts'))); - expect(analyzer.wouldCreateCycle(a, c)).toBe(false); - expect(analyzer.wouldCreateCycle(c, a)).toBe(true); + expect(analyzer.wouldCreateCycle(a, c)).toBe(null); + const cycle = analyzer.wouldCreateCycle(c, a); + expect(cycle).toBeInstanceOf(Cycle); + expect(importPath(cycle!.getPath())).toEqual('c,a,b,c'); }); it('should detect a cycle in a more complex program', () => { const {program, analyzer} = makeAnalyzer('a:*b,*c;b:*e,*f;c:*g,*h;e:f;f:c;g;h:g'); const b = getSourceFileOrError(program, (_('/b.ts'))); + const c = getSourceFileOrError(program, (_('/c.ts'))); + const e = getSourceFileOrError(program, (_('/e.ts'))); + const f = getSourceFileOrError(program, (_('/f.ts'))); const g = getSourceFileOrError(program, (_('/g.ts'))); - expect(analyzer.wouldCreateCycle(b, g)).toBe(false); - expect(analyzer.wouldCreateCycle(g, b)).toBe(true); + expect(analyzer.wouldCreateCycle(b, g)).toBe(null); + const cycle = analyzer.wouldCreateCycle(g, b); + expect(cycle).toBeInstanceOf(Cycle); + expect(importPath(cycle!.getPath())).toEqual('g,b,f,c,g'); }); it('should detect a cycle caused by a synthetic edge', () => { const {program, analyzer} = makeAnalyzer('a:b,c;b;c'); const b = getSourceFileOrError(program, (_('/b.ts'))); const c = getSourceFileOrError(program, (_('/c.ts'))); - expect(analyzer.wouldCreateCycle(b, c)).toBe(false); + expect(analyzer.wouldCreateCycle(b, c)).toBe(null); analyzer.recordSyntheticImport(c, b); - expect(analyzer.wouldCreateCycle(b, c)).toBe(true); + const cycle = analyzer.wouldCreateCycle(b, c); + expect(cycle).toBeInstanceOf(Cycle); + expect(importPath(cycle!.getPath())).toEqual('b,c,b'); }); }); function makeAnalyzer(graph: string): {program: ts.Program, analyzer: CycleAnalyzer} { - const {program, options, host} = makeProgramFromGraph(getFileSystem(), graph); - const moduleResolver = - new ModuleResolver(program, options, host, /* moduleResolutionCache */ null); + const {program} = makeProgramFromGraph(getFileSystem(), graph); return { program, - analyzer: new CycleAnalyzer(new ImportGraph(moduleResolver)), + analyzer: new CycleAnalyzer(new ImportGraph(program.getTypeChecker(), NOOP_PERF_RECORDER)), }; } }); diff --git a/packages/compiler-cli/src/ngtsc/cycles/test/imports_spec.ts b/packages/compiler-cli/src/ngtsc/cycles/test/imports_spec.ts index b834fa70fc..f88500643d 100644 --- a/packages/compiler-cli/src/ngtsc/cycles/test/imports_spec.ts +++ b/packages/compiler-cli/src/ngtsc/cycles/test/imports_spec.ts @@ -8,58 +8,86 @@ import * as ts from 'typescript'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; -import {ModuleResolver} from '../../imports'; +import {NOOP_PERF_RECORDER} from '../../perf'; import {ImportGraph} from '../src/imports'; -import {makeProgramFromGraph} from './util'; +import {importPath, makeProgramFromGraph} from './util'; runInEachFileSystem(() => { - describe('import graph', () => { + describe('ImportGraph', () => { let _: typeof absoluteFrom; beforeEach(() => _ = absoluteFrom); - it('should record imports of a simple program', () => { - const {program, graph} = makeImportGraph('a:b;b:c;c'); - const a = getSourceFileOrError(program, (_('/a.ts'))); - const b = getSourceFileOrError(program, (_('/b.ts'))); - const c = getSourceFileOrError(program, (_('/c.ts'))); - expect(importsToString(graph.importsOf(a))).toBe('b'); - expect(importsToString(graph.importsOf(b))).toBe('c'); + describe('importsOf()', () => { + it('should record imports of a simple program', () => { + const {program, graph} = makeImportGraph('a:b;b:c;c'); + const a = getSourceFileOrError(program, (_('/a.ts'))); + const b = getSourceFileOrError(program, (_('/b.ts'))); + const c = getSourceFileOrError(program, (_('/c.ts'))); + expect(importsToString(graph.importsOf(a))).toBe('b'); + expect(importsToString(graph.importsOf(b))).toBe('c'); + }); }); - it('should calculate transitive imports of a simple program', () => { - const {program, graph} = makeImportGraph('a:b;b:c;c'); - const a = getSourceFileOrError(program, (_('/a.ts'))); - const b = getSourceFileOrError(program, (_('/b.ts'))); - const c = getSourceFileOrError(program, (_('/c.ts'))); - expect(importsToString(graph.transitiveImportsOf(a))).toBe('a,b,c'); + describe('transitiveImportsOf()', () => { + it('should calculate transitive imports of a simple program', () => { + const {program, graph} = makeImportGraph('a:b;b:c;c'); + const a = getSourceFileOrError(program, (_('/a.ts'))); + const b = getSourceFileOrError(program, (_('/b.ts'))); + const c = getSourceFileOrError(program, (_('/c.ts'))); + expect(importsToString(graph.transitiveImportsOf(a))).toBe('a,b,c'); + }); + + it('should calculate transitive imports in a more complex program (with a cycle)', () => { + const {program, graph} = makeImportGraph('a:*b,*c;b:*e,*f;c:*g,*h;e:f;f;g:e;h:g'); + const c = getSourceFileOrError(program, (_('/c.ts'))); + expect(importsToString(graph.transitiveImportsOf(c))).toBe('c,e,f,g,h'); + }); + + it('should reflect the addition of a synthetic import', () => { + const {program, graph} = makeImportGraph('a:b,c,d;b;c;d:b'); + const b = getSourceFileOrError(program, (_('/b.ts'))); + const c = getSourceFileOrError(program, (_('/c.ts'))); + const d = getSourceFileOrError(program, (_('/d.ts'))); + expect(importsToString(graph.importsOf(b))).toEqual(''); + expect(importsToString(graph.transitiveImportsOf(d))).toEqual('b,d'); + graph.addSyntheticImport(b, c); + expect(importsToString(graph.importsOf(b))).toEqual('c'); + expect(importsToString(graph.transitiveImportsOf(d))).toEqual('b,c,d'); + }); }); - it('should calculate transitive imports in a more complex program (with a cycle)', () => { - const {program, graph} = makeImportGraph('a:*b,*c;b:*e,*f;c:*g,*h;e:f;f;g:e;h:g'); - const c = getSourceFileOrError(program, (_('/c.ts'))); - expect(importsToString(graph.transitiveImportsOf(c))).toBe('c,e,f,g,h'); - }); + describe('findPath()', () => { + it('should be able to compute the path between two source files if there is a cycle', () => { + const {program, graph} = makeImportGraph('a:*b,*c;b:*e,*f;c:*g,*h;e:f;f;g:e;h:g'); + const a = getSourceFileOrError(program, (_('/a.ts'))); + const b = getSourceFileOrError(program, (_('/b.ts'))); + const c = getSourceFileOrError(program, (_('/c.ts'))); + const e = getSourceFileOrError(program, (_('/e.ts'))); + expect(importPath(graph.findPath(a, a)!)).toBe('a'); + expect(importPath(graph.findPath(a, b)!)).toBe('a,b'); + expect(importPath(graph.findPath(c, e)!)).toBe('c,g,e'); + expect(graph.findPath(e, c)).toBe(null); + expect(graph.findPath(b, c)).toBe(null); + }); - it('should reflect the addition of a synthetic import', () => { - const {program, graph} = makeImportGraph('a:b,c,d;b;c;d:b'); - const b = getSourceFileOrError(program, (_('/b.ts'))); - const c = getSourceFileOrError(program, (_('/c.ts'))); - const d = getSourceFileOrError(program, (_('/d.ts'))); - expect(importsToString(graph.importsOf(b))).toEqual(''); - expect(importsToString(graph.transitiveImportsOf(d))).toEqual('b,d'); - graph.addSyntheticImport(b, c); - expect(importsToString(graph.importsOf(b))).toEqual('c'); - expect(importsToString(graph.transitiveImportsOf(d))).toEqual('b,c,d'); + it('should handle circular dependencies within the path between `from` and `to`', () => { + // a -> b -> c -> d + // ^----/ | + // ^---------/ + const {program, graph} = makeImportGraph('a:b;b:a,c;c:a,d;d'); + const a = getSourceFileOrError(program, (_('/a.ts'))); + const c = getSourceFileOrError(program, (_('/c.ts'))); + const d = getSourceFileOrError(program, (_('/d.ts'))); + expect(importPath(graph.findPath(a, d)!)).toBe('a,b,c,d'); + }); }); }); function makeImportGraph(graph: string): {program: ts.Program, graph: ImportGraph} { - const {program, options, host} = makeProgramFromGraph(getFileSystem(), graph); - const moduleResolver = - new ModuleResolver(program, options, host, /* moduleResolutionCache */ null); + const {program} = makeProgramFromGraph(getFileSystem(), graph); return { program, - graph: new ImportGraph(moduleResolver), + graph: new ImportGraph(program.getTypeChecker(), NOOP_PERF_RECORDER), }; } diff --git a/packages/compiler-cli/src/ngtsc/cycles/test/util.ts b/packages/compiler-cli/src/ngtsc/cycles/test/util.ts index f46ee75e11..c4a8ca4ed4 100644 --- a/packages/compiler-cli/src/ngtsc/cycles/test/util.ts +++ b/packages/compiler-cli/src/ngtsc/cycles/test/util.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import * as ts from 'typescript'; -import {FileSystem} from '../../file_system'; +import {getFileSystem, PathManipulation} from '../../file_system'; import {TestFile} from '../../file_system/testing'; import {makeProgram} from '../../testing'; @@ -31,7 +31,7 @@ import {makeProgram} from '../../testing'; * * represents a program where a.ts exports from b.ts and imports from c.ts. */ -export function makeProgramFromGraph(fs: FileSystem, graph: string): { +export function makeProgramFromGraph(fs: PathManipulation, graph: string): { program: ts.Program, host: ts.CompilerHost, options: ts.CompilerOptions, @@ -56,3 +56,8 @@ export function makeProgramFromGraph(fs: FileSystem, graph: string): { }); return makeProgram(files); } + +export function importPath(files: ts.SourceFile[]): string { + const fs = getFileSystem(); + return files.map(sf => fs.basename(sf.fileName).replace('.ts', '')).join(','); +} diff --git a/packages/compiler-cli/src/ngtsc/diagnostics/index.ts b/packages/compiler-cli/src/ngtsc/diagnostics/index.ts index 991719e867..fcd7f12098 100644 --- a/packages/compiler-cli/src/ngtsc/diagnostics/index.ts +++ b/packages/compiler-cli/src/ngtsc/diagnostics/index.ts @@ -7,5 +7,5 @@ */ export {FatalDiagnosticError, isFatalDiagnosticError, makeDiagnostic, makeRelatedInformation} from './src/error'; -export {ErrorCode, ngErrorCode} from './src/error_code'; +export {COMPILER_ERRORS_WITH_GUIDES, ERROR_DETAILS_PAGE_BASE_URL, ErrorCode, ngErrorCode} from './src/error_code'; export {replaceTsWithNgInErrors} from './src/util'; diff --git a/packages/compiler-cli/src/ngtsc/diagnostics/src/error_code.ts b/packages/compiler-cli/src/ngtsc/diagnostics/src/error_code.ts index b1e767dcf2..910abcbde6 100644 --- a/packages/compiler-cli/src/ngtsc/diagnostics/src/error_code.ts +++ b/packages/compiler-cli/src/ngtsc/diagnostics/src/error_code.ts @@ -44,8 +44,19 @@ export enum ErrorCode { */ UNDECORATED_CLASS_USING_ANGULAR_FEATURES = 2007, + /** + * Raised when an component cannot resolve an external resource, such as a template or a style + * sheet. + */ + COMPONENT_RESOURCE_NOT_FOUND = 2008, + SYMBOL_NOT_EXPORTED = 3001, SYMBOL_EXPORTED_UNDER_DIFFERENT_NAME = 3002, + /** + * Raised when a relationship between directives and/or pipes would cause a cyclic import to be + * created that cannot be handled, such as in partial compilation mode. + */ + IMPORT_CYCLE_DETECTED = 3003, CONFIG_FLAT_MODULE_NO_INDEX = 4001, CONFIG_STRICT_TEMPLATES_IMPLIES_FULL_TEMPLATE_TYPECHECK = 4002, @@ -159,8 +170,47 @@ export enum ErrorCode { * An injectable already has a `ɵprov` property. */ INJECTABLE_DUPLICATE_PROV = 9001, + + // 10XXX error codes are reserved for diagnostics with categories other than + // `ts.DiagnosticCategory.Error`. These diagnostics are generated by the compiler when configured + // to do so by a tool such as the Language Service, or by the Language Service itself. + + /** + * Suggest users to enable `strictTemplates` to make use of full capabilities + * provided by Angular language service. + */ + SUGGEST_STRICT_TEMPLATES = 10001, + + /** + * Indicates that a particular structural directive provides advanced type narrowing + * functionality, but the current template type-checking configuration does not allow its usage in + * type inference. + */ + SUGGEST_SUBOPTIMAL_TYPE_INFERENCE = 10002, } +/** + * @internal + * Base URL for the error details page. + * Keep this value in sync with a similar const in + * `packages/core/src/render3/error_code.ts`. + */ +export const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.io/errors'; + +/** + * @internal + * Contains a set of error messages that have detailed guides at angular.io. + * Full list of available error guides can be found at https://angular.io/errors + */ +export const COMPILER_ERRORS_WITH_GUIDES = new Set([ + ErrorCode.DECORATOR_ARG_NOT_LITERAL, + ErrorCode.IMPORT_CYCLE_DETECTED, + ErrorCode.PARAM_MISSING_TOKEN, + ErrorCode.SCHEMA_INVALID_ELEMENT, + ErrorCode.SCHEMA_INVALID_ATTRIBUTE, + ErrorCode.MISSING_REFERENCE_TARGET, +]); + /** * @internal */ diff --git a/packages/compiler-cli/src/ngtsc/file_system/README.md b/packages/compiler-cli/src/ngtsc/file_system/README.md index 06a29acf08..bce731dd07 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/README.md +++ b/packages/compiler-cli/src/ngtsc/file_system/README.md @@ -3,7 +3,15 @@ To improve cross platform support, all file access (and path manipulation) is now done through a well known interface (`FileSystem`). -For testing a number of `MockFileSystem` implementations are supplied. +Note that `FileSystem` extends `ReadonlyFileSystem`, which itself extends +`PathManipulation`. +If you are using a file-system object you should only ask for the type that supports +all the methods that you require. +For example, if you have a function (`foo()`) that only needs to resolve paths then +it should only require `PathManipulation`: `foo(fs: PathManipulation)`. +This allows the caller to avoid implementing unneeded functionality. + +For testing, a number of `MockFileSystem` implementations are supplied. These provide an in-memory file-system which emulates operating systems like OS/X, Unix and Windows. @@ -16,6 +24,11 @@ To prevent this happening accidentally the current file system always starts out as an instance of `InvalidFileSystem`, which will throw an error if any of its methods are called. +Generally it is safer to explicitly pass file-system objects to constructors or +free-standing functions if possible. This avoids confusing bugs where the +global file-system has not been set-up correctly before calling functions that +expect there to be a file-system configured globally. + You can set the current file-system by calling `setFileSystem()`. During testing you can call the helper function `initMockFileSystem(os)` which takes a string name of the OS to emulate, and will also monkey-patch diff --git a/packages/compiler-cli/src/ngtsc/file_system/index.ts b/packages/compiler-cli/src/ngtsc/file_system/index.ts index 218f99de4b..7b25db8e8e 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/index.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/index.ts @@ -9,5 +9,5 @@ export {NgtscCompilerHost} from './src/compiler_host'; export {absoluteFrom, absoluteFromSourceFile, basename, dirname, getFileSystem, isLocalRelativePath, isRoot, isRooted, join, relative, relativeFrom, resolve, setFileSystem, toRelativeImport} 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'; +export {AbsoluteFsPath, FileStats, FileSystem, PathManipulation, PathSegment, PathString, ReadonlyFileSystem} from './src/types'; export {getSourceFileOrError} from './src/util'; 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 4440928319..12c91285c8 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 @@ -9,74 +9,18 @@ import * as fs from 'fs'; import * as fsExtra from 'fs-extra'; import * as p from 'path'; -import {absoluteFrom} from './helpers'; -import {AbsoluteFsPath, FileStats, FileSystem, PathSegment, PathString} from './types'; +import {AbsoluteFsPath, FileStats, FileSystem, PathManipulation, PathSegment, PathString, ReadonlyFileSystem} from './types'; /** - * A wrapper around the Node.js file-system (i.e the `fs` package). + * A wrapper around the Node.js file-system that supports path manipulation. */ -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'); - } - readFileBuffer(path: AbsoluteFsPath): Uint8Array { - return fs.readFileSync(path); - } - writeFile(path: AbsoluteFsPath, data: string|Uint8Array, 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); - } +export class NodeJSPathManipulation implements PathManipulation { 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)) { - parents.push(path); - path = this.dirname(path); - } - while (parents.length) { - this.safeMkdir(parents.pop()!); - } - } - removeDeep(path: AbsoluteFsPath): void { - fsExtra.removeSync(path); - } - isCaseSensitive(): boolean { - if (this._caseSensitive === undefined) { - // Note the use of the real file-system is intentional: - // `this.exists()` relies upon `isCaseSensitive()` so that would cause an infinite recursion. - this._caseSensitive = !fs.existsSync(togglePathCase(__filename)); - } - return this._caseSensitive; - } resolve(...paths: string[]): AbsoluteFsPath { return this.normalize(p.resolve(...paths)) as AbsoluteFsPath; } @@ -102,15 +46,82 @@ export class NodeJSFileSystem implements FileSystem { extname(path: AbsoluteFsPath|PathSegment): string { return p.extname(path); } + normalize(path: T): T { + // Convert backslashes to forward slashes + return path.replace(/\\/g, '/') as T; + } +} + +/** + * A wrapper around the Node.js file-system that supports readonly operations and path manipulation. + */ +export class NodeJSReadonlyFileSystem extends NodeJSPathManipulation implements ReadonlyFileSystem { + private _caseSensitive: boolean|undefined = undefined; + isCaseSensitive(): boolean { + if (this._caseSensitive === undefined) { + // Note the use of the real file-system is intentional: + // `this.exists()` relies upon `isCaseSensitive()` so that would cause an infinite recursion. + this._caseSensitive = !fs.existsSync(this.normalize(toggleCase(__filename))); + } + return this._caseSensitive; + } + exists(path: AbsoluteFsPath): boolean { + return fs.existsSync(path); + } + readFile(path: AbsoluteFsPath): string { + return fs.readFileSync(path, 'utf8'); + } + readFileBuffer(path: AbsoluteFsPath): Uint8Array { + return fs.readFileSync(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); + } realpath(path: AbsoluteFsPath): AbsoluteFsPath { return this.resolve(fs.realpathSync(path)); } getDefaultLibLocation(): AbsoluteFsPath { return this.resolve(require.resolve('typescript'), '..'); } - normalize(path: T): T { - // Convert backslashes to forward slashes - return path.replace(/\\/g, '/') as T; +} + +/** + * A wrapper around the Node.js file-system (i.e. the `fs` package). + */ +export class NodeJSFileSystem extends NodeJSReadonlyFileSystem implements FileSystem { + writeFile(path: AbsoluteFsPath, data: string|Uint8Array, 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); + } + 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)) { + parents.push(path); + path = this.dirname(path); + } + while (parents.length) { + this.safeMkdir(parents.pop()!); + } + } + removeDeep(path: AbsoluteFsPath): void { + fsExtra.removeSync(path); } private safeMkdir(path: AbsoluteFsPath): void { @@ -127,9 +138,8 @@ export class NodeJSFileSystem implements FileSystem { } /** - * Toggle the case of each character in a file path. + * Toggle the case of each character in a string. */ -function togglePathCase(str: string): AbsoluteFsPath { - return absoluteFrom( - str.replace(/\w/g, ch => ch.toUpperCase() === ch ? ch.toLowerCase() : ch.toUpperCase())); +function toggleCase(str: string): string { + return str.replace(/\w/g, ch => ch.toUpperCase() === ch ? ch.toLowerCase() : ch.toUpperCase()); } 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 bdc660e301..6720de0b45 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/types.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/types.ts @@ -29,33 +29,14 @@ export type AbsoluteFsPath = BrandedPath<'AbsoluteFsPath'>; export type PathSegment = BrandedPath<'PathSegment'>; /** - * A basic interface to abstract the underlying file-system. - * - * This makes it easier to provide mock file-systems in unit tests, - * but also to create clever file-systems that have features such as caching. + * An abstraction over the path manipulation aspects of a file-system. */ -export interface FileSystem { - exists(path: AbsoluteFsPath): boolean; - readFile(path: AbsoluteFsPath): string; - readFileBuffer(path: AbsoluteFsPath): Uint8Array; - writeFile(path: AbsoluteFsPath, data: string|Uint8Array, exclusive?: boolean): void; - removeFile(path: AbsoluteFsPath): void; - symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void; - readdir(path: AbsoluteFsPath): PathSegment[]; - lstat(path: AbsoluteFsPath): FileStats; - stat(path: AbsoluteFsPath): FileStats; - pwd(): AbsoluteFsPath; - chdir(path: AbsoluteFsPath): void; +export interface PathManipulation { extname(path: AbsoluteFsPath|PathSegment): string; - copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void; - moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void; - ensureDir(path: AbsoluteFsPath): void; - removeDeep(path: AbsoluteFsPath): void; - isCaseSensitive(): boolean; isRoot(path: AbsoluteFsPath): boolean; isRooted(path: string): boolean; - resolve(...paths: string[]): AbsoluteFsPath; dirname(file: T): T; + extname(path: AbsoluteFsPath|PathSegment): string; join(basePath: T, ...paths: string[]): T; /** * Compute the relative path between `from` and `to`. @@ -66,9 +47,41 @@ export interface FileSystem { */ relative(from: T, to: T): PathSegment|AbsoluteFsPath; basename(filePath: string, extension?: string): PathSegment; + normalize(path: T): T; + resolve(...paths: string[]): AbsoluteFsPath; + pwd(): AbsoluteFsPath; + chdir(path: AbsoluteFsPath): void; +} + +/** + * An abstraction over the read-only aspects of a file-system. + */ +export interface ReadonlyFileSystem extends PathManipulation { + isCaseSensitive(): boolean; + exists(path: AbsoluteFsPath): boolean; + readFile(path: AbsoluteFsPath): string; + readFileBuffer(path: AbsoluteFsPath): Uint8Array; + readdir(path: AbsoluteFsPath): PathSegment[]; + lstat(path: AbsoluteFsPath): FileStats; + stat(path: AbsoluteFsPath): FileStats; realpath(filePath: AbsoluteFsPath): AbsoluteFsPath; getDefaultLibLocation(): AbsoluteFsPath; - normalize(path: T): T; +} + +/** + * A basic interface to abstract the underlying file-system. + * + * This makes it easier to provide mock file-systems in unit tests, + * but also to create clever file-systems that have features such as caching. + */ +export interface FileSystem extends ReadonlyFileSystem { + writeFile(path: AbsoluteFsPath, data: string|Uint8Array, exclusive?: boolean): void; + removeFile(path: AbsoluteFsPath): void; + symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void; + copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void; + moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void; + ensureDir(path: AbsoluteFsPath): void; + removeDeep(path: AbsoluteFsPath): void; } export type PathString = string|AbsoluteFsPath|PathSegment; 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 ea87f7dc62..4cd7cfb60c 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 @@ -8,22 +8,55 @@ import * as realFs from 'fs'; import * as fsExtra from 'fs-extra'; import * as os from 'os'; -import {absoluteFrom, dirname, relativeFrom, setFileSystem} from '../src/helpers'; -import {NodeJSFileSystem} from '../src/node_js_file_system'; -import {AbsoluteFsPath} from '../src/types'; +import {NodeJSFileSystem, NodeJSPathManipulation, NodeJSReadonlyFileSystem} from '../src/node_js_file_system'; +import {AbsoluteFsPath, PathSegment} from '../src/types'; -describe('NodeJSFileSystem', () => { - let fs: NodeJSFileSystem; +describe('NodeJSPathManipulation', () => { + let fs: NodeJSPathManipulation; let abcPath: AbsoluteFsPath; let xyzPath: AbsoluteFsPath; beforeEach(() => { - fs = new NodeJSFileSystem(); - // Set the file-system so that calls like `absoluteFrom()` - // and `relativeFrom()` work correctly. - setFileSystem(fs); - abcPath = absoluteFrom('/a/b/c'); - xyzPath = absoluteFrom('/x/y/z'); + fs = new NodeJSPathManipulation(); + abcPath = fs.resolve('/a/b/c'); + xyzPath = fs.resolve('/x/y/z'); + }); + + describe('pwd()', () => { + it('should delegate to process.cwd()', () => { + const spy = spyOn(process, 'cwd').and.returnValue(abcPath); + const result = fs.pwd(); + expect(result).toEqual(abcPath); + expect(spy).toHaveBeenCalledWith(); + }); + }); + + if (os.platform() === 'win32') { + // Only relevant on Windows + describe('relative', () => { + it('should handle Windows paths on different drives', () => { + expect(fs.relative('C:\\a\\b\\c', 'D:\\a\\b\\d')).toEqual(fs.resolve('D:\\a\\b\\d')); + }); + }); + } +}); + +describe('NodeJSReadonlyFileSystem', () => { + let fs: NodeJSReadonlyFileSystem; + let abcPath: AbsoluteFsPath; + let xyzPath: AbsoluteFsPath; + + beforeEach(() => { + fs = new NodeJSReadonlyFileSystem(); + abcPath = fs.resolve('/a/b/c'); + xyzPath = fs.resolve('/x/y/z'); + }); + + describe('isCaseSensitive()', () => { + it('should return true if the FS is case-sensitive', () => { + const isCaseSensitive = !realFs.existsSync(__filename.toUpperCase()); + expect(fs.isCaseSensitive()).toEqual(isCaseSensitive); + }); }); describe('exists()', () => { @@ -55,30 +88,11 @@ describe('NodeJSFileSystem', () => { }); }); - describe('writeFile()', () => { - it('should delegate to fs.writeFileSync()', () => { - const spy = spyOn(realFs, 'writeFileSync'); - 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', {flag: 'wx'}); - }); - }); - - describe('removeFile()', () => { - it('should delegate to fs.unlink()', () => { - const spy = spyOn(realFs, 'unlinkSync'); - fs.removeFile(abcPath); - expect(spy).toHaveBeenCalledWith(abcPath); - }); - }); - describe('readdir()', () => { it('should delegate to fs.readdirSync()', () => { 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(result).toEqual(['x' as PathSegment, 'y/z' as PathSegment]); // 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); @@ -106,13 +120,35 @@ describe('NodeJSFileSystem', () => { expect(spy as any).toHaveBeenCalledWith(abcPath); }); }); +}); - describe('pwd()', () => { - it('should delegate to process.cwd()', () => { - const spy = spyOn(process, 'cwd').and.returnValue(abcPath); - const result = fs.pwd(); - expect(result).toEqual(abcPath); - expect(spy).toHaveBeenCalledWith(); +describe('NodeJSFileSystem', () => { + let fs: NodeJSFileSystem; + let abcPath: AbsoluteFsPath; + let xyzPath: AbsoluteFsPath; + + beforeEach(() => { + fs = new NodeJSFileSystem(); + abcPath = fs.resolve('/a/b/c'); + xyzPath = fs.resolve('/x/y/z'); + }); + + describe('writeFile()', () => { + it('should delegate to fs.writeFileSync()', () => { + const spy = spyOn(realFs, 'writeFileSync'); + 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', {flag: 'wx'}); + }); + }); + + describe('removeFile()', () => { + it('should delegate to fs.unlink()', () => { + const spy = spyOn(realFs, 'unlinkSync'); + fs.removeFile(abcPath); + expect(spy).toHaveBeenCalledWith(abcPath); }); }); @@ -134,10 +170,10 @@ describe('NodeJSFileSystem', () => { describe('ensureDir()', () => { it('should call exists() and fs.mkdir()', () => { - const aPath = absoluteFrom('/a'); - const abPath = absoluteFrom('/a/b'); - const xPath = absoluteFrom('/x'); - const xyPath = absoluteFrom('/x/y'); + const aPath = fs.resolve('/a'); + const abPath = fs.resolve('/a/b'); + const xPath = fs.resolve('/x'); + const xyPath = fs.resolve('/x/y'); const mkdirCalls: string[] = []; const existsCalls: string[] = []; spyOn(realFs, 'mkdirSync').and.callFake(((path: string) => mkdirCalls.push(path)) as any); @@ -190,7 +226,7 @@ describe('NodeJSFileSystem', () => { fs.ensureDir(abcPath); expect(mkdirSyncSpy).toHaveBeenCalledTimes(3); expect(mkdirSyncSpy).toHaveBeenCalledWith(abcPath); - expect(mkdirSyncSpy).toHaveBeenCalledWith(dirname(abcPath)); + expect(mkdirSyncSpy).toHaveBeenCalledWith(fs.dirname(abcPath)); }); it('should fail if creating the directory throws and the directory does not exist', () => { @@ -239,20 +275,4 @@ describe('NodeJSFileSystem', () => { expect(spy).toHaveBeenCalledWith(abcPath); }); }); - - describe('isCaseSensitive()', () => { - it('should return true if the FS is case-sensitive', () => { - const isCaseSensitive = !realFs.existsSync(__filename.toUpperCase()); - expect(fs.isCaseSensitive()).toEqual(isCaseSensitive); - }); - }); - - if (os.platform() === 'win32') { - // Only relevant on Windows - describe('relative', () => { - it('should handle Windows paths on different drives', () => { - expect(fs.relative('C:\\a\\b\\c', 'D:\\a\\b\\d')).toEqual(absoluteFrom('D:\\a\\b\\d')); - }); - }); - } }); diff --git a/packages/compiler-cli/src/ngtsc/imports/index.ts b/packages/compiler-cli/src/ngtsc/imports/index.ts index 6ee65f03af..8843ad1824 100644 --- a/packages/compiler-cli/src/ngtsc/imports/index.ts +++ b/packages/compiler-cli/src/ngtsc/imports/index.ts @@ -9,7 +9,7 @@ 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'; +export {AbsoluteModuleStrategy, EmittedReference, ImportedFile, ImportFlags, LocalIdentifierStrategy, LogicalProjectStrategy, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesStrategy} from './src/emitter'; export {Reexport} from './src/reexport'; export {OwningModule, Reference} from './src/references'; export {ModuleResolver} from './src/resolver'; diff --git a/packages/compiler-cli/src/ngtsc/imports/src/alias.ts b/packages/compiler-cli/src/ngtsc/imports/src/alias.ts index 1eec3c2a84..7af8ffebf0 100644 --- a/packages/compiler-cli/src/ngtsc/imports/src/alias.ts +++ b/packages/compiler-cli/src/ngtsc/imports/src/alias.ts @@ -10,13 +10,12 @@ import {Expression, ExternalExpr} from '@angular/compiler'; import * as ts from 'typescript'; import {UnifiedModulesHost} from '../../core/api'; -import {ClassDeclaration, isNamedClassDeclaration, ReflectionHost} from '../../reflection'; +import {ClassDeclaration, ReflectionHost} from '../../reflection'; -import {ImportFlags, ReferenceEmitStrategy} from './emitter'; +import {EmittedReference, ImportFlags, ReferenceEmitStrategy} from './emitter'; import {Reference} from './references'; - // Escape anything that isn't alphanumeric, '/' or '_'. const CHARS_TO_ESCAPE = /[^a-zA-Z0-9/_]/g; @@ -213,11 +212,11 @@ export class PrivateExportAliasingHost implements AliasingHost { * directive or pipe, if it exists. */ export class AliasStrategy implements ReferenceEmitStrategy { - emit(ref: Reference, context: ts.SourceFile, importMode: ImportFlags): Expression|null { - if (importMode & ImportFlags.NoAliasing) { + emit(ref: Reference, context: ts.SourceFile, importMode: ImportFlags): EmittedReference|null { + if (importMode & ImportFlags.NoAliasing || ref.alias === null) { return null; } - return ref.alias; + return {expression: ref.alias, importedFile: 'unknown'}; } } diff --git a/packages/compiler-cli/src/ngtsc/imports/src/core.ts b/packages/compiler-cli/src/ngtsc/imports/src/core.ts index eae858337b..e952336dd9 100644 --- a/packages/compiler-cli/src/ngtsc/imports/src/core.ts +++ b/packages/compiler-cli/src/ngtsc/imports/src/core.ts @@ -59,11 +59,11 @@ const CORE_SUPPORTED_SYMBOLS = new Map([ ['ɵɵdefineNgModule', 'ɵɵdefineNgModule'], ['ɵɵsetNgModuleScope', 'ɵɵsetNgModuleScope'], ['ɵɵinject', 'ɵɵinject'], - ['ɵɵFactoryDef', 'ɵɵFactoryDef'], + ['ɵɵFactoryDeclaration', 'ɵɵFactoryDeclaration'], ['ɵsetClassMetadata', 'setClassMetadata'], ['ɵɵInjectableDef', 'ɵɵInjectableDef'], - ['ɵɵInjectorDef', 'ɵɵInjectorDef'], - ['ɵɵNgModuleDefWithMeta', 'ɵɵNgModuleDefWithMeta'], + ['ɵɵInjectorDeclaration', 'ɵɵInjectorDeclaration'], + ['ɵɵNgModuleDeclaration', 'ɵɵNgModuleDeclaration'], ['ɵNgModuleFactory', 'NgModuleFactory'], ['ɵnoSideEffects', 'ɵnoSideEffects'], ]); diff --git a/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts b/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts index aa77bb1884..f21dca0ce1 100644 --- a/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts +++ b/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {UnifiedModulesHost} from '../../core/api'; import {absoluteFromSourceFile, dirname, LogicalFileSystem, LogicalProjectPath, relative, toRelativeImport} from '../../file_system'; import {stripExtension} from '../../file_system/src/util'; -import {DeclarationNode, isConcreteDeclaration, ReflectionHost} from '../../reflection'; +import {DeclarationNode, ReflectionHost} from '../../reflection'; import {getSourceFile, isDeclaration, isTypeDeclaration, nodeNameForError} from '../../util/src/typescript'; import {findExportedNameOfNode} from './find_export'; @@ -51,6 +51,36 @@ export enum ImportFlags { AllowTypeImports = 0x04, } +/** + * An emitter strategy has the ability to indicate which `ts.SourceFile` is being imported by the + * expression that it has generated. This information is useful for consumers of the emitted + * reference that would otherwise have to perform a relatively expensive module resolution step, + * e.g. for cyclic import analysis. In cases the emitter is unable to definitively determine the + * imported source file or a computation would be required to actually determine the imported + * source file, then `'unknown'` should be returned. If the generated expression does not represent + * an import then `null` should be used. + */ +export type ImportedFile = ts.SourceFile|'unknown'|null; + +/** + * Represents the emitted expression of a `Reference` that is valid in the source file it was + * emitted from. + */ +export interface EmittedReference { + /** + * The expression that refers to `Reference`. + */ + expression: Expression; + + /** + * The `ts.SourceFile` that is imported by `expression`. This is not necessarily the source file + * of the `Reference`'s declaration node, as the reference may have been rewritten through an + * alias export. It could also be `null` if `expression` is a local identifier, or `'unknown'` if + * the exact source file that is being imported is not known to the emitter. + */ + importedFile: ImportedFile; +} + /** * A particular strategy for generating an expression which refers to a `Reference`. * @@ -71,9 +101,10 @@ export interface ReferenceEmitStrategy { * @param ref the `Reference` for which to generate an expression * @param context the source file in which the `Expression` must be valid * @param importFlags a flag which controls whether imports should be generated or not - * @returns an `Expression` which refers to the `Reference`, or `null` if none can be generated + * @returns an `EmittedReference` which refers to the `Reference`, or `null` if none can be + * generated */ - emit(ref: Reference, context: ts.SourceFile, importFlags: ImportFlags): Expression|null; + emit(ref: Reference, context: ts.SourceFile, importFlags: ImportFlags): EmittedReference|null; } /** @@ -85,8 +116,8 @@ export interface ReferenceEmitStrategy { export class ReferenceEmitter { constructor(private strategies: ReferenceEmitStrategy[]) {} - emit(ref: Reference, context: ts.SourceFile, importFlags: ImportFlags = ImportFlags.None): - Expression { + emit(ref: Reference, context: ts.SourceFile, importFlags: ImportFlags = ImportFlags.None): + EmittedReference { for (const strategy of this.strategies) { const emitted = strategy.emit(ref, context, importFlags); if (emitted !== null) { @@ -103,7 +134,7 @@ export class ReferenceEmitter { * such identifiers are available. */ export class LocalIdentifierStrategy implements ReferenceEmitStrategy { - emit(ref: Reference, context: ts.SourceFile, importFlags: ImportFlags): Expression|null { + emit(ref: Reference, context: ts.SourceFile, importFlags: ImportFlags): EmittedReference|null { const refSf = getSourceFile(ref.node); // If the emitter has specified ForceNewImport, then LocalIdentifierStrategy should not use a @@ -118,20 +149,41 @@ export class LocalIdentifierStrategy implements ReferenceEmitStrategy { // such a case, the reference's `identities` property would be `[foo]`, which would result in an // invalid emission of a free-standing `foo` identifier, rather than `exports.foo`. if (!isDeclaration(ref.node) && refSf === context) { - return new WrappedNodeExpr(ref.node); + return { + expression: new WrappedNodeExpr(ref.node), + importedFile: null, + }; } // A Reference can have multiple identities in different files, so it may already have an // Identifier in the requested context file. const identifier = ref.getIdentityIn(context); if (identifier !== null) { - return new WrappedNodeExpr(identifier); + return { + expression: new WrappedNodeExpr(identifier), + importedFile: null, + }; } else { return null; } } } +/** + * Represents the exported declarations from a module source file. + */ +interface ModuleExports { + /** + * The source file of the module. + */ + module: ts.SourceFile; + + /** + * The map of declarations to their exported name. + */ + exportMap: Map; +} + /** * A `ReferenceEmitStrategy` which will refer to declarations that come from `node_modules` using * an absolute import. @@ -146,13 +198,13 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { * A cache of the exports of specific modules, because resolving a module to its exports is a * costly operation. */ - private moduleExportsCache = new Map|null>(); + private moduleExportsCache = new Map(); constructor( protected program: ts.Program, protected checker: ts.TypeChecker, protected moduleResolver: ModuleResolver, private reflectionHost: ReflectionHost) {} - emit(ref: Reference, context: ts.SourceFile, importFlags: ImportFlags): Expression|null { + emit(ref: Reference, context: ts.SourceFile, importFlags: ImportFlags): EmittedReference|null { if (ref.bestGuessOwningModule === null) { // There is no module name available for this Reference, meaning it was arrived at via a // relative path. @@ -168,38 +220,30 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { // Try to find the exported name of the declaration, if one is available. const {specifier, resolutionContext} = ref.bestGuessOwningModule; - const symbolName = this.resolveImportName(specifier, ref.node, resolutionContext); - if (symbolName === null) { + const exports = this.getExportsOfModule(specifier, resolutionContext); + if (exports === null || !exports.exportMap.has(ref.node)) { // 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})`); } + const symbolName = exports.exportMap.get(ref.node)!; - return new ExternalExpr(new ExternalReference(specifier, symbolName)); + return { + expression: new ExternalExpr(new ExternalReference(specifier, symbolName)), + importedFile: exports.module, + }; } - private resolveImportName(moduleName: string, target: DeclarationNode, fromFile: string): string - |null { - const exports = this.getExportsOfModule(moduleName, fromFile); - if (exports !== null && exports.has(target)) { - return exports.get(target)!; - } else { - return null; - } - } - - private getExportsOfModule(moduleName: string, fromFile: string): - Map|null { + private getExportsOfModule(moduleName: string, fromFile: string): ModuleExports|null { if (!this.moduleExportsCache.has(moduleName)) { this.moduleExportsCache.set(moduleName, this.enumerateExportsOfModule(moduleName, fromFile)); } return this.moduleExportsCache.get(moduleName)!; } - protected enumerateExportsOfModule(specifier: string, fromFile: string): - Map|null { + protected enumerateExportsOfModule(specifier: string, fromFile: string): ModuleExports|null { // First, resolve the module specifier to its entry point, and get the ts.Symbol for it. const entryPointFile = this.moduleResolver.resolveModule(specifier, fromFile); if (entryPointFile === null) { @@ -214,7 +258,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { exports.forEach((declaration, name) => { exportMap.set(declaration.node, name); }); - return exportMap; + return {module: entryPointFile, exportMap}; } } @@ -229,7 +273,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { export class LogicalProjectStrategy implements ReferenceEmitStrategy { constructor(private reflector: ReflectionHost, private logicalFs: LogicalFileSystem) {} - emit(ref: Reference, context: ts.SourceFile): Expression|null { + emit(ref: Reference, context: ts.SourceFile): EmittedReference|null { const destSf = getSourceFile(ref.node); // Compute the relative path from the importing file to the file being imported. This is done @@ -260,7 +304,10 @@ export class LogicalProjectStrategy implements ReferenceEmitStrategy { // With both files expressed as LogicalProjectPaths, getting the module specifier as a relative // path is now straightforward. const moduleName = LogicalProjectPath.relativePathBetween(originPath, destPath); - return new ExternalExpr({moduleName, name}); + return { + expression: new ExternalExpr({moduleName, name}), + importedFile: destSf, + }; } } @@ -273,14 +320,14 @@ export class LogicalProjectStrategy implements ReferenceEmitStrategy { export class RelativePathStrategy implements ReferenceEmitStrategy { constructor(private reflector: ReflectionHost) {} - emit(ref: Reference, context: ts.SourceFile): Expression|null { + emit(ref: Reference, context: ts.SourceFile): EmittedReference|null { const destSf = getSourceFile(ref.node); const relativePath = relative(dirname(absoluteFromSourceFile(context)), absoluteFromSourceFile(destSf)); const moduleName = toRelativeImport(stripExtension(relativePath)); const name = findExportedNameOfNode(ref.node, destSf, this.reflector); - return new ExternalExpr({moduleName, name}); + return {expression: new ExternalExpr({moduleName, name}), importedFile: destSf}; } } @@ -291,7 +338,7 @@ export class RelativePathStrategy implements ReferenceEmitStrategy { export class UnifiedModulesStrategy implements ReferenceEmitStrategy { constructor(private reflector: ReflectionHost, private unifiedModulesHost: UnifiedModulesHost) {} - emit(ref: Reference, context: ts.SourceFile): Expression|null { + emit(ref: Reference, context: ts.SourceFile): EmittedReference|null { const destSf = getSourceFile(ref.node); const name = findExportedNameOfNode(ref.node, destSf, this.reflector); if (name === null) { @@ -301,6 +348,9 @@ export class UnifiedModulesStrategy implements ReferenceEmitStrategy { const moduleName = this.unifiedModulesHost.fileNameToModuleName(destSf.fileName, context.fileName); - return new ExternalExpr({moduleName, name}); + return { + expression: new ExternalExpr({moduleName, name}), + importedFile: destSf, + }; } } 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 79fbf5c986..c556a1cf72 100644 --- a/packages/compiler-cli/src/ngtsc/imports/test/emitter_spec.ts +++ b/packages/compiler-cli/src/ngtsc/imports/test/emitter_spec.ts @@ -72,11 +72,14 @@ runInEachFileSystem(() => { resolutionContext: context.fileName, }); const emitted = strategy.emit(reference, context, ImportFlags.None); - if (!(emitted instanceof ExternalExpr)) { + if (emitted === null) { + return fail('Reference should be emitted'); + } + if (!(emitted.expression instanceof ExternalExpr)) { return fail('Reference should be emitted as ExternalExpr'); } - expect(emitted.value.name).toEqual('Bar'); - expect(emitted.value.moduleName).toEqual('external'); + expect(emitted.expression.value.name).toEqual('Bar'); + expect(emitted.expression.value.moduleName).toEqual('external'); }); it('should throw when generating an import to a type-only declaration when not allowed', () => { @@ -121,11 +124,14 @@ runInEachFileSystem(() => { const reference = new Reference(decl, {specifier: 'external', resolutionContext: context.fileName}); const emitted = strategy.emit(reference, context, ImportFlags.AllowTypeImports); - if (!(emitted instanceof ExternalExpr)) { + if (emitted === null) { + return fail('Reference should be emitted'); + } + if (!(emitted.expression instanceof ExternalExpr)) { return fail('Reference should be emitted as ExternalExpr'); } - expect(emitted.value.name).toEqual('Foo'); - expect(emitted.value.moduleName).toEqual('external'); + expect(emitted.expression.value.name).toEqual('Foo'); + expect(emitted.expression.value.moduleName).toEqual('external'); }); }); @@ -165,7 +171,7 @@ runInEachFileSystem(() => { expect(ref).not.toBeNull(); // Expect the prefixed name from the TestHost. - expect((ref! as ExternalExpr).value.name).toEqual('testFoo'); + expect((ref!.expression as ExternalExpr).value.name).toEqual('testFoo'); }); }); }); diff --git a/packages/compiler-cli/src/ngtsc/incremental/BUILD.bazel b/packages/compiler-cli/src/ngtsc/incremental/BUILD.bazel index db2afaa805..0e89dfbed2 100644 --- a/packages/compiler-cli/src/ngtsc/incremental/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/incremental/BUILD.bazel @@ -11,8 +11,10 @@ ts_library( ":api", "//packages/compiler-cli/src/ngtsc/file_system", "//packages/compiler-cli/src/ngtsc/imports", + "//packages/compiler-cli/src/ngtsc/incremental/semantic_graph", "//packages/compiler-cli/src/ngtsc/metadata", "//packages/compiler-cli/src/ngtsc/partial_evaluator", + "//packages/compiler-cli/src/ngtsc/perf", "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/scope", "//packages/compiler-cli/src/ngtsc/transform", @@ -27,6 +29,7 @@ ts_library( srcs = ["api.ts"], deps = [ "//packages/compiler-cli/src/ngtsc/file_system", + "//packages/compiler-cli/src/ngtsc/reflection", "@npm//typescript", ], ) diff --git a/packages/compiler-cli/src/ngtsc/incremental/README.md b/packages/compiler-cli/src/ngtsc/incremental/README.md index 94faa48071..a5ed27edd8 100644 --- a/packages/compiler-cli/src/ngtsc/incremental/README.md +++ b/packages/compiler-cli/src/ngtsc/incremental/README.md @@ -1,154 +1,291 @@ -# What is the `incremental` package? +# Incremental Compilation -This package contains logic related to incremental compilation in ngtsc. +The `incremental` package contains logic related to incremental compilation in ngtsc. Its goal is to +ensure that the compiler's incremental performance is largely O(number of files changed in that +iteration) instead of O(size of the program as a whole), by allowing the compiler to optimize away +as much work as possible without sacrificing the correctness of its output. -In particular, it tracks dependencies between `ts.SourceFile`s, so the compiler can make intelligent decisions about when it's safe to skip certain operations. +An incremental compilation receives information about the prior compilation, including +its `ts.Program` and the result of ngtsc's analyses of each class in that program. Depending on the +nature of any changes made to files in the program between its prior and current versions, and on +the semantic effect of those changes, ngtsc may perform 3 different optimizations as it processes +the new build: + +* It can reuse analysis work performed in the previous program -# What optimizations are made? +ngtsc receives the analyses of all decorated classes performed as part of the previous compilation, +and can reuse that work for a class if it can prove that the results are not stale. -ngtsc currently makes two optimizations: reuse of prior analysis work, and the skipping of file emits. +* It can skip emitting a file -## Reuse of analyses +Emitting a file is a very expensive operation in TypeScript, involving the execution of many +internal TS transforms (downleveling, module system, etc) as well as the synthesis of a large text +buffer for the final JS output. Skipping emit of a file is the most effective optimizations ngtsc +can do. It's also one of the most challenging. Even if ngtsc's _analysis_ of a specific file is not +stale, that file may still need to be re-emitted if other changes in the program impact its +semantics. For example, a change to a component selector affects other components which use that +selector in their templates, even though no direct dependency exists between them. -If a build has succeeded previously, ngtsc has available the analyses of all Angular classes in the prior program, as well as the dependency graph which outlines inter-file dependencies. This is known as the "last good compilation". +* It can reuse template type-checking code -When the next build begins, ngtsc follows a simple algorithm which reuses prior work where possible: +Template type-checking code is generated using semantic information extracted from the user's +program. This generation can be expensive, and ngtsc attempts to reuse previous results as much as +possible. This optimization can be thought of as a special case of the above re-emit optimization, +since template type-checking code is a particular flavor of "emit" for a component. -1) For each input file, ngtsc makes a determination as to whether the file is "logically changed". +Due to the way that template type-checking works (creation of a second `ts.Program` +with `.ngtypecheck` files containing template type-checking blocks, or TCBs), reuse of template +type-checking code is critical for good performance. Not only is generation of these TCBs expensive, +but forcing TypeScript to re-parse and re-analyze every `.ngtypecheck` file on each incremental +change would be costly as well. -"Logically changed" means that either: +The `incremental` package is dedicated to allowing ngtsc to make these important optimizations +safely. + +During an incremental compilation, the compiler begins with a process called "reconciliation", +focused on understanding the differences between the incoming, new `ts.Program` and the +last `ts.Program`. In TypeScript, an unchanged file will have its `ts.SourceFile` AST completely +reused. Reconciliation therefore examines the `ts.SourceFile`s of both the old and new programs, and +identifies files which have been added, removed, or changed. This information feeds in to the rest +of the incremental compilation process. + +## Reuse of analysis results + +Angular's process of understanding an individual component, directive, or other decorated class is +known as "analysis". Analysis is always performed on a class-by-class basis, so the analysis of a +component only takes into consideration information present in the `@Component` decorator, and not +for example in the `@NgModule` which declares the component. + +However, analysis _can_ depend on information outside of the decorated class's file. This can happen +in two ways: + +* External resources, such as templates or stylesheets, are covered by analysis. +* The partial evaluation of expressions within a class's metadata may descend into symbols imported + from other files. + +For example, a directive's selector may be determined via an imported constant: -* The file itself has physically changed on disk, or -* One of the file's dependencies has physically changed on disk. +```typescript= +import {Directive} from '@angular/core'; +import {DIR_SELECTOR} from './selectors'; + +@Directive({ + selector: DIR_SELECTOR, +}) +export class Dir {} +``` + +The analysis of this directive _depends on_ the value of `DIR_SELECTOR` from `selectors.ts`. +Consequently, if `selectors.ts` changes, `Dir` needs to be re-analyzed, even if `dir.ts` has not +changed. + +The `incremental` system provides a mechanism which tracks such dependencies at the file level. The +partial evaluation system records dependencies for any given evaluation operation when an import +boundary is crossed, building up a file-to-file dependency graph. This graph is then transmitted to +the next incremental compilation, where it can be used to determine, based on the set of files +physically changed on disk, which files have _logically_ changed and need to be re-analyzed. -Either of these conditions invalidates the previous analysis of the file. +## Reuse of emit results + +In plain TypeScript programs, the compiled JavaScript code for any given input file (e.g. `foo.ts`) +depends only on the code within that input file. That is, only the contents of `foo.ts` can affect +the generated contents written to `foo.js`. The TypeScript compiler can therefore perform a very +simple optimization, and avoid generating and emitting code for any input files which do not change. +This is important for good incremental build performance, as emitting a file is a very expensive +operation. -2) ngtsc begins constructing a new dependency graph. +(in practice, the TypeScript feature of `const enum` declarations breaks this overly simple model) -For each logically unchanged file, its dependencies are copied wholesale into the new graph. +In Angular applications, however, this optimization is not nearly so simple. The emit of a `.js` +file in Angular is affected in four main ways: -3) ngtsc begins analyzing each file in the program. +* Just as in plain TS, it depends on the contents of the input `.ts` file. +* It can be affected by expressions that were statically evaluated during analysis of any decorated + classes in the input, and these expressions can depend on other files. -If the file is logically unchanged, ngtsc will reuse the previous analysis and only call the 'register' phase of compilation, to apply any necessary side effects. +For example, the directive with its selector specified via the imported `DIR_SELECTOR` constant +above has compilation output which depends on the value of `DIR_SELECTOR`. Therefore, the `dir.js` +file needs to be emitted whenever the value of the selector constant in `selectors.ts` changes, even +if `dir.ts` itself is unchanged. The compiler therefore will re-emit `dir.js` if the `dir.ts` file +is determined to have _logically_ changed, using the same dependency graph that powers analysis +reuse. -If the file is logically changed, ngtsc will re-analyze it. +* Components can have external templates and CSS stylesheets which influence their compilation. -## Reuse of template type-checking code +These are incorporated into a component's analysis dependencies. -Generally speaking, the generation of a template type-checking "shim" for an input component file is a time-consuming operation. Such generation produces several outputs: +* Components (and NgModules) are influenced by the NgModule graph, which controls which directives + and pipes are "in scope" for each component's template. -1) The text of the template type-checking shim file, which can later be fed to TypeScript for the production of raw diagnostics. -2) Metadata regarding source mappings within the template type-checking shim, which can be used to convert the raw diagnostics into mapped template diagnostics. -3) "Construction" diagnostics, which are diagnostics produced as a side effect of generation of the shim itself. +This last relationship is the most difficult, as there is no import relationship between a component +and the directives and pipes it uses in its template. That means that a component file can be +logically unchanged, but still require re-emit if one of its dependencies has been updated in a way +that influences the compilation of the component. -When a component file is logically unchanged, ngtsc attempts to reuse this generation work. As part of creating both the new emit program and template type-checking program, the `ts.SourceFile` of the shim for the component file is included directly and not re-generated. +### Example -At the same time, the metadata and construction diagnostics are passed via the incremental build system. When TS gets diagnostics for the shim file, this metadata is used to convert them into mapped template diagnostics for delivery to the user. +For example, the output of a compiled component includes an array called `directiveDefs`, listing +all of the directives and components actually used within the component's template. This array is +built by combining the template (from analysis) with the "scope" of the component - the set of +directives and pipes which are available for use in its template. This scope is synthesized from the +analysis of not just the component's NgModule, but other NgModules which might be imported, and the +components/directives that those NgModules export, and their analysis data as well. -### Limitations on template type-checking reuse +These dependencies of a component on the directives/pipes it consumes, and the NgModule structures +that made them visible, are not captured in the file-level dependency graph. This is due to the +peculiar nature of NgModule and component relationships: NgModules import components, so there is +never a reference from a component to its NgModule, or any of its directive or pipe dependencies. -In certain cases the template type-checking system is unable to use the existing shim code. If the component is logically changed, the shim is regenerated in case its contents may have changed. If generating the shim itself required the use of any "inline" code (type-checking code which needs to be inserted into the component file instead for some reason), it also becomes ineligible for reuse. +In code, this looks like: -## Skipping emit +```typescript= +// dir.ts +@Directive({selector: '[dir]'}) +export class Dir {} -ngtsc makes a decision to skip the emit of a file if it can prove that the contents of the file will not have changed since the last good compilation. To prove this, two conditions must be true. +// cmp.ts +@Component({ + selector: 'cmp', + template: '
    ', // Matches the `[dir]` selector +}) +export class Cmp {} -* The input file itself must not have changed since the previous compilation. +// mod.ts +import {Dir} from './dir'; +import {Cmp} from './cmp'; -* None of the files on which the input file is dependent have changed since the previous compilation. +@NgModule({declarations: [Dir, Cmp]}) +export class Mod {} +``` -The second condition is challenging to prove, as Angular allows statically evaluated expressions in lots of contexts that could result in changes from file to file. For example, the `name` of an `@Pipe` could be a reference to a constant in a different file. As part of analyzing the program, the compiler keeps track of such dependencies in order to answer this question. +Here, `Cmp` never directly imports or refers to `Dir`, but it _does_ consume the directive in its +template. During emit, `Cmp` would receive a `directiveDefs` array: -The emit of a file is the most expensive part of TypeScript/Angular compilation, so skipping emits when they are not necessary is one of the most valuable things the compiler can do to improve incremental build performance. +```typescript= +// cmp.js +import * as i1 from './dir'; -## The two dependency graphs +export class Cmp { + static cmp = defineComponent({ + ... + directiveDefs: [i1.Dir], + }); +} +``` -For both of the above optimizations, ngtsc makes use of dependency information extracted from the program. But these usages are subtly different. +If `Dir`'s selector were to change to `[other]` in an incremental step, it might no longer +match `Cmp`'s template, in which case `cmp.js` would need to be re-emitted. -To reuse previous analyses, ngtsc uses the _prior_ compilation's dependency graph, plus the information about which files have changed, to determine whether it's safe to reuse the prior compilation's work. +### SemanticSymbols -To skip emit, ngtsc uses the _current_ compilation's dependency graph, coupled with the information about which files have changed since the last successful build, to determine the set of outputs that need to be re-emitted. +For each decorated class being processed, the compiler creates a `SemanticSymbol` representing the +data regarding that class that's involved in these "indirect" relationships. During the +compiler's `resolve` phase, these `SemanticSymbol`s are connected together to form a "semantic +dependency graph". Two classes of data are recorded: -# How does incremental compilation work? +* Information about the public shape API of the class. -The initial compilation is no different from a standalone compilation; the compiler is unaware that incremental compilation will be utilized. +For example, directives have a public API which includes their selector, any inputs or outputs, and +their `exportAs` name if any. -When an `NgtscProgram` is created for a _subsequent_ compilation, it is initialized with the `NgtscProgram` from the previous compilation. It is therefore able to take advantage of any information present in the previous compilation to optimize the next one. +* Information about the emit shape of the class, including any dependencies on + other `SemanticSymbol`s. -This information is leveraged in two major ways: +This information allows the compiler to determine which classes have been semantically affected by +other changes in the program (and therefore need to be re-emitted) according to a simple algorithm: -1) The previous `ts.Program` itself is used to create the next `ts.Program`, allowing TypeScript internally to leverage information from the previous compile in much the same way. +1. Determine the set of `SemanticSymbol`s which have had their public API changed. +2. For each `SemanticSymbol`, determine if its emit shape was affected by any of the public API + changes (that is, if it depends on a symbol with public API changes). -2) An `IncrementalDriver` instance is constructed from the old and new `ts.Program`s, and the previous program's `IncrementalDriver`. +### Determination of public API changes -The compiler then proceeds normally, using the `IncrementalDriver` to manage the reuse of any pertinent information while processing the new program. As a part of this process, the compiler (again) maps out all of the dependencies between files. +The first step of this algorithm is to determine, for each `SemanticSymbol`, if its public API has +been affected. Doing this requires knowing which `SemanticSymbol` in the previous program +corresponds to the current version of the symbol. There are two ways that symbols can be "matched": -## Determination of files to emit +* The old and new symbols share the same `ts.ClassDeclaration`. -The principle question the incremental build system must answer is "which TS files need to be emitted for a given compilation?" +This is true whenever the `ts.SourceFile` declaring the class has not changed between the old and +new programs. The public API of the symbol may still have changed (such as when a directive's +selector is determined by a constant imported from another file, like in one of the examples above). +But if the declaration file itself has not changed, then the previous symbol can be directly found +this way. -To determine whether an individual TS file needs to be emitted, the compiler must determine 3 things about the file: +* By its unique path and name. -1. Have its contents changed since the last time it was emitted? -2. Has any resource file that the TS file depends on (like an HTML template) changed since the last time it was emitted? -3. Have any of the dependencies of the TS file changed since the last time it was emitted? +If the file _has_ changed, then symbols can be located by their declaration path plus their name, if +they have a name that's guaranteed to be unique. Currently, this means that the classes are declared +at the top level of the source file, so their names are in the module's scope. If this is the case, +then a symbol can be matched to its ancestor even if the declaration itself has changed in the +meantime. Note that there is no guarantee the symbol will be of the same type - an incremental step +may change a directive into a component, or even into a pipe or injectable. -If the answer to any of these questions is yes, then the TS file needs to be re-emitted. +Once a previous symbol is located, its public API can be compared against the current version of the +symbol. Symbols without a valid ancestor are assumed to have changed in their public API. -## Tracking of changes +The compiler processes all `SemanticSymbol`s and determines the `Set` of them which have experienced +public API changes. In the example above, this `Set` would include the `DirectiveSymbol` for `Dir`, +since its selector would have changed. -On every invocation, the compiler receives (or can easily determine) several pieces of information: +### Determination of emit requirements -* The set of `ts.SourceFile`s that have changed since the last invocation. -* The set of resources (`.html` files) that have changed since the last invocation. +For each potential output file, the compiler then looks at all declared `SemanticSymbol`s and uses +their ancestor symbol (if present) as well as the `Set` of public API changes to make a +determination if that file needs be emitted. -With this information, the compiler can perform rebuild optimizations: +In the case of a `ComponentSymbol`, for example, the symbol tracks the dependencies of the component +which will go into the `directiveDefs` array. If that array is different, the component needs to be +re-emitted. Even if the same directives are referenced, if one of those directives has changed in +its public API, the emitted output (especially when generating prelink library code) may be +affected, and the component needs to be re-emitted. -1. The compiler uses the last good compilation's dependency graph to determine which parts of its analysis work can be reused, and an initial set of files which need to be re-emitted. -2. The compiler analyzes the rest of the program and generates an updated dependency graph, which describes the relationships between files in the program as they are currently. -3. Based on this graph, the compiler can make a final determination for each TS file whether it needs to be re-emitted or can safely be skipped. This produces a set called `pendingEmit` of every file which requires a re-emit. -4. The compiler cycles through the files and emits those which are necessary, removing them from `pendingEmit`. +### `SemanticReference`s -Theoretically, after this process `pendingEmit` should be empty. As a precaution against errors which might happen in the future, `pendingEmit` is also passed into future compilations, so any files which previously were determined to need an emit (but have not been successfully produced yet) will be retried on subsequent compilations. This is mostly relevant if a client of `ngtsc` attempts to implement emit-on-error functionality. +`ComponentSymbol`s track their dependencies via an intermediate type, a `SemanticReference`. Such +references track not only the `SemanticSymbol` of the dependency, but also the name by which it was +imported previously. Even if a dependency's identity and public API remain the same, changes in how +it was exported can affect the import which needs to be emitted within the component consuming it, +and thus would require a re-emit. -However, normally the execution of these steps requires a correct input program. In the presence of TypeScript errors, the compiler cannot perform this process. It might take many invocations for the user to fix all their TypeScript errors and reach a compilation that can be analyzed. +## Reuse of template type-checking results -As a result, the compiler must accumulate the set of these changes (to source files and resource files) from build to build until analysis can succeed. +Since type-checking block (TCB) generation for template type-checking is a form of +emit, `SemanticSymbol`s also track the type-checking shape of decorated classes. This includes any +data which is not public API, but upon which the TCB generation for components might depend. Such +data includes: -This accumulation happens via a type called `BuildState`. This type is a union of two possible states. +* Type-checking API shape from any base classes, since TCB generation uses information from the full + inheritance chain of a directive/pipe. +* The generic signature shape of the class. +* Private field names for `@Input`s and `@Output`s. -### `PendingBuildState` +Using a similar algorithm to the `emit` optimization, the compiler can determine which files need +their type-checking code regenerated, and which can continue to use TCB code from the previous +program, even if some dependencies have unrelated changes. -This is the initial state of any build, and the final state of any unsuccessful build. This state tracks both `pendingEmit` files from the previous program as well as any source or resource files which have changed since the last successful analysis. +## Unsuccessful compilation attempts -If a new build starts and inherits from a failed build, it will merge the failed build's `PendingBuildState` into its own, including the sets of changed files. +Often, incremental compilations will fail. The user's input program may contain incomplete changes, +typos, semantic errors, or other problems which prevent the compiler from fully analyzing or +emitting it. Such errors create problems for incremental build correctness, as the compiler relies +on information extracted from the previous program to correctly optimize the next compilation. If +the previous compilation failed, such information may be unreliable. -### `AnalyzedBuildState` +In theory, the compiler could simply not perform incremental compilation on top of a broken build, +and assume that it must redo all analysis and re-emit all files, but this would result in +devestatingly poor performance for common developer workflows that rely on automatically running +builds and/or tests on every change. The compiler must deal with such scenarios more gracefully. -After analysis is successfully performed, the compiler uses its dependency graph to evaluate the impact of any accumulated changes from the `PendingBuildState`, and updates `pendingEmit` with all of the pending files. At this point, the compiler transitions from a `PendingBuildState` to an `AnalyzedBuildState`, which only tracks `pendingEmit`. In `AnalyzedBuildState` this set is complete, and the raw changes can be forgotten. +ngtsc solves this problem by always performing its incremental steps from a "last known good" +compilation. Thus, if compilation A succeeds, and a subsequent compilation B fails, compilation C +will begin using the state of compilation A as a starting point. This requires tracking of two +important pieces of state: -If a new build is started after a successful build, only `pendingEmit` from the `AnalyzedBuildState` needs to be merged into the new build's `PendingBuildState`. +* Reusable information, such as analysis results, from the last known good compilation. +* The accumulated set of files which have physically changed since the last known good compilation. -## Component to NgModule dependencies - -The dependency of a component on its NgModule is slightly problematic, because its arrow is in the opposite direction of the source dependency (which is from NgModule to the component, via `declarations`). This creates a scenario where, if the NgModule is changed to no longer include the component, the component still needs to be re-emitted because the module has changed. - -This is one of very few cases where `pendingEmit` must be populated with the logical changes from the previous program (those files determined to be changed in step 1 under "Tracking of changes" above), and cannot simply be created from the current dependency graph. - -# What optimizations are possible in the future? - -There is plenty of room for improvement here, with diminishing returns for the work involved. - -## Semantic dependency tracking - -Currently the compiler tracks dependencies only at the file level, and will re-emit dependent files if they _may_ have been affected by a change. Often times a change though does _not_ require updates to dependent files. - -For example, today a component's `NgModule` and all of the other components which consume that module's export scope are considered to depend on the component file itself. If the component's template changes, this triggers a re-emit of not only the component's file, but the entire chain of its NgModule and that module's export scope. This happens even though the template of a component _does not have any impact_ on any components which consume it - these other emits are deeply unnecessary. - -In contrast, if the component's _selector_ changes, then all those dependent files do need to be updated since their `directiveDefs` might have changed. - -Currently the compiler does not distinguish these two cases, and conservatively always re-emits the entire NgModule chain. It would be possible to break the dependency graph down into finer-grained nodes and distinguish between updates that affect the component, vs updates that affect its dependents. This would be a huge win, but is exceedingly complex. - -## Skipping template type-checking - -Under ideal conditions, after an initial template type-checking program is created, it may be possible to reuse it for emit _and_ type-checking in subsequent builds. This would be a pretty advanced optimization but would save creation of a second `ts.Program` on each valid rebuild. +Using this information, ngtsc is able to "forget" about the intermediate failed attempts and begin +each new compilation as if it were a single step from the last successful build. It can then ensure +complete correctness of its reuse optimization, since it has reliable data extracted from the " +previous" successful build. diff --git a/packages/compiler-cli/src/ngtsc/incremental/api.ts b/packages/compiler-cli/src/ngtsc/incremental/api.ts index 10a222e2bd..0f3ca34258 100644 --- a/packages/compiler-cli/src/ngtsc/incremental/api.ts +++ b/packages/compiler-cli/src/ngtsc/incremental/api.ts @@ -50,18 +50,10 @@ export interface DependencyTracker addResourceDependency(from: T, on: AbsoluteFsPath): void; /** - * Record that the file `from` depends on the file `on` as well as `on`'s direct dependencies. + * Record that the given file contains unresolvable dependencies. * - * This operation is reified immediately, so if future dependencies are added to `on` they will - * not automatically be added to `from`. + * In practice, this means that the dependency graph cannot provide insight into the effects of + * future changes on that file. */ - addTransitiveDependency(from: T, on: T): void; - - /** - * Record that the file `from` depends on the resource dependencies of `resourcesOf`. - * - * This operation is reified immediately, so if future resource dependencies are added to - * `resourcesOf` they will not automatically be added to `from`. - */ - addTransitiveResources(from: T, resourcesOf: T): void; + recordDependencyAnalysisFailure(file: T): void; } 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 42fc122f00..0dded69344 100644 --- a/packages/compiler-cli/src/ngtsc/incremental/src/dependency_tracking.ts +++ b/packages/compiler-cli/src/ngtsc/incremental/src/dependency_tracking.ts @@ -35,22 +35,8 @@ export class FileDependencyGraph i this.nodeFor(from).usesResources.add(resource); } - addTransitiveDependency(from: T, on: T): void { - const nodeFrom = this.nodeFor(from); - nodeFrom.dependsOn.add(on.fileName); - - const nodeOn = this.nodeFor(on); - for (const dep of nodeOn.dependsOn) { - nodeFrom.dependsOn.add(dep); - } - } - - addTransitiveResources(from: T, resourcesOf: T): void { - const nodeFrom = this.nodeFor(from); - const nodeOn = this.nodeFor(resourcesOf); - for (const dep of nodeOn.usesResources) { - nodeFrom.usesResources.add(dep); - } + recordDependencyAnalysisFailure(file: T): void { + this.nodeFor(file).failedAnalysis = true; } getResourceDependencies(from: T): AbsoluteFsPath[] { @@ -59,10 +45,6 @@ export class FileDependencyGraph i return node ? [...node.usesResources] : []; } - isStale(sf: T, changedTsPaths: Set, changedResources: Set): boolean { - return isLogicallyChanged(sf, this.nodeFor(sf), changedTsPaths, EMPTY_SET, changedResources); - } - /** * Update the current dependency graph from a previous one, incorporating a set of physical * changes. @@ -97,6 +79,7 @@ export class FileDependencyGraph i this.nodes.set(sf, { dependsOn: new Set(node.dependsOn), usesResources: new Set(node.usesResources), + failedAnalysis: false, }); } } @@ -109,6 +92,7 @@ export class FileDependencyGraph i this.nodes.set(sf, { dependsOn: new Set(), usesResources: new Set(), + failedAnalysis: false, }); } return this.nodes.get(sf)!; @@ -122,6 +106,12 @@ export class FileDependencyGraph i function isLogicallyChanged( sf: T, node: FileNode, changedTsPaths: ReadonlySet, deletedTsPaths: ReadonlySet, changedResources: ReadonlySet): boolean { + // A file is assumed to have logically changed if its dependencies could not be determined + // accurately. + if (node.failedAnalysis) { + return true; + } + // A file is logically changed if it has physically changed itself (including being deleted). if (changedTsPaths.has(sf.fileName) || deletedTsPaths.has(sf.fileName)) { return true; @@ -146,6 +136,5 @@ function isLogicallyChanged( interface FileNode { dependsOn: Set; usesResources: Set; + failedAnalysis: boolean; } - -const EMPTY_SET: ReadonlySet = new Set(); diff --git a/packages/compiler-cli/src/ngtsc/incremental/src/state.ts b/packages/compiler-cli/src/ngtsc/incremental/src/state.ts index dbcb93ef34..cd5f5fa431 100644 --- a/packages/compiler-cli/src/ngtsc/incremental/src/state.ts +++ b/packages/compiler-cli/src/ngtsc/incremental/src/state.ts @@ -9,9 +9,12 @@ import * as ts from 'typescript'; import {absoluteFrom, absoluteFromSourceFile, AbsoluteFsPath} from '../../file_system'; +import {PerfEvent, PerfPhase, PerfRecorder} from '../../perf'; +import {ClassDeclaration} from '../../reflection'; import {ClassRecord, TraitCompiler} from '../../transform'; import {FileTypeCheckingData} from '../../typecheck/src/checker'; import {IncrementalBuild} from '../api'; +import {SemanticDepGraph, SemanticDepGraphUpdater} from '../semantic_graph'; import {FileDependencyGraph} from './dependency_tracking'; @@ -27,8 +30,8 @@ export class IncrementalDriver implements IncrementalBuild, - readonly depGraph: FileDependencyGraph, private logicalChanges: Set|null) { + state: PendingBuildState, readonly depGraph: FileDependencyGraph, + private logicalChanges: Set|null) { this.state = state; } @@ -41,110 +44,122 @@ export class IncrementalDriver implements IncrementalBuild|null): IncrementalDriver { - // Initialize the state of the current build based on the previous one. - let state: PendingBuildState; - if (oldDriver.state.kind === BuildStateKind.Pending) { - // The previous build never made it past the pending state. Reuse it as the starting state for - // this build. - state = oldDriver.state; - } else { - // The previous build was successfully analyzed. `pendingEmit` is the only state carried - // forward into this build. - state = { - kind: BuildStateKind.Pending, - pendingEmit: oldDriver.state.pendingEmit, - changedResourcePaths: new Set(), - changedTsPaths: new Set(), - lastGood: oldDriver.state.lastGood, - }; - } - - // Merge the freshly modified resource files with any prior ones. - if (modifiedResourceFiles !== null) { - for (const resFile of modifiedResourceFiles) { - state.changedResourcePaths.add(absoluteFrom(resFile)); - } - } - - // Next, process the files in the new program, with a couple of goals: - // 1) Determine which TS files have changed, if any, and merge them into `changedTsFiles`. - // 2) Produce a list of TS files which no longer exist in the program (they've been deleted - // since the previous compilation). These need to be removed from the state tracking to avoid - // leaking memory. - - // All files in the old program, for easy detection of changes. - const oldFiles = new Set(oldProgram.getSourceFiles()); - - // Assume all the old files were deleted to begin with. Only TS files are tracked. - const deletedTsPaths = new Set(tsOnlyFiles(oldProgram).map(sf => sf.fileName)); - - for (const newFile of newProgram.getSourceFiles()) { - if (!newFile.isDeclarationFile) { - // This file exists in the new program, so remove it from `deletedTsPaths`. - deletedTsPaths.delete(newFile.fileName); - } - - if (oldFiles.has(newFile)) { - // This file hasn't changed; no need to look at it further. - continue; - } - - // The file has changed since the last successful build. The appropriate reaction depends on - // what kind of file it is. - if (!newFile.isDeclarationFile) { - // It's a .ts file, so track it as a change. - state.changedTsPaths.add(newFile.fileName); + modifiedResourceFiles: Set|null, perf: PerfRecorder): IncrementalDriver { + return perf.inPhase(PerfPhase.Reconciliation, () => { + // Initialize the state of the current build based on the previous one. + let state: PendingBuildState; + if (oldDriver.state.kind === BuildStateKind.Pending) { + // The previous build never made it past the pending state. Reuse it as the starting state + // for this build. + state = oldDriver.state; } else { - // It's a .d.ts file. Currently the compiler does not do a great job of tracking - // dependencies on .d.ts files, so bail out of incremental builds here and do a full build. - // This usually only happens if something in node_modules changes. - return IncrementalDriver.fresh(newProgram); - } - } + let priorGraph: SemanticDepGraph|null = null; + if (oldDriver.state.lastGood !== null) { + priorGraph = oldDriver.state.lastGood.semanticDepGraph; + } - // The next step is to remove any deleted files from the state. - for (const filePath of deletedTsPaths) { - state.pendingEmit.delete(filePath); - - // Even if the file doesn't exist in the current compilation, it still might have been changed - // in a previous one, so delete it from the set of changed TS files, just in case. - state.changedTsPaths.delete(filePath); - } - - // Now, changedTsPaths contains physically changed TS paths. Use the previous program's logical - // dependency graph to determine logically changed files. - const depGraph = new FileDependencyGraph(); - - // If a previous compilation exists, use its dependency graph to determine the set of logically - // changed files. - let logicalChanges: Set|null = null; - if (state.lastGood !== null) { - // Extract the set of logically changed files. At the same time, this operation populates the - // current (fresh) dependency graph with information about those files which have not - // logically changed. - logicalChanges = depGraph.updateWithPhysicalChanges( - state.lastGood.depGraph, state.changedTsPaths, deletedTsPaths, - state.changedResourcePaths); - for (const fileName of state.changedTsPaths) { - logicalChanges.add(fileName); + // The previous build was successfully analyzed. `pendingEmit` is the only state carried + // forward into this build. + state = { + kind: BuildStateKind.Pending, + pendingEmit: oldDriver.state.pendingEmit, + pendingTypeCheckEmit: oldDriver.state.pendingTypeCheckEmit, + changedResourcePaths: new Set(), + changedTsPaths: new Set(), + lastGood: oldDriver.state.lastGood, + semanticDepGraphUpdater: new SemanticDepGraphUpdater(priorGraph), + }; } - // Any logically changed files need to be re-emitted. Most of the time this would happen - // regardless because the new dependency graph would _also_ identify the file as stale. - // However there are edge cases such as removing a component from an NgModule without adding - // it to another one, where the previous graph identifies the file as logically changed, but - // the new graph (which does not have that edge) fails to identify that the file should be - // re-emitted. - for (const change of logicalChanges) { - state.pendingEmit.add(change); + // Merge the freshly modified resource files with any prior ones. + if (modifiedResourceFiles !== null) { + for (const resFile of modifiedResourceFiles) { + state.changedResourcePaths.add(absoluteFrom(resFile)); + } } - } - // `state` now reflects the initial pending state of the current compilation. + // Next, process the files in the new program, with a couple of goals: + // 1) Determine which TS files have changed, if any, and merge them into `changedTsFiles`. + // 2) Produce a list of TS files which no longer exist in the program (they've been deleted + // since the previous compilation). These need to be removed from the state tracking to + // avoid leaking memory. - return new IncrementalDriver( - state, new Set(tsOnlyFiles(newProgram)), depGraph, logicalChanges); + // All files in the old program, for easy detection of changes. + const oldFiles = new Set(oldProgram.getSourceFiles()); + + // Assume all the old files were deleted to begin with. Only TS files are tracked. + const deletedTsPaths = new Set(tsOnlyFiles(oldProgram).map(sf => sf.fileName)); + + for (const newFile of newProgram.getSourceFiles()) { + if (!newFile.isDeclarationFile) { + // This file exists in the new program, so remove it from `deletedTsPaths`. + deletedTsPaths.delete(newFile.fileName); + } + + if (oldFiles.has(newFile)) { + // This file hasn't changed; no need to look at it further. + continue; + } + + // The file has changed since the last successful build. The appropriate reaction depends on + // what kind of file it is. + if (!newFile.isDeclarationFile) { + // It's a .ts file, so track it as a change. + state.changedTsPaths.add(newFile.fileName); + } else { + // It's a .d.ts file. Currently the compiler does not do a great job of tracking + // dependencies on .d.ts files, so bail out of incremental builds here and do a full + // build. This usually only happens if something in node_modules changes. + return IncrementalDriver.fresh(newProgram); + } + } + + // The next step is to remove any deleted files from the state. + for (const filePath of deletedTsPaths) { + state.pendingEmit.delete(filePath); + state.pendingTypeCheckEmit.delete(filePath); + + // Even if the file doesn't exist in the current compilation, it still might have been + // changed in a previous one, so delete it from the set of changed TS files, just in case. + state.changedTsPaths.delete(filePath); + } + + perf.eventCount(PerfEvent.SourceFilePhysicalChange, state.changedTsPaths.size); + + // Now, changedTsPaths contains physically changed TS paths. Use the previous program's + // logical dependency graph to determine logically changed files. + const depGraph = new FileDependencyGraph(); + + // If a previous compilation exists, use its dependency graph to determine the set of + // logically changed files. + let logicalChanges: Set|null = null; + if (state.lastGood !== null) { + // Extract the set of logically changed files. At the same time, this operation populates + // the current (fresh) dependency graph with information about those files which have not + // logically changed. + logicalChanges = depGraph.updateWithPhysicalChanges( + state.lastGood.depGraph, state.changedTsPaths, deletedTsPaths, + state.changedResourcePaths); + perf.eventCount(PerfEvent.SourceFileLogicalChange, logicalChanges.size); + for (const fileName of state.changedTsPaths) { + logicalChanges.add(fileName); + } + + // Any logically changed files need to be re-emitted. Most of the time this would happen + // regardless because the new dependency graph would _also_ identify the file as stale. + // However there are edge cases such as removing a component from an NgModule without adding + // it to another one, where the previous graph identifies the file as logically changed, but + // the new graph (which does not have that edge) fails to identify that the file should be + // re-emitted. + for (const change of logicalChanges) { + state.pendingEmit.add(change); + state.pendingTypeCheckEmit.add(change); + } + } + + // `state` now reflects the initial pending state of the current compilation. + return new IncrementalDriver(state, depGraph, logicalChanges); + }); } static fresh(program: ts.Program): IncrementalDriver { @@ -155,13 +170,21 @@ export class IncrementalDriver implements IncrementalBuild(tsFiles.map(sf => sf.fileName)), + pendingTypeCheckEmit: new Set(tsFiles.map(sf => sf.fileName)), changedResourcePaths: new Set(), changedTsPaths: new Set(), lastGood: null, + semanticDepGraphUpdater: new SemanticDepGraphUpdater(/* priorGraph */ null), }; - return new IncrementalDriver( - state, new Set(tsFiles), new FileDependencyGraph(), /* logicalChanges */ null); + return new IncrementalDriver(state, new FileDependencyGraph(), /* logicalChanges */ null); + } + + getSemanticDepGraphUpdater(): SemanticDepGraphUpdater { + if (this.state.kind !== BuildStateKind.Pending) { + throw new Error('Semantic dependency updater is only available when pending analysis'); + } + return this.state.semanticDepGraphUpdater; } recordSuccessfulAnalysis(traitCompiler: TraitCompiler): void { @@ -170,26 +193,29 @@ export class IncrementalDriver implements IncrementalBuild; + /** + * Similar to `pendingEmit`, but then for representing the set of files for which the type-check + * file should be regenerated. It behaves identically with respect to errored compilations as + * `pendingEmit`. + */ + pendingTypeCheckEmit: Set; + /** * Specific aspects of the last compilation which successfully completed analysis, if any. @@ -296,6 +335,14 @@ interface BaseBuildState { */ depGraph: FileDependencyGraph; + /** + * The semantic dependency graph from the last successfully analyzed build. + * + * This is used to perform in-depth comparison of Angular decorated classes, to determine + * which files have to be re-emitted and/or re-type-checked. + */ + semanticDepGraph: SemanticDepGraph; + /** * The `TraitCompiler` from the last successfully analyzed build. * @@ -333,6 +380,12 @@ interface PendingBuildState extends BaseBuildState { * Set of resource file paths which have changed since the last successfully analyzed build. */ changedResourcePaths: Set; + + /** + * In a pending state, the semantic dependency graph is available to the compilation to register + * the incremental symbols into. + */ + semanticDepGraphUpdater: SemanticDepGraphUpdater; } interface AnalyzedBuildState extends BaseBuildState { diff --git a/packages/compiler-cli/src/ngtsc/incremental/src/strategy.ts b/packages/compiler-cli/src/ngtsc/incremental/src/strategy.ts index b7d60b9856..9eacb262c7 100644 --- a/packages/compiler-cli/src/ngtsc/incremental/src/strategy.ts +++ b/packages/compiler-cli/src/ngtsc/incremental/src/strategy.ts @@ -24,6 +24,12 @@ export interface IncrementalBuildStrategy { * future compilations. */ setIncrementalDriver(driver: IncrementalDriver, program: ts.Program): void; + + /** + * Convert this `IncrementalBuildStrategy` into a possibly new instance to be used in the next + * incremental compilation (may be a no-op if the strategy is not stateful). + */ + toNextBuildStrategy(): IncrementalBuildStrategy; } /** @@ -36,6 +42,10 @@ export class NoopIncrementalBuildStrategy implements IncrementalBuildStrategy { } setIncrementalDriver(): void {} + + toNextBuildStrategy(): IncrementalBuildStrategy { + return this; + } } /** @@ -78,6 +88,10 @@ export class PatchedProgramIncrementalBuildStrategy implements IncrementalBuildS setIncrementalDriver(driver: IncrementalDriver, program: ts.Program): void { (program as any)[SYM_INCREMENTAL_DRIVER] = driver; } + + toNextBuildStrategy(): IncrementalBuildStrategy { + return this; + } } diff --git a/packages/compiler-cli/src/ngtsc/indexer/test/util.ts b/packages/compiler-cli/src/ngtsc/indexer/test/util.ts index 7986cab520..0ec5238cb3 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/test/util.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/test/util.ts @@ -54,6 +54,7 @@ export function getBoundTemplate( inputs: ClassPropertyMapping.fromMappedObject({}), outputs: ClassPropertyMapping.fromMappedObject({}), exportAs: null, + isStructural: false, }); }); const binder = new R3TargetBinder(matcher); diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/api.ts b/packages/compiler-cli/src/ngtsc/metadata/src/api.ts index 3ec001db95..0b48090de9 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/src/api.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/src/api.ts @@ -108,6 +108,17 @@ export interface DirectiveMeta extends T2DirectiveMeta, DirectiveTypeCheckMeta { * another type, it could not statically determine the base class. */ baseClass: Reference|'dynamic'|null; + + /** + * Whether the directive had some issue with its declaration that means it might not have complete + * and reliable metadata. + */ + isPoisoned: boolean; + + /** + * Whether the directive is likely a structural directive (injects `TemplateRef`). + */ + isStructural: boolean; } /** diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/dts.ts b/packages/compiler-cli/src/ngtsc/metadata/src/dts.ts index 4bbdc14fd8..d88bf1a296 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, isNamedClassDeclaration, ReflectionHost} from '../../reflection'; +import {ClassDeclaration, isNamedClassDeclaration, ReflectionHost, TypeValueReferenceKind} from '../../reflection'; import {DirectiveMeta, MetadataReader, NgModuleMeta, PipeMeta} from './api'; import {ClassPropertyMapping} from './property_mapping'; @@ -77,6 +77,19 @@ export class DtsMetadataReader implements MetadataReader { return null; } + const isComponent = def.name === 'ɵcmp'; + + const ctorParams = this.reflector.getConstructorParameters(clazz); + + // A directive is considered to be structural if: + // 1) it's a directive, not a component, and + // 2) it injects `TemplateRef` + const isStructural = !isComponent && ctorParams !== null && ctorParams.some(param => { + return param.typeValueReference.kind === TypeValueReferenceKind.IMPORTED && + param.typeValueReference.moduleName === '@angular/core' && + param.typeValueReference.importedName === 'TemplateRef'; + }); + const inputs = ClassPropertyMapping.fromMappedObject(readStringMapType(def.type.typeArguments[3])); const outputs = @@ -84,7 +97,7 @@ export class DtsMetadataReader implements MetadataReader { return { ref, name: clazz.name.text, - isComponent: def.name === 'ɵcmp', + isComponent, selector: readStringType(def.type.typeArguments[1]), exportAs: readStringArrayType(def.type.typeArguments[2]), inputs, @@ -92,6 +105,8 @@ export class DtsMetadataReader implements MetadataReader { queries: readStringArrayType(def.type.typeArguments[5]), ...extractDirectiveTypeCheckMeta(clazz, inputs, this.reflector), baseClass: readBaseClass(clazz, this.checker, this.reflector), + isPoisoned: false, + isStructural, }; } diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts b/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts index b923de6e2a..097e1ef422 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts @@ -37,6 +37,7 @@ export function flattenInheritedDirectiveMetadata( let isDynamic = false; let inputs = ClassPropertyMapping.empty(); let outputs = ClassPropertyMapping.empty(); + let isStructural: boolean = false; const addMetadata = (meta: DirectiveMeta): void => { if (meta.baseClass === 'dynamic') { @@ -51,6 +52,8 @@ export function flattenInheritedDirectiveMetadata( } } + isStructural = isStructural || meta.isStructural; + inputs = ClassPropertyMapping.merge(inputs, meta.inputs); outputs = ClassPropertyMapping.merge(outputs, meta.outputs); @@ -79,5 +82,6 @@ export function flattenInheritedDirectiveMetadata( restrictedInputFields, stringLiteralInputFields, baseClass: isDynamic ? 'dynamic' : null, + isStructural, }; } diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/util.ts b/packages/compiler-cli/src/ngtsc/metadata/src/util.ts index 8575437127..a27a1b2654 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/src/util.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/src/util.ts @@ -223,6 +223,5 @@ function afterUnderscore(str: string): string { /** Returns whether a class declaration has the necessary class fields to make it injectable. */ export function hasInjectableFields(clazz: ClassDeclaration, host: ReflectionHost): boolean { const members = host.getMembersOfClass(clazz); - return members.some( - ({isStatic, name}) => isStatic && (name === 'ɵprov' || name === 'ɵfac' || name === 'ɵinj')); + return members.some(({isStatic, name}) => isStatic && (name === 'ɵprov' || name === 'ɵfac')); } diff --git a/packages/compiler-cli/src/ngtsc/modulewithproviders/src/scanner.ts b/packages/compiler-cli/src/ngtsc/modulewithproviders/src/scanner.ts index f5635550e3..573dd20a2d 100644 --- a/packages/compiler-cli/src/ngtsc/modulewithproviders/src/scanner.ts +++ b/packages/compiler-cli/src/ngtsc/modulewithproviders/src/scanner.ts @@ -101,7 +101,7 @@ export class ModuleWithProvidersScanner { const ngModuleExpr = this.emitter.emit(ngModule, decl.getSourceFile(), ImportFlags.ForceNewImport); - const ngModuleType = new ExpressionType(ngModuleExpr); + const ngModuleType = new ExpressionType(ngModuleExpr.expression); const mwpNgType = new ExpressionType( new ExternalExpr(Identifiers.ModuleWithProviders), [/* modifiers */], [ngModuleType]); 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 20f1932055..5579e6ccf3 100644 --- a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/interpreter.ts +++ b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/interpreter.ts @@ -220,6 +220,15 @@ export class StaticInterpreter { if (node.originalKeywordKind === ts.SyntaxKind.UndefinedKeyword) { return undefined; } else { + // Check if the symbol here is imported. + if (this.dependencyTracker !== null && this.host.getImportOfIdentifier(node) !== null) { + // It was, but no declaration for the node could be found. This means that the dependency + // graph for the current file cannot be properly updated to account for this (broken) + // import. Instead, the originating file is reported as failing dependency analysis, + // ensuring that future compilations will always attempt to re-resolve the previously + // broken identifier. + this.dependencyTracker.recordDependencyAnalysisFailure(context.originatingFile); + } return DynamicValue.fromUnknownIdentifier(node); } } diff --git a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/known_declaration.ts b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/known_declaration.ts index afc2e4d62b..f17e38648b 100644 --- a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/known_declaration.ts +++ b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/known_declaration.ts @@ -10,7 +10,7 @@ import {KnownDeclaration} from '../../reflection/src/host'; import {ObjectAssignBuiltinFn} from './builtin'; import {ResolvedValue} from './result'; -import {AssignHelperFn, SpreadHelperFn} from './ts_helpers'; +import {AssignHelperFn, ReadHelperFn, SpreadArrayHelperFn, SpreadHelperFn} from './ts_helpers'; /** Resolved value for the JavaScript global `Object` declaration. */ export const jsGlobalObjectValue = new Map([['assign', new ObjectAssignBuiltinFn()]]); @@ -21,6 +21,12 @@ const assignTsHelperFn = new AssignHelperFn(); /** Resolved value for the `__spread()` and `__spreadArrays()` TypeScript helper declarations. */ const spreadTsHelperFn = new SpreadHelperFn(); +/** Resolved value for the `__spreadArray()` TypeScript helper declarations. */ +const spreadArrayTsHelperFn = new SpreadArrayHelperFn(); + +/** Resolved value for the `__read()` TypeScript helper declarations. */ +const readTsHelperFn = new ReadHelperFn(); + /** * Resolves the specified known declaration to a resolved value. For example, * the known JavaScript global `Object` will resolve to a `Map` that provides the @@ -35,6 +41,10 @@ export function resolveKnownDeclaration(decl: KnownDeclaration): ResolvedValue { case KnownDeclaration.TsHelperSpread: case KnownDeclaration.TsHelperSpreadArrays: return spreadTsHelperFn; + case KnownDeclaration.TsHelperSpreadArray: + return spreadArrayTsHelperFn; + case KnownDeclaration.TsHelperRead: + return readTsHelperFn; default: throw new Error(`Cannot resolve known declaration. Received: ${KnownDeclaration[decl]}.`); } diff --git a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/ts_helpers.ts b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/ts_helpers.ts index 2466eee6d2..109ef11292 100644 --- a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/ts_helpers.ts +++ b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/ts_helpers.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {ObjectAssignBuiltinFn} from './builtin'; import {DynamicValue} from './dynamic'; -import {KnownFn, ResolvedValueArray} from './result'; +import {KnownFn, ResolvedValue, ResolvedValueArray} from './result'; // Use the same implementation we use for `Object.assign()`. Semantically these functions are the @@ -35,3 +35,48 @@ export class SpreadHelperFn extends KnownFn { return result; } } + +// Used for `__spreadArray` TypeScript helper function. +export class SpreadArrayHelperFn extends KnownFn { + evaluate(node: ts.Node, args: ResolvedValueArray): ResolvedValue { + if (args.length !== 2) { + return DynamicValue.fromUnknown(node); + } + + const [to, from] = args; + if (to instanceof DynamicValue) { + return DynamicValue.fromDynamicInput(node, to); + } else if (from instanceof DynamicValue) { + return DynamicValue.fromDynamicInput(node, from); + } + + if (!Array.isArray(to)) { + return DynamicValue.fromInvalidExpressionType(node, to); + } else if (!Array.isArray(from)) { + return DynamicValue.fromInvalidExpressionType(node, from); + } + + return to.concat(from); + } +} + +// Used for `__read` TypeScript helper function. +export class ReadHelperFn extends KnownFn { + evaluate(node: ts.Node, args: ResolvedValueArray): ResolvedValue { + if (args.length !== 1) { + // The `__read` helper accepts a second argument `n` but that case is not supported. + return DynamicValue.fromUnknown(node); + } + + const [value] = args; + if (value instanceof DynamicValue) { + return DynamicValue.fromDynamicInput(node, value); + } + + if (!Array.isArray(value)) { + return DynamicValue.fromInvalidExpressionType(node, value); + } + + return value; + } +} 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 0f728bae44..e597f6d0f3 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 @@ -646,6 +646,8 @@ runInEachFileSystem(() => { export declare function __assign(t: any, ...sources: any[]): any; export declare function __spread(...args: any[][]): any[]; export declare function __spreadArrays(...args: any[][]): any[]; + export declare function __spreadArray(to: any[], from: any[]): any[]; + export declare function __read(o: any, n?: number): any[]; `, }, ]); @@ -733,6 +735,52 @@ runInEachFileSystem(() => { expect(arr).toEqual([4, 5, 6]); }); + + it('should evaluate `__spreadArray()` (named import)', () => { + const arr: number[] = evaluateExpression( + ` + import {__spreadArray} from 'tslib'; + const a = [4]; + const b = [5, 6]; + `, + '__spreadArray(a, b)'); + + expect(arr).toEqual([4, 5, 6]); + }); + + it('should evaluate `__spreadArray()` (star import)', () => { + const arr: number[] = evaluateExpression( + ` + import * as tslib from 'tslib'; + const a = [4]; + const b = [5, 6]; + `, + 'tslib.__spreadArray(a, b)'); + + expect(arr).toEqual([4, 5, 6]); + }); + + it('should evaluate `__read()` (named import)', () => { + const arr: number[] = evaluateExpression( + ` + import {__read} from 'tslib'; + const a = [5, 6]; + `, + '__read(a)'); + + expect(arr).toEqual([5, 6]); + }); + + it('should evaluate `__read()` (star import)', () => { + const arr: number[] = evaluateExpression( + ` + import * as tslib from 'tslib'; + const a = [5, 6]; + `, + 'tslib.__read(a)'); + + expect(arr).toEqual([5, 6]); + }); }); describe('(with emitted TypeScript helpers as functions)', () => { @@ -742,6 +790,8 @@ runInEachFileSystem(() => { function __assign(t, ...sources) { /* ... */ } function __spread(...args) { /* ... */ } function __spreadArrays(...args) { /* ... */ } + function __spreadArray(to, from) { /* ... */ } + function __read(o) { /* ... */ } `; const {checker, expression} = makeExpression(helpers + code, expr); @@ -786,6 +836,27 @@ runInEachFileSystem(() => { expect(arr).toEqual([4, 5, 6]); }); + + it('should evaluate `__spreadArray()`', () => { + const arr: number[] = evaluateExpression( + ` + const a = [4]; + const b = [5, 6]; + `, + '__spreadArray(a, b)'); + + expect(arr).toEqual([4, 5, 6]); + }); + + it('should evaluate `__read()`', () => { + const arr: number[] = evaluateExpression( + ` + const a = [5, 6]; + `, + '__read(a)'); + + expect(arr).toEqual([5, 6]); + }); }); describe('(with emitted TypeScript helpers as variables)', () => { @@ -795,6 +866,8 @@ runInEachFileSystem(() => { var __assign = (this && this.__assign) || function (t, ...sources) { /* ... */ } var __spread = (this && this.__spread) || function (...args) { /* ... */ } var __spreadArrays = (this && this.__spreadArrays) || function (...args) { /* ... */ } + var __spreadArray = (this && this.__spreadArray) || function (to, from) { /* ... */ } + var __read = (this && this.__read) || function (o) { /* ... */ } `; const {checker, expression} = makeExpression(helpers + code, expr); @@ -839,6 +912,27 @@ runInEachFileSystem(() => { expect(arr).toEqual([4, 5, 6]); }); + + it('should evaluate `__spreadArray()`', () => { + const arr: number[] = evaluateExpression( + ` + const a = [4]; + const b = [5, 6]; + `, + '__spreadArray(a, b)'); + + expect(arr).toEqual([4, 5, 6]); + }); + + it('should evaluate `__read()`', () => { + const arr: number[] = evaluateExpression( + ` + const a = [5, 6]; + `, + '__read(a)'); + + expect(arr).toEqual([5, 6]); + }); }); describe('(visited file tracking)', () => { @@ -980,6 +1074,10 @@ runInEachFileSystem(() => { return KnownDeclaration.TsHelperSpread; case '__spreadArrays': return KnownDeclaration.TsHelperSpreadArrays; + case '__spreadArray': + return KnownDeclaration.TsHelperSpreadArray; + case '__read': + return KnownDeclaration.TsHelperRead; default: return null; } @@ -989,6 +1087,5 @@ runInEachFileSystem(() => { const fakeDepTracker: DependencyTracker = { addDependency: () => undefined, addResourceDependency: () => undefined, - addTransitiveDependency: () => undefined, - addTransitiveResources: () => undefined, + recordDependencyAnalysisFailure: () => undefined, }; diff --git a/packages/compiler-cli/src/ngtsc/perf/BUILD.bazel b/packages/compiler-cli/src/ngtsc/perf/BUILD.bazel index f51e6b0a56..53af9b1c4f 100644 --- a/packages/compiler-cli/src/ngtsc/perf/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/perf/BUILD.bazel @@ -9,8 +9,6 @@ ts_library( ]), deps = [ "//packages:types", - "//packages/compiler-cli/src/ngtsc/file_system", - "//packages/compiler-cli/src/ngtsc/reflection", "@npm//@types/node", "@npm//typescript", ], diff --git a/packages/compiler-cli/src/ngtsc/perf/index.ts b/packages/compiler-cli/src/ngtsc/perf/index.ts index 9f5b7ce343..32b9762f9f 100644 --- a/packages/compiler-cli/src/ngtsc/perf/index.ts +++ b/packages/compiler-cli/src/ngtsc/perf/index.ts @@ -6,6 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -export {PerfRecorder} from './src/api'; +export * from './src/api'; export {NOOP_PERF_RECORDER} from './src/noop'; -export {PerfTracker} from './src/tracking'; +export {ActivePerfRecorder, DelegatingPerfRecorder} from './src/recorder'; diff --git a/packages/compiler-cli/src/ngtsc/perf/src/api.ts b/packages/compiler-cli/src/ngtsc/perf/src/api.ts index a717808324..39e2a44bc0 100644 --- a/packages/compiler-cli/src/ngtsc/perf/src/api.ts +++ b/packages/compiler-cli/src/ngtsc/perf/src/api.ts @@ -6,12 +6,362 @@ * found in the LICENSE file at https://angular.io/license */ -import {DeclarationNode} from '../../reflection'; +/** + * A phase of compilation for which time is tracked in a distinct bucket. + */ +export enum PerfPhase { + /** + * The "default" phase which tracks time not spent in any other phase. + */ + Unaccounted, -export interface PerfRecorder { - readonly enabled: boolean; + /** + * Time spent setting up the compiler, before a TypeScript program is created. + * + * This includes operations like configuring the `ts.CompilerHost` and any wrappers. + */ + Setup, - mark(name: string, node?: DeclarationNode, category?: string, detail?: string): void; - start(name: string, node?: DeclarationNode, category?: string, detail?: string): number; - stop(span: number): void; + /** + * Time spent in `ts.createProgram`, including reading and parsing `ts.SourceFile`s in the + * `ts.CompilerHost`. + * + * This might be an incremental program creation operation. + */ + TypeScriptProgramCreate, + + /** + * Time spent reconciling the contents of an old `ts.Program` with the new incremental one. + * + * Only present in incremental compilations. + */ + Reconciliation, + + /** + * Time spent updating an `NgCompiler` instance with a resource-only change. + * + * Only present in incremental compilations where the change was resource-only. + */ + ResourceUpdate, + + /** + * Time spent calculating the plain TypeScript diagnostics (structural and semantic). + */ + TypeScriptDiagnostics, + + /** + * Time spent in Angular analysis of individual classes in the program. + */ + Analysis, + + /** + * Time spent in Angular global analysis (synthesis of analysis information into a complete + * understanding of the program). + */ + Resolve, + + /** + * Time spent building the import graph of the program in order to perform cycle detection. + */ + CycleDetection, + + /** + * Time spent generating the text of Type Check Blocks in order to perform template type checking. + */ + TcbGeneration, + + /** + * Time spent updating the `ts.Program` with new Type Check Block code. + */ + TcbUpdateProgram, + + /** + * Time spent by TypeScript performing its emit operations, including downleveling and writing + * output files. + */ + TypeScriptEmit, + + /** + * Time spent by Angular performing code transformations of ASTs as they're about to be emitted. + * + * This includes the actual code generation step for templates, and occurs during the emit phase + * (but is tracked separately from `TypeScriptEmit` time). + */ + Compile, + + /** + * Time spent performing a `TemplateTypeChecker` autocompletion operation. + */ + TtcAutocompletion, + + /** + * Time spent computing template type-checking diagnostics. + */ + TtcDiagnostics, + + /** + * Time spent getting a `Symbol` from the `TemplateTypeChecker`. + */ + TtcSymbol, + + /** + * Time spent by the Angular Language Service calculating a "get references" or a renaming + * operation. + */ + LsReferencesAndRenames, + + /** + * Time spent by the Angular Language Service calculating a "quick info" operation. + */ + LsQuickInfo, + + /** + * Time spent by the Angular Language Service calculating a "get type definition" or "get + * definition" operation. + */ + LsDefinition, + + /** + * Time spent by the Angular Language Service calculating a "get completions" (AKA autocomplete) + * operation. + */ + LsCompletions, + + /** + * Time spent by the Angular Language Service calculating a "view template typecheck block" + * operation. + */ + LsTcb, + + /** + * Time spent by the Angular Language Service calculating diagnostics. + */ + LsDiagnostics, + + /** + * Time spent by the Angular Language Service calculating a "get component locations for template" + * operation. + */ + LsComponentLocations, + + /** + * Tracks the number of `PerfPhase`s, and must appear at the end of the list. + */ + LAST, +} + +/** + * Represents some occurrence during compilation, and is tracked with a counter. + */ +export enum PerfEvent { + /** + * Counts the number of `.d.ts` files in the program. + */ + InputDtsFile, + + /** + * Counts the number of non-`.d.ts` files in the program. + */ + InputTsFile, + + /** + * An `@Component` class was analyzed. + */ + AnalyzeComponent, + + /** + * An `@Directive` class was analyzed. + */ + AnalyzeDirective, + + /** + * An `@Injectable` class was analyzed. + */ + AnalyzeInjectable, + + /** + * An `@NgModule` class was analyzed. + */ + AnalyzeNgModule, + + /** + * An `@Pipe` class was analyzed. + */ + AnalyzePipe, + + /** + * A trait was analyzed. + * + * In theory, this should be the sum of the `Analyze` counters for each decorator type. + */ + TraitAnalyze, + + /** + * A trait had a prior analysis available from an incremental program, and did not need to be + * re-analyzed. + */ + TraitReuseAnalysis, + + /** + * A `ts.SourceFile` directly changed between the prior program and a new incremental compilation. + */ + SourceFilePhysicalChange, + + /** + * A `ts.SourceFile` did not physically changed, but according to the file dependency graph, has + * logically changed between the prior program and a new incremental compilation. + */ + SourceFileLogicalChange, + + /** + * A `ts.SourceFile` has not logically changed and all of its analysis results were thus available + * for reuse. + */ + SourceFileReuseAnalysis, + + /** + * A Type Check Block (TCB) was generated. + */ + GenerateTcb, + + /** + * A Type Check Block (TCB) could not be generated because inlining was disabled, and the block + * would've required inlining. + */ + SkipGenerateTcbNoInline, + + /** + * A `.ngtypecheck.ts` file could be reused from the previous program and did not need to be + * regenerated. + */ + ReuseTypeCheckFile, + + /** + * The template type-checking program required changes and had to be updated in an incremental + * step. + */ + UpdateTypeCheckProgram, + + /** + * The compiler was able to prove that a `ts.SourceFile` did not need to be re-emitted. + */ + EmitSkipSourceFile, + + /** + * A `ts.SourceFile` was emitted. + */ + EmitSourceFile, + + /** + * Tracks the number of `PrefEvent`s, and must appear at the end of the list. + */ + LAST, +} + +/** + * Represents a checkpoint during compilation at which the memory usage of the compiler should be + * recorded. + */ +export enum PerfCheckpoint { + /** + * The point at which the `PerfRecorder` was created, and ideally tracks memory used before any + * compilation structures are created. + */ + Initial, + + /** + * The point just after the `ts.Program` has been created. + */ + TypeScriptProgramCreate, + + /** + * The point just before Angular analysis starts. + * + * In the main usage pattern for the compiler, TypeScript diagnostics have been calculated at this + * point, so the `ts.TypeChecker` has fully ingested the current program, all `ts.Type` structures + * and `ts.Symbol`s have been created. + */ + PreAnalysis, + + /** + * The point just after Angular analysis completes. + */ + Analysis, + + /** + * The point just after Angular resolution is complete. + */ + Resolve, + + /** + * The point just after Type Check Blocks (TCBs) have been generated. + */ + TtcGeneration, + + /** + * The point just after the template type-checking program has been updated with any new TCBs. + */ + TtcUpdateProgram, + + /** + * The point just before emit begins. + * + * In the main usage pattern for the compiler, all template type-checking diagnostics have been + * requested at this point. + */ + PreEmit, + + /** + * The point just after the program has been fully emitted. + */ + Emit, + + /** + * Tracks the number of `PerfCheckpoint`s, and must appear at the end of the list. + */ + LAST, +} + +/** + * Records timing, memory, or counts at specific points in the compiler's operation. + */ +export interface PerfRecorder { + /** + * Set the current phase of compilation. + * + * Time spent in the previous phase will be accounted to that phase. The caller is responsible for + * exiting the phase when work that should be tracked within it is completed, and either returning + * to the previous phase or transitioning to the next one directly. + * + * In general, prefer using `inPhase()` to instrument a section of code, as it automatically + * handles entering and exiting the phase. `phase()` should only be used when the former API + * cannot be cleanly applied to a particular operation. + * + * @returns the previous phase + */ + phase(phase: PerfPhase): PerfPhase; + + /** + * Run `fn` in the given `PerfPhase` and return the result. + * + * Enters `phase` before executing the given `fn`, then exits the phase and returns the result. + * Prefer this API to `phase()` where possible. + */ + inPhase(phase: PerfPhase, fn: () => T): T; + + /** + * Record the memory usage of the compiler at the given checkpoint. + */ + memory(after: PerfCheckpoint): void; + + /** + * Record that a specific event has occurred, possibly more than once. + */ + eventCount(event: PerfEvent, incrementBy?: number): void; + + /** + * Return the `PerfRecorder` to an empty state (clear all tracked statistics) and reset the zero + * point to the current time. + */ + reset(): void; } diff --git a/packages/compiler-cli/src/ngtsc/perf/src/noop.ts b/packages/compiler-cli/src/ngtsc/perf/src/noop.ts index 73f4384dde..8d5c8e0a41 100644 --- a/packages/compiler-cli/src/ngtsc/perf/src/noop.ts +++ b/packages/compiler-cli/src/ngtsc/perf/src/noop.ts @@ -5,15 +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 {DeclarationNode} from '../../reflection'; +import {PerfPhase, PerfRecorder} from './api'; -import {PerfRecorder} from './api'; +class NoopPerfRecorder implements PerfRecorder { + eventCount(): void {} -export const NOOP_PERF_RECORDER: PerfRecorder = { - enabled: false, - mark: (name: string, node: DeclarationNode, category?: string, detail?: string): void => {}, - start: (name: string, node: DeclarationNode, category?: string, detail?: string): number => { - return 0; - }, - stop: (span: number|false): void => {}, -}; + memory(): void {} + + phase(): PerfPhase { + return PerfPhase.Unaccounted; + } + + inPhase(phase: PerfPhase, fn: () => T): T { + return fn(); + } + + reset(): void {} +} + + +export const NOOP_PERF_RECORDER: PerfRecorder = new NoopPerfRecorder(); diff --git a/packages/compiler-cli/src/ngtsc/perf/src/tracking.ts b/packages/compiler-cli/src/ngtsc/perf/src/tracking.ts deleted file mode 100644 index 732c8f0367..0000000000 --- a/packages/compiler-cli/src/ngtsc/perf/src/tracking.ts +++ /dev/null @@ -1,110 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -/// -import * as fs from 'fs'; -import * as ts from 'typescript'; -import {resolve} from '../../file_system'; -import {DeclarationNode} from '../../reflection'; -import {PerfRecorder} from './api'; -import {HrTime, mark, timeSinceInMicros} from './clock'; - -export class PerfTracker implements PerfRecorder { - private nextSpanId = 1; - private log: PerfLogEvent[] = []; - - readonly enabled = true; - - private constructor(private zeroTime: HrTime) {} - - static zeroedToNow(): PerfTracker { - return new PerfTracker(mark()); - } - - mark(name: string, node?: DeclarationNode, category?: string, detail?: string): void { - const msg = this.makeLogMessage(PerfLogEventType.MARK, name, node, category, detail, undefined); - this.log.push(msg); - } - - start(name: string, node?: DeclarationNode, category?: string, detail?: string): number { - const span = this.nextSpanId++; - const msg = this.makeLogMessage(PerfLogEventType.SPAN_OPEN, name, node, category, detail, span); - this.log.push(msg); - return span; - } - - stop(span: number): void { - this.log.push({ - type: PerfLogEventType.SPAN_CLOSE, - span, - stamp: timeSinceInMicros(this.zeroTime), - }); - } - - private makeLogMessage( - type: PerfLogEventType, name: string, node: DeclarationNode|undefined, - category: string|undefined, detail: string|undefined, span: number|undefined): PerfLogEvent { - const msg: PerfLogEvent = { - type, - name, - stamp: timeSinceInMicros(this.zeroTime), - }; - if (category !== undefined) { - msg.category = category; - } - if (detail !== undefined) { - msg.detail = detail; - } - if (span !== undefined) { - msg.span = span; - } - if (node !== undefined) { - msg.file = node.getSourceFile().fileName; - if (!ts.isSourceFile(node)) { - const name = ts.getNameOfDeclaration(node); - if (name !== undefined && ts.isIdentifier(name)) { - msg.declaration = name.text; - } - } - } - return msg; - } - - asJson(): unknown { - return this.log; - } - - serializeToFile(target: string, host: ts.CompilerHost): void { - const json = JSON.stringify(this.log, null, 2); - - if (target.startsWith('ts:')) { - target = target.substr('ts:'.length); - const outFile = resolve(host.getCurrentDirectory(), target); - host.writeFile(outFile, json, false); - } else { - const outFile = resolve(host.getCurrentDirectory(), target); - fs.writeFileSync(outFile, json); - } - } -} - -export interface PerfLogEvent { - name?: string; - span?: number; - file?: string; - declaration?: string; - type: PerfLogEventType; - category?: string; - detail?: string; - stamp: number; -} - -export enum PerfLogEventType { - SPAN_OPEN, - SPAN_CLOSE, - MARK, -} diff --git a/packages/compiler-cli/src/ngtsc/program.ts b/packages/compiler-cli/src/ngtsc/program.ts index 13e209fc0a..1eb8ae18e7 100644 --- a/packages/compiler-cli/src/ngtsc/program.ts +++ b/packages/compiler-cli/src/ngtsc/program.ts @@ -12,14 +12,16 @@ import * as ts from 'typescript'; import * as api from '../transformers/api'; import {verifySupportedTypeScriptVersion} from '../typescript_support'; -import {NgCompiler, NgCompilerHost} from './core'; +import {CompilationTicket, freshCompilationTicket, incrementalFromCompilerTicket, NgCompiler, NgCompilerHost} from './core'; import {NgCompilerOptions} from './core/api'; +import {absoluteFrom, AbsoluteFsPath, getFileSystem} from './file_system'; import {TrackedIncrementalBuildStrategy} from './incremental'; import {IndexedComponent} from './indexer'; -import {NOOP_PERF_RECORDER, PerfRecorder, PerfTracker} from './perf'; +import {ActivePerfRecorder, PerfCheckpoint as PerfCheckpoint, PerfEvent, PerfPhase} from './perf'; import {DeclarationNode} from './reflection'; import {retagAllTsFiles, untagAllTsFiles} from './shims'; import {ReusedProgramStrategy} from './typecheck'; +import {OptimizeFor} from './typecheck/api'; @@ -52,22 +54,20 @@ export class NgtscProgram implements api.Program { private reuseTsProgram: ts.Program; private closureCompilerEnabled: boolean; private host: NgCompilerHost; - private perfRecorder: PerfRecorder = NOOP_PERF_RECORDER; - private perfTracker: PerfTracker|null = null; private incrementalStrategy: TrackedIncrementalBuildStrategy; constructor( rootNames: ReadonlyArray, private options: NgCompilerOptions, delegateHost: api.CompilerHost, oldProgram?: NgtscProgram) { + const perfRecorder = ActivePerfRecorder.zeroedToNow(); + + perfRecorder.phase(PerfPhase.Setup); + // First, check whether the current TS version is supported. if (!options.disableTypeScriptVersionCheck) { verifySupportedTypeScriptVersion(); } - if (options.tracePerformance !== undefined) { - this.perfTracker = PerfTracker.zeroedToNow(); - this.perfRecorder = this.perfTracker; - } this.closureCompilerEnabled = !!options.annotateForClosureCompiler; const reuseProgram = oldProgram?.reuseTsProgram; @@ -81,9 +81,14 @@ export class NgtscProgram implements api.Program { retagAllTsFiles(reuseProgram); } - this.tsProgram = ts.createProgram(this.host.inputFiles, options, this.host, reuseProgram); + this.tsProgram = perfRecorder.inPhase( + PerfPhase.TypeScriptProgramCreate, + () => ts.createProgram(this.host.inputFiles, options, this.host, reuseProgram)); this.reuseTsProgram = this.tsProgram; + perfRecorder.phase(PerfPhase.Unaccounted); + perfRecorder.memory(PerfCheckpoint.TypeScriptProgramCreate); + this.host.postProgramCreationCleanup(); // Shim tagging has served its purpose, and tags can now be removed from all `ts.SourceFile`s in @@ -96,11 +101,35 @@ export class NgtscProgram implements api.Program { this.incrementalStrategy = oldProgram !== undefined ? oldProgram.incrementalStrategy.toNextBuildStrategy() : new TrackedIncrementalBuildStrategy(); + const modifiedResourceFiles = new Set(); + if (this.host.getModifiedResourceFiles !== undefined) { + const strings = this.host.getModifiedResourceFiles(); + if (strings !== undefined) { + for (const fileString of strings) { + modifiedResourceFiles.add(absoluteFrom(fileString)); + } + } + } + + let ticket: CompilationTicket; + if (oldProgram === undefined) { + ticket = freshCompilationTicket( + this.tsProgram, options, this.incrementalStrategy, reusedProgramStrategy, perfRecorder, + /* enableTemplateTypeChecker */ false, /* usePoisonedData */ false); + } else { + ticket = incrementalFromCompilerTicket( + oldProgram.compiler, + this.tsProgram, + this.incrementalStrategy, + reusedProgramStrategy, + modifiedResourceFiles, + perfRecorder, + ); + } + // Create the NgCompiler which will drive the rest of the compilation. - this.compiler = new NgCompiler( - this.host, options, this.tsProgram, reusedProgramStrategy, this.incrementalStrategy, - /** enableTemplateTypeChecker */ false, reuseProgram, this.perfRecorder); + this.compiler = NgCompiler.fromTicket(ticket, this.host); } getTsProgram(): ts.Program { @@ -113,49 +142,59 @@ export class NgtscProgram implements api.Program { getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken| undefined): readonly ts.Diagnostic[] { - return this.tsProgram.getOptionsDiagnostics(cancellationToken); + return this.compiler.perfRecorder.inPhase( + PerfPhase.TypeScriptDiagnostics, + () => this.tsProgram.getOptionsDiagnostics(cancellationToken)); } getTsSyntacticDiagnostics( sourceFile?: ts.SourceFile|undefined, cancellationToken?: ts.CancellationToken|undefined): readonly ts.Diagnostic[] { - const ignoredFiles = this.compiler.ignoreForDiagnostics; - if (sourceFile !== undefined) { - if (ignoredFiles.has(sourceFile)) { - return []; - } - - return this.tsProgram.getSyntacticDiagnostics(sourceFile, cancellationToken); - } else { - const diagnostics: ts.Diagnostic[] = []; - for (const sf of this.tsProgram.getSourceFiles()) { - if (!ignoredFiles.has(sf)) { - diagnostics.push(...this.tsProgram.getSyntacticDiagnostics(sf, cancellationToken)); + return this.compiler.perfRecorder.inPhase(PerfPhase.TypeScriptDiagnostics, () => { + const ignoredFiles = this.compiler.ignoreForDiagnostics; + let res: readonly ts.Diagnostic[]; + if (sourceFile !== undefined) { + if (ignoredFiles.has(sourceFile)) { + return []; } + + res = this.tsProgram.getSyntacticDiagnostics(sourceFile, cancellationToken); + } else { + const diagnostics: ts.Diagnostic[] = []; + for (const sf of this.tsProgram.getSourceFiles()) { + if (!ignoredFiles.has(sf)) { + diagnostics.push(...this.tsProgram.getSyntacticDiagnostics(sf, cancellationToken)); + } + } + res = diagnostics; } - return diagnostics; - } + return res; + }); } getTsSemanticDiagnostics( sourceFile?: ts.SourceFile|undefined, cancellationToken?: ts.CancellationToken|undefined): readonly ts.Diagnostic[] { - const ignoredFiles = this.compiler.ignoreForDiagnostics; - if (sourceFile !== undefined) { - if (ignoredFiles.has(sourceFile)) { - return []; - } - - return this.tsProgram.getSemanticDiagnostics(sourceFile, cancellationToken); - } else { - const diagnostics: ts.Diagnostic[] = []; - for (const sf of this.tsProgram.getSourceFiles()) { - if (!ignoredFiles.has(sf)) { - diagnostics.push(...this.tsProgram.getSemanticDiagnostics(sf, cancellationToken)); + return this.compiler.perfRecorder.inPhase(PerfPhase.TypeScriptDiagnostics, () => { + const ignoredFiles = this.compiler.ignoreForDiagnostics; + let res: readonly ts.Diagnostic[]; + if (sourceFile !== undefined) { + if (ignoredFiles.has(sourceFile)) { + return []; } + + res = this.tsProgram.getSemanticDiagnostics(sourceFile, cancellationToken); + } else { + const diagnostics: ts.Diagnostic[] = []; + for (const sf of this.tsProgram.getSourceFiles()) { + if (!ignoredFiles.has(sf)) { + diagnostics.push(...this.tsProgram.getSemanticDiagnostics(sf, cancellationToken)); + } + } + res = diagnostics; } - return diagnostics; - } + return res; + }); } getNgOptionDiagnostics(cancellationToken?: ts.CancellationToken| @@ -181,7 +220,9 @@ export class NgtscProgram implements api.Program { } } - const diagnostics = this.compiler.getDiagnostics(sf); + const diagnostics = sf === undefined ? + this.compiler.getDiagnostics() : + this.compiler.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram); this.reuseTsProgram = this.compiler.getNextProgram(); return diagnostics; } @@ -208,73 +249,82 @@ export class NgtscProgram implements api.Program { emitCallback?: api.TsEmitCallback | undefined; mergeEmitResultsCallback?: api.TsMergeEmitResultsCallback | undefined; }|undefined): ts.EmitResult { - const {transformers} = this.compiler.prepareEmit(); - const ignoreFiles = this.compiler.ignoreForEmit; - const emitCallback = opts && opts.emitCallback || defaultEmitCallback; + this.compiler.perfRecorder.memory(PerfCheckpoint.PreEmit); - const writeFile: ts.WriteFileCallback = - (fileName: string, data: string, writeByteOrderMark: boolean, - onError: ((message: string) => void)|undefined, - sourceFiles: ReadonlyArray|undefined) => { - if (sourceFiles !== undefined) { - // Record successful writes for any `ts.SourceFile` (that's not a declaration file) - // that's an input to this write. - for (const writtenSf of sourceFiles) { - if (writtenSf.isDeclarationFile) { - continue; + const res = this.compiler.perfRecorder.inPhase(PerfPhase.TypeScriptEmit, () => { + const {transformers} = this.compiler.prepareEmit(); + const ignoreFiles = this.compiler.ignoreForEmit; + const emitCallback = opts && opts.emitCallback || defaultEmitCallback; + + const writeFile: ts.WriteFileCallback = + (fileName: string, data: string, writeByteOrderMark: boolean, + onError: ((message: string) => void)|undefined, + sourceFiles: ReadonlyArray|undefined) => { + if (sourceFiles !== undefined) { + // Record successful writes for any `ts.SourceFile` (that's not a declaration file) + // that's an input to this write. + for (const writtenSf of sourceFiles) { + if (writtenSf.isDeclarationFile) { + continue; + } + + this.compiler.incrementalDriver.recordSuccessfulEmit(writtenSf); } - - this.compiler.incrementalDriver.recordSuccessfulEmit(writtenSf); } - } - this.host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles); - }; + this.host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles); + }; - const customTransforms = opts && opts.customTransformers; - const beforeTransforms = transformers.before || []; - const afterDeclarationsTransforms = transformers.afterDeclarations; + const customTransforms = opts && opts.customTransformers; + const beforeTransforms = transformers.before || []; + const afterDeclarationsTransforms = transformers.afterDeclarations; - if (customTransforms !== undefined && customTransforms.beforeTs !== undefined) { - beforeTransforms.push(...customTransforms.beforeTs); - } - - const emitSpan = this.perfRecorder.start('emit'); - const emitResults: ts.EmitResult[] = []; - - for (const targetSourceFile of this.tsProgram.getSourceFiles()) { - if (targetSourceFile.isDeclarationFile || ignoreFiles.has(targetSourceFile)) { - continue; + if (customTransforms !== undefined && customTransforms.beforeTs !== undefined) { + beforeTransforms.push(...customTransforms.beforeTs); } - if (this.compiler.incrementalDriver.safeToSkipEmit(targetSourceFile)) { - continue; + const emitResults: ts.EmitResult[] = []; + + for (const targetSourceFile of this.tsProgram.getSourceFiles()) { + if (targetSourceFile.isDeclarationFile || ignoreFiles.has(targetSourceFile)) { + continue; + } + + if (this.compiler.incrementalDriver.safeToSkipEmit(targetSourceFile)) { + this.compiler.perfRecorder.eventCount(PerfEvent.EmitSkipSourceFile); + continue; + } + + this.compiler.perfRecorder.eventCount(PerfEvent.EmitSourceFile); + + emitResults.push(emitCallback({ + targetSourceFile, + program: this.tsProgram, + host: this.host, + options: this.options, + emitOnlyDtsFiles: false, + writeFile, + customTransformers: { + before: beforeTransforms, + after: customTransforms && customTransforms.afterTs, + afterDeclarations: afterDeclarationsTransforms, + } as any, + })); } - const fileEmitSpan = this.perfRecorder.start('emitFile', targetSourceFile); - emitResults.push(emitCallback({ - targetSourceFile, - program: this.tsProgram, - host: this.host, - options: this.options, - emitOnlyDtsFiles: false, - writeFile, - customTransformers: { - before: beforeTransforms, - after: customTransforms && customTransforms.afterTs, - afterDeclarations: afterDeclarationsTransforms, - } as any, - })); - this.perfRecorder.stop(fileEmitSpan); + this.compiler.perfRecorder.memory(PerfCheckpoint.Emit); + + // Run the emit, including a custom transformer that will downlevel the Ivy decorators in + // code. + return ((opts && opts.mergeEmitResultsCallback) || mergeEmitResults)(emitResults); + }); + + // Record performance analysis information to disk if we've been asked to do so. + if (this.options.tracePerformance !== undefined) { + const perf = this.compiler.perfRecorder.finalize(); + getFileSystem().writeFile( + getFileSystem().resolve(this.options.tracePerformance), JSON.stringify(perf, null, 2)); } - - this.perfRecorder.stop(emitSpan); - - if (this.perfTracker !== null && this.options.tracePerformance !== undefined) { - this.perfTracker.serializeToFile(this.options.tracePerformance, this.host); - } - - // Run the emit, including a custom transformer that will downlevel the Ivy decorators in code. - return ((opts && opts.mergeEmitResultsCallback) || mergeEmitResults)(emitResults); + return res; } getIndexedComponents(): Map { diff --git a/packages/compiler-cli/src/ngtsc/reflection/src/host.ts b/packages/compiler-cli/src/ngtsc/reflection/src/host.ts index cbf28be5b8..7b6adeadbb 100644 --- a/packages/compiler-cli/src/ngtsc/reflection/src/host.ts +++ b/packages/compiler-cli/src/ngtsc/reflection/src/host.ts @@ -474,6 +474,16 @@ export enum KnownDeclaration { * Indicates the `__spreadArrays` TypeScript helper function. */ TsHelperSpreadArrays, + + /** + * Indicates the `__spreadArray` TypeScript helper function. + */ + TsHelperSpreadArray, + + /** + * Indicates the `__read` TypeScript helper function. + */ + TsHelperRead, } /** diff --git a/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts b/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts index 263a31d534..451d0c9ae3 100644 --- a/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts +++ b/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts @@ -36,12 +36,14 @@ export class TypeScriptReflectionHost implements ReflectionHost { getConstructorParameters(clazz: ClassDeclaration): CtorParameter[]|null { const tsClazz = castDeclarationToClassOrDie(clazz); - // First, find the constructor with a `body`. The constructors without a `body` are overloads - // whereas we want the implementation since it's the one that'll be executed and which can - // have decorators. + const isDeclaration = tsClazz.getSourceFile().isDeclarationFile; + // For non-declaration files, we want to find the constructor with a `body`. The constructors + // without a `body` are overloads whereas we want the implementation since it's the one that'll + // be executed and which can have decorators. For declaration files, we take the first one that + // we get. const ctor = tsClazz.members.find( (member): member is ts.ConstructorDeclaration => - ts.isConstructorDeclaration(member) && member.body !== undefined); + ts.isConstructorDeclaration(member) && (isDeclaration || member.body !== undefined)); if (ctor === undefined) { return null; } diff --git a/packages/compiler-cli/src/ngtsc/resource/src/loader.ts b/packages/compiler-cli/src/ngtsc/resource/src/loader.ts index 62e89ef784..9db512d65b 100644 --- a/packages/compiler-cli/src/ngtsc/resource/src/loader.ts +++ b/packages/compiler-cli/src/ngtsc/resource/src/loader.ts @@ -8,8 +8,8 @@ import * as ts from 'typescript'; -import {ResourceLoader} from '../../annotations'; -import {NgCompilerAdapter} from '../../core/api'; +import {ResourceLoader, ResourceLoaderContext} from '../../annotations'; +import {NgCompilerAdapter, ResourceHostContext} from '../../core/api'; import {AbsoluteFsPath, join, PathSegment} from '../../file_system'; import {RequiredDelegations} from '../../util/src/typescript'; @@ -27,6 +27,7 @@ export class AdapterResourceLoader implements ResourceLoader { private lookupResolutionHost = createLookupResolutionHost(this.adapter); canPreload = !!this.adapter.readResource; + canPreprocess = !!this.adapter.transformResource; constructor(private adapter: NgCompilerAdapter, private options: ts.CompilerOptions) {} @@ -62,11 +63,12 @@ export class AdapterResourceLoader implements ResourceLoader { * `load()` method. * * @param resolvedUrl The url (resolved by a call to `resolve()`) of the resource to preload. + * @param context Information about the resource such as the type and containing file. * @returns A Promise that is resolved once the resource has been loaded or `undefined` if the * file has already been loaded. * @throws An Error if pre-loading is not available. */ - preload(resolvedUrl: string): Promise|undefined { + preload(resolvedUrl: string, context: ResourceLoaderContext): Promise|undefined { if (!this.adapter.readResource) { throw new Error( 'HostResourceLoader: the CompilerHost provided does not support pre-loading resources.'); @@ -77,7 +79,20 @@ export class AdapterResourceLoader implements ResourceLoader { return this.fetching.get(resolvedUrl); } - const result = this.adapter.readResource(resolvedUrl); + let result = this.adapter.readResource(resolvedUrl); + + if (this.adapter.transformResource && context.type === 'style') { + const resourceContext: ResourceHostContext = { + type: 'style', + containingFile: context.containingFile, + resourceFile: resolvedUrl, + }; + result = Promise.resolve(result).then(async (str) => { + const transformResult = await this.adapter.transformResource!(str, resourceContext); + return transformResult === null ? str : transformResult.content; + }); + } + if (typeof result === 'string') { this.cache.set(resolvedUrl, result); return undefined; @@ -91,6 +106,28 @@ export class AdapterResourceLoader implements ResourceLoader { } } + /** + * Preprocess the content data of an inline resource, asynchronously. + * + * @param data The existing content data from the inline resource. + * @param context Information regarding the resource such as the type and containing file. + * @returns A Promise that resolves to the processed data. If no processing occurs, the + * same data string that was passed to the function will be resolved. + */ + async preprocessInline(data: string, context: ResourceLoaderContext): Promise { + if (!this.adapter.transformResource || context.type !== 'style') { + return data; + } + + const transformResult = await this.adapter.transformResource( + data, {type: 'style', containingFile: context.containingFile, resourceFile: null}); + if (transformResult === null) { + return data; + } + + return transformResult.content; + } + /** * Load the resource at the given url, synchronously. * @@ -113,6 +150,13 @@ export class AdapterResourceLoader implements ResourceLoader { return result; } + /** + * Invalidate the entire resource cache. + */ + invalidate(): void { + this.cache.clear(); + } + /** * Attempt to resolve `url` in the context of `fromFile`, while respecting the rootDirs * option from the tsconfig. First, normalize the file name. diff --git a/packages/compiler-cli/src/ngtsc/scope/index.ts b/packages/compiler-cli/src/ngtsc/scope/index.ts index 814ae3a8f0..920f8dba8a 100644 --- a/packages/compiler-cli/src/ngtsc/scope/index.ts +++ b/packages/compiler-cli/src/ngtsc/scope/index.ts @@ -10,3 +10,4 @@ export {ExportScope, ScopeData} from './src/api'; export {ComponentScopeReader, CompoundComponentScopeReader} from './src/component_scope'; export {DtsModuleScopeResolver, MetadataDtsModuleScopeResolver} from './src/dependency'; export {DeclarationData, LocalModuleScope, LocalModuleScopeRegistry, LocalNgModuleData} from './src/local'; +export {TypeCheckScope, TypeCheckScopeRegistry} from './src/typecheck'; \ No newline at end of file diff --git a/packages/compiler-cli/src/ngtsc/scope/src/api.ts b/packages/compiler-cli/src/ngtsc/scope/src/api.ts index e293acda1c..70ba24bc13 100644 --- a/packages/compiler-cli/src/ngtsc/scope/src/api.ts +++ b/packages/compiler-cli/src/ngtsc/scope/src/api.ts @@ -29,6 +29,12 @@ export interface ScopeData { * NgModules which contributed to the scope of the module. */ ngModules: ClassDeclaration[]; + + /** + * Whether some module or component in this scope contains errors and is thus semantically + * unreliable. + */ + isPoisoned: boolean; } /** diff --git a/packages/compiler-cli/src/ngtsc/scope/src/component_scope.ts b/packages/compiler-cli/src/ngtsc/scope/src/component_scope.ts index 0f4d63913f..d1f06409d7 100644 --- a/packages/compiler-cli/src/ngtsc/scope/src/component_scope.ts +++ b/packages/compiler-cli/src/ngtsc/scope/src/component_scope.ts @@ -13,7 +13,7 @@ import {LocalModuleScope} from './local'; * Read information about the compilation scope of components. */ export interface ComponentScopeReader { - getScopeForComponent(clazz: ClassDeclaration): LocalModuleScope|null|'error'; + getScopeForComponent(clazz: ClassDeclaration): LocalModuleScope|null; /** * Get the `RemoteScope` required for this component, if any. @@ -34,7 +34,7 @@ export interface ComponentScopeReader { export class CompoundComponentScopeReader implements ComponentScopeReader { constructor(private readers: ComponentScopeReader[]) {} - getScopeForComponent(clazz: ClassDeclaration): LocalModuleScope|null|'error' { + getScopeForComponent(clazz: ClassDeclaration): LocalModuleScope|null { for (const reader of this.readers) { const meta = reader.getScopeForComponent(clazz); if (meta !== null) { diff --git a/packages/compiler-cli/src/ngtsc/scope/src/dependency.ts b/packages/compiler-cli/src/ngtsc/scope/src/dependency.ts index d6c073dc05..e278c6e3d7 100644 --- a/packages/compiler-cli/src/ngtsc/scope/src/dependency.ts +++ b/packages/compiler-cli/src/ngtsc/scope/src/dependency.ts @@ -124,7 +124,6 @@ export class MetadataDtsModuleScopeResolver implements DtsModuleScopeResolver { // The export was not a directive, a pipe, or a module. This is an error. // TODO(alxhub): produce a ts.Diagnostic - throw new Error(`Exported value ${exportRef.debugName} was not a directive, pipe, or module`); } const exportScope: ExportScope = { @@ -132,6 +131,7 @@ export class MetadataDtsModuleScopeResolver implements DtsModuleScopeResolver { directives, pipes, ngModules: Array.from(ngModules), + isPoisoned: false, }, }; this.cache.set(clazz, exportScope); diff --git a/packages/compiler-cli/src/ngtsc/scope/src/local.ts b/packages/compiler-cli/src/ngtsc/scope/src/local.ts index 30c3a1ce26..5e509324d1 100644 --- a/packages/compiler-cli/src/ngtsc/scope/src/local.ts +++ b/packages/compiler-cli/src/ngtsc/scope/src/local.ts @@ -32,16 +32,6 @@ export interface LocalModuleScope extends ExportScope { schemas: SchemaMetadata[]; } -/** - * Information about the compilation scope of a registered declaration. - */ -export interface CompilationScope extends ScopeData { - /** The declaration whose compilation scope is described here. */ - declaration: ClassDeclaration; - /** The declaration of the NgModule that declares this `declaration`. */ - ngModule: ClassDeclaration; -} - /** * A registry which collects information about NgModules, Directives, Components, and Pipes which * are local (declared in the ts.Program being compiled), and can produce `LocalModuleScope`s @@ -89,11 +79,9 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop /** * A cache of calculated `LocalModuleScope`s for each NgModule declared in the current program. - * - * A value of `undefined` indicates the scope was invalid and produced errors (therefore, - * diagnostics should exist in the `scopeErrors` map). + */ - private cache = new Map(); + private cache = new Map(); /** * Tracks the `RemoteScope` for components requiring "remote scoping". @@ -111,13 +99,9 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop private scopeErrors = new Map(); /** - * Tracks which NgModules are unreliable due to errors within their declarations. - * - * This provides a unified view of which modules have errors, across all of the different - * diagnostic categories that can be produced. Theoretically this can be inferred from the other - * properties of this class, but is tracked explicitly to simplify the logic. + * Tracks which NgModules have directives/pipes that are declared in more than one module. */ - private taintedModules = new Set(); + private modulesWithStructuralErrors = new Set(); constructor( private localReader: MetadataReader, private dependencyScopeReader: DtsModuleScopeResolver, @@ -141,7 +125,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop registerPipeMetadata(pipe: PipeMeta): void {} - getScopeForComponent(clazz: ClassDeclaration): LocalModuleScope|null|'error' { + getScopeForComponent(clazz: ClassDeclaration): LocalModuleScope|null { const scope = !this.declarationToModule.has(clazz) ? null : this.getScopeOfModule(this.declarationToModule.get(clazz)!.ngModule); @@ -171,17 +155,10 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop * `LocalModuleScope` for the given NgModule if one can be produced, `null` if no scope was ever * defined, or the string `'error'` if the scope contained errors. */ - getScopeOfModule(clazz: ClassDeclaration): LocalModuleScope|'error'|null { - const scope = this.moduleToRef.has(clazz) ? + getScopeOfModule(clazz: ClassDeclaration): LocalModuleScope|null { + return this.moduleToRef.has(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)) { - return 'error'; - } - - // Translate undefined -> 'error'. - return scope !== undefined ? scope : 'error'; } /** @@ -200,20 +177,6 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop } } - /** - * Returns a collection of the compilation scope for each registered declaration. - */ - getCompilationScopes(): CompilationScope[] { - const scopes: CompilationScope[] = []; - this.declarationToModule.forEach((declData, declaration) => { - const scope = this.getScopeOfModule(declData.ngModule); - if (scope !== null && scope !== 'error') { - scopes.push({declaration, ngModule: declData.ngModule, ...scope.compilation}); - } - }); - return scopes; - } - private registerDeclarationOfModule( ngModule: ClassDeclaration, decl: Reference, rawDeclarations: ts.Expression|null): void { @@ -236,9 +199,9 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop const duplicateDeclMap = new Map(); const firstDeclData = this.declarationToModule.get(decl.node)!; - // Mark both modules as tainted, since their declarations are missing a component. - this.taintedModules.add(firstDeclData.ngModule); - this.taintedModules.add(ngModule); + // Mark both modules as having duplicate declarations. + this.modulesWithStructuralErrors.add(firstDeclData.ngModule); + this.modulesWithStructuralErrors.add(ngModule); // Being detected as a duplicate means there are two NgModules (for now) which declare this // directive/pipe. Add both of them to the duplicate tracking map. @@ -256,16 +219,11 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop } /** - * Implementation of `getScopeOfModule` which accepts a reference to a class and differentiates - * between: - * - * * no scope being available (returns `null`) - * * a scope being produced with errors (returns `undefined`). + * Implementation of `getScopeOfModule` which accepts a reference to a class. */ - private getScopeOfModuleReference(ref: Reference): LocalModuleScope|null - |undefined { + private getScopeOfModuleReference(ref: Reference): LocalModuleScope|null { if (this.cache.has(ref.node)) { - return this.cache.get(ref.node); + return this.cache.get(ref.node)!; } // Seal the registry to protect the integrity of the `LocalModuleScope` cache. @@ -315,20 +273,33 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop // c) If it's neither an NgModule nor a directive/pipe in the compilation scope, then this // is an error. + // + let isPoisoned = false; + if (this.modulesWithStructuralErrors.has(ngModule.ref.node)) { + // If the module contains declarations that are duplicates, then it's considered poisoned. + isPoisoned = true; + } + // 1) process imports. for (const decl of ngModule.imports) { const importScope = this.getExportedScope(decl, diagnostics, ref.node, 'import'); if (importScope === null) { // An import wasn't an NgModule, so record an error. diagnostics.push(invalidRef(ref.node, decl, 'import')); + isPoisoned = true; continue; - } else if (importScope === undefined) { + } else if (importScope === 'invalid' || importScope.exported.isPoisoned) { // An import was an NgModule but contained errors of its own. Record this as an error too, // because this scope is always going to be incorrect if one of its imports could not be // read. diagnostics.push(invalidTransitiveNgModuleRef(ref.node, decl, 'import')); - continue; + isPoisoned = true; + + if (importScope === 'invalid') { + continue; + } } + for (const directive of importScope.exported.directives) { compilationDirectives.set(directive.ref.node, directive); } @@ -346,11 +317,12 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop const pipe = this.localReader.getPipeMetadata(decl); if (directive !== null) { compilationDirectives.set(decl.node, {...directive, ref: decl}); + if (directive.isPoisoned) { + isPoisoned = true; + } } else if (pipe !== null) { compilationPipes.set(decl.node, {...pipe, ref: decl}); } else { - this.taintedModules.add(ngModule.ref.node); - const errorNode = decl.getOriginForDiagnostics(ngModule.rawDeclarations!); diagnostics.push(makeDiagnostic( ErrorCode.NGMODULE_INVALID_DECLARATION, errorNode, @@ -361,6 +333,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop `Either remove it from the NgModule's declarations, or add an appropriate Angular decorator.`, [makeRelatedInformation( decl.node.name, `'${decl.node.name.text}' is declared here.`)])); + isPoisoned = true; continue; } @@ -374,22 +347,26 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop // imported types. for (const decl of ngModule.exports) { // Attempt to resolve decl as an NgModule. - const importScope = this.getExportedScope(decl, diagnostics, ref.node, 'export'); - if (importScope === undefined) { + const exportScope = this.getExportedScope(decl, diagnostics, ref.node, 'export'); + if (exportScope === 'invalid' || (exportScope !== null && exportScope.exported.isPoisoned)) { // An export was an NgModule but contained errors of its own. Record this as an error too, // because this scope is always going to be incorrect if one of its exports could not be // read. diagnostics.push(invalidTransitiveNgModuleRef(ref.node, decl, 'export')); - continue; - } else if (importScope !== null) { + isPoisoned = true; + + if (exportScope === 'invalid') { + continue; + } + } else if (exportScope !== null) { // decl is an NgModule. - for (const directive of importScope.exported.directives) { + for (const directive of exportScope.exported.directives) { exportDirectives.set(directive.ref.node, directive); } - for (const pipe of importScope.exported.pipes) { + for (const pipe of exportScope.exported.pipes) { exportPipes.set(pipe.ref.node, pipe); } - for (const exportedModule of importScope.exported.ngModules) { + for (const exportedModule of exportScope.exported.ngModules) { exportedModules.add(exportedModule); } } else if (compilationDirectives.has(decl.node)) { @@ -408,30 +385,20 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop } else { diagnostics.push(invalidRef(ref.node, decl, 'export')); } + isPoisoned = true; continue; } } - const exported = { + const exported: ScopeData = { directives: Array.from(exportDirectives.values()), pipes: Array.from(exportPipes.values()), ngModules: Array.from(exportedModules), + isPoisoned, }; const reexports = this.getReexports(ngModule, ref, declared, exported, diagnostics); - // Check if this scope had any errors during production. - if (diagnostics.length > 0) { - // Cache undefined, to mark the fact that the scope is invalid. - this.cache.set(ref.node, undefined); - - // Save the errors for retrieval. - this.scopeErrors.set(ref.node, diagnostics); - - // Mark this module as being tainted. - this.taintedModules.add(ref.node); - return undefined; - } // Finally, produce the `LocalModuleScope` with both the compilation and export scopes. const scope: LocalModuleScope = { @@ -440,11 +407,22 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop directives: Array.from(compilationDirectives.values()), pipes: Array.from(compilationPipes.values()), ngModules: Array.from(compilationModules), + isPoisoned, }, exported, reexports, schemas: ngModule.schemas, }; + + // Check if this scope had any errors during production. + if (diagnostics.length > 0) { + // Save the errors for retrieval. + this.scopeErrors.set(ref.node, diagnostics); + + // Mark this module as being tainted. + this.modulesWithStructuralErrors.add(ref.node); + } + this.cache.set(ref.node, scope); return scope; } @@ -471,15 +449,15 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop * The NgModule in question may be declared locally in the current ts.Program, or it may be * declared in a .d.ts file. * - * @returns `null` if no scope could be found, or `undefined` if an invalid scope - * was found. + * @returns `null` if no scope could be found, or `'invalid'` if the `Reference` is not a valid + * NgModule. * * May also contribute diagnostics of its own by adding to the given `diagnostics` * array parameter. */ private getExportedScope( ref: Reference, diagnostics: ts.Diagnostic[], - ownerForErrors: DeclarationNode, type: 'import'|'export'): ExportScope|null|undefined { + ownerForErrors: DeclarationNode, type: 'import'|'export'): ExportScope|null|'invalid' { if (ref.node.getSourceFile().isDeclarationFile) { // The NgModule is declared in a .d.ts file. Resolve it with the `DependencyScopeReader`. if (!ts.isClassDeclaration(ref.node)) { @@ -491,7 +469,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop code, identifierOfNode(ref.node) || ref.node, `Appears in the NgModule.${type}s of ${ nodeNameForError(ownerForErrors)}, but could not be resolved to an NgModule`)); - return undefined; + return 'invalid'; } return this.dependencyScopeReader.resolve(ref); } else { @@ -533,7 +511,8 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop asAlias: exportName, }); } else { - const expr = this.refEmitter.emit(exportRef.cloneWithNoIdentifiers(), sourceFile); + const expr = + this.refEmitter.emit(exportRef.cloneWithNoIdentifiers(), sourceFile).expression; if (!(expr instanceof ExternalExpr) || expr.value.moduleName === null || expr.value.name === null) { throw new Error('Expected ExternalExpr'); 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 0216fd8864..38a8ad79dd 100644 --- a/packages/compiler-cli/src/ngtsc/scope/test/local_spec.ts +++ b/packages/compiler-cli/src/ngtsc/scope/test/local_spec.ts @@ -219,7 +219,7 @@ describe('LocalModuleScopeRegistry', () => { rawDeclarations: null, }); - expect(scopeRegistry.getScopeOfModule(ModuleA.node)).toBe('error'); + expect(scopeRegistry.getScopeOfModule(ModuleA.node)!.compilation.isPoisoned).toBeTrue(); // ModuleA should have associated diagnostics as it exports `Dir` without declaring it. expect(scopeRegistry.getDiagnosticsOfModule(ModuleA.node)).not.toBeNull(); @@ -248,6 +248,8 @@ function fakeDirective(ref: Reference): DirectiveMeta { undeclaredInputFields: new Set(), isGeneric: false, baseClass: null, + isPoisoned: false, + isStructural: false, }; } diff --git a/packages/compiler-cli/src/ngtsc/sourcemaps/index.ts b/packages/compiler-cli/src/ngtsc/sourcemaps/index.ts index 583e3b1a0c..210aeb457d 100644 --- a/packages/compiler-cli/src/ngtsc/sourcemaps/index.ts +++ b/packages/compiler-cli/src/ngtsc/sourcemaps/index.ts @@ -5,6 +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 */ -export {RawSourceMap} from './src/raw_source_map'; +export {ContentOrigin} from './src/content_origin'; +export {MapAndPath, RawSourceMap} from './src/raw_source_map'; export {Mapping, SourceFile} from './src/source_file'; export {SourceFileLoader} from './src/source_file_loader'; diff --git a/packages/compiler-cli/src/ngtsc/sourcemaps/src/raw_source_map.ts b/packages/compiler-cli/src/ngtsc/sourcemaps/src/raw_source_map.ts index 1a5e86369b..c9ac934ac3 100644 --- a/packages/compiler-cli/src/ngtsc/sourcemaps/src/raw_source_map.ts +++ b/packages/compiler-cli/src/ngtsc/sourcemaps/src/raw_source_map.ts @@ -5,6 +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 {AbsoluteFsPath} from '../../file_system'; +import {ContentOrigin} from './content_origin'; /** * This interface is the basic structure of the JSON in a raw source map that one might load from @@ -19,3 +21,22 @@ export interface RawSourceMap { sourcesContent?: (string|null)[]; mappings: string; } + + +/** + * The path and content of a source-map. + */ +export interface MapAndPath { + /** The path to the source map if it was external or `null` if it was inline. */ + mapPath: AbsoluteFsPath|null; + /** The raw source map itself. */ + map: RawSourceMap; +} + +/** + * Information about a loaded source-map. + */ +export interface SourceMapInfo extends MapAndPath { + /** From where the content for this source-map came. */ + origin: ContentOrigin; +} diff --git a/packages/compiler-cli/src/ngtsc/sourcemaps/src/segment_marker.ts b/packages/compiler-cli/src/ngtsc/sourcemaps/src/segment_marker.ts index 82865869c6..a325bc19c3 100644 --- a/packages/compiler-cli/src/ngtsc/sourcemaps/src/segment_marker.ts +++ b/packages/compiler-cli/src/ngtsc/sourcemaps/src/segment_marker.ts @@ -10,7 +10,7 @@ /** * 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 + * The end of a segment is indicated by the first segment-marker of another mapping whose start * is greater or equal to this one. */ export interface SegmentMarker { diff --git a/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file.ts b/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file.ts index 43e1b841ea..5405756f60 100644 --- a/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file.ts +++ b/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file.ts @@ -8,9 +8,9 @@ import {removeComments, removeMapFileComments} from 'convert-source-map'; import {decode, encode, SourceMapMappings, SourceMapSegment} from 'sourcemap-codec'; -import {AbsoluteFsPath, FileSystem} from '../../file_system'; +import {AbsoluteFsPath, PathManipulation} from '../../file_system'; -import {RawSourceMap} from './raw_source_map'; +import {RawSourceMap, SourceMapInfo} from './raw_source_map'; import {compareSegments, offsetSegment, SegmentMarker} from './segment_marker'; export function removeSourceMapComments(contents: string): string { @@ -33,13 +33,11 @@ export class SourceFile { readonly sourcePath: AbsoluteFsPath, /** The contents of this source file. */ readonly contents: string, - /** The raw source map (if any) associated with this source file. */ - readonly rawMap: RawSourceMap|null, - /** Whether this source file's source map was inline or external. */ - readonly inline: boolean, + /** The raw source map (if any) referenced by this source file. */ + readonly rawMap: SourceMapInfo|null, /** Any source files referenced by the raw source map associated with this source file. */ readonly sources: (SourceFile|null)[], - private fs: FileSystem, + private fs: PathManipulation, ) { this.contents = removeSourceMapComments(contents); this.startOfLinePositions = computeStartOfLinePositions(this.contents); @@ -50,13 +48,19 @@ export class SourceFile { * Render the raw source map generated from the flattened mappings. */ renderFlattenedSourceMap(): RawSourceMap { - const sources: SourceFile[] = []; - const names: string[] = []; - + const sources = new IndexedMap(); + const names = new IndexedSet(); const mappings: SourceMapMappings = []; + const sourcePathDir = this.fs.dirname(this.sourcePath); + // Computing the relative path can be expensive, and we are likely to have the same path for + // many (if not all!) mappings. + const relativeSourcePathCache = + new Cache(input => this.fs.relative(sourcePathDir, input)); for (const mapping of this.flattenedMappings) { - const sourceIndex = findIndexOrAdd(sources, mapping.originalSource); + const sourceIndex = sources.set( + relativeSourcePathCache.get(mapping.originalSource.sourcePath), + mapping.originalSource.contents); const mappingArray: SourceMapSegment = [ mapping.generatedSegment.column, sourceIndex, @@ -64,7 +68,7 @@ export class SourceFile { mapping.originalSegment.column, ]; if (mapping.name !== undefined) { - const nameIndex = findIndexOrAdd(names, mapping.name); + const nameIndex = names.add(mapping.name); mappingArray.push(nameIndex); } @@ -77,14 +81,13 @@ export class SourceFile { mappings[line].push(mappingArray); } - const sourcePathDir = this.fs.dirname(this.sourcePath); const sourceMap: RawSourceMap = { version: 3, file: this.fs.relative(sourcePathDir, this.sourcePath), - sources: sources.map(sf => this.fs.relative(sourcePathDir, sf.sourcePath)), - names, + sources: sources.keys, + names: names.values, mappings: encode(mappings), - sourcesContent: sources.map(sf => sf.contents), + sourcesContent: sources.values, }; return sourceMap; } @@ -136,7 +139,8 @@ export class SourceFile { * source files with no transitive source maps. */ private flattenMappings(): Mapping[] { - const mappings = parseMappings(this.rawMap, this.sources, this.startOfLinePositions); + const mappings = + parseMappings(this.rawMap && this.rawMap.map, this.sources, this.startOfLinePositions); ensureOriginalSegmentLinks(mappings); const flattenedMappings: Mapping[] = []; for (let mappingIndex = 0; mappingIndex < mappings.length; mappingIndex++) { @@ -259,23 +263,6 @@ export interface Mapping { readonly name?: string; } -/** - * Find the index of `item` in the `items` array. - * If it is not found, then push `item` to the end of the array and return its new index. - * - * @param items the collection in which to look for `item`. - * @param item the item to look for. - * @returns the index of the `item` in the `items` array. - */ -function findIndexOrAdd(items: T[], item: T): number { - const itemIndex = items.indexOf(item); - if (itemIndex > -1) { - return itemIndex; - } else { - items.push(item); - return items.length - 1; - } -} /** @@ -446,5 +433,98 @@ export function computeStartOfLinePositions(str: string) { } function computeLineLengths(str: string): number[] { - return (str.split(/\r?\n/)).map(s => s.length); + return (str.split(/\n/)).map(s => s.length); +} + +/** + * A collection of mappings between `keys` and `values` stored in the order in which the keys are + * first seen. + * + * The difference between this and a standard `Map` is that when you add a key-value pair the index + * of the `key` is returned. + */ +class IndexedMap { + private map = new Map(); + + /** + * An array of keys added to this map. + * + * This array is guaranteed to be in the order of the first time the key was added to the map. + */ + readonly keys: K[] = []; + + /** + * An array of values added to this map. + * + * This array is guaranteed to be in the order of the first time the associated key was added to + * the map. + */ + readonly values: V[] = []; + + /** + * Associate the `value` with the `key` and return the index of the key in the collection. + * + * If the `key` already exists then the `value` is not set and the index of that `key` is + * returned; otherwise the `key` and `value` are stored and the index of the new `key` is + * returned. + * + * @param key the key to associated with the `value`. + * @param value the value to associated with the `key`. + * @returns the index of the `key` in the `keys` array. + */ + set(key: K, value: V): number { + if (this.map.has(key)) { + return this.map.get(key)!; + } + const index = this.values.push(value) - 1; + this.keys.push(key); + this.map.set(key, index); + return index; + } +} + +/** + * A collection of `values` stored in the order in which they were added. + * + * The difference between this and a standard `Set` is that when you add a value the index of that + * item is returned. + */ +class IndexedSet { + private map = new Map(); + + /** + * An array of values added to this set. + * This array is guaranteed to be in the order of the first time the value was added to the set. + */ + readonly values: V[] = []; + + /** + * Add the `value` to the `values` array, if it doesn't already exist; returning the index of the + * `value` in the `values` array. + * + * If the `value` already exists then the index of that `value` is returned, otherwise the new + * `value` is stored and the new index returned. + * + * @param value the value to add to the set. + * @returns the index of the `value` in the `values` array. + */ + add(value: V): number { + if (this.map.has(value)) { + return this.map.get(value)!; + } + const index = this.values.push(value) - 1; + this.map.set(value, index); + return index; + } +} + +class Cache { + private map = new Map(); + constructor(private computeFn: (input: Input) => Cached) {} + get(input: Input): Cached { + if (!this.map.has(input)) { + this.map.set(input, this.computeFn(input)); + } + return this.map.get(input)!; + } } diff --git a/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file_loader.ts b/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file_loader.ts index 7017144bb6..bb6721cc12 100644 --- a/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file_loader.ts +++ b/packages/compiler-cli/src/ngtsc/sourcemaps/src/source_file_loader.ts @@ -7,10 +7,11 @@ */ import {commentRegex, fromComment, mapFileCommentRegex} from 'convert-source-map'; -import {AbsoluteFsPath, FileSystem} from '../../file_system'; +import {AbsoluteFsPath, ReadonlyFileSystem} from '../../file_system'; import {Logger} from '../../logging'; -import {RawSourceMap} from './raw_source_map'; +import {ContentOrigin} from './content_origin'; +import {MapAndPath, RawSourceMap, SourceMapInfo} from './raw_source_map'; import {SourceFile} from './source_file'; const SCHEME_MATCHER = /^([a-z][a-z0-9.-]*):\/\//i; @@ -28,12 +29,13 @@ export class SourceFileLoader { private currentPaths: AbsoluteFsPath[] = []; constructor( - private fs: FileSystem, private logger: Logger, + private fs: ReadonlyFileSystem, private logger: Logger, /** A map of URL schemes to base paths. The scheme name should be lowercase. */ private schemeMap: Record) {} /** - * Load a source file, compute its source map, and recursively load any referenced source files. + * Load a source file from the provided content and 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. @@ -41,24 +43,51 @@ export class SourceFileLoader { * @returns a SourceFile object created from the `contents` and provided source-map info. */ loadSourceFile(sourcePath: AbsoluteFsPath, contents: string, mapAndPath: MapAndPath): SourceFile; + /** + * Load a source file from the provided content, 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. + * @returns a SourceFile object created from the `contents` and computed source-map info. + */ + loadSourceFile(sourcePath: AbsoluteFsPath, contents: string): SourceFile; + /** + * Load a source file from the file-system, compute its source map, and recursively load any + * referenced source files. + * + * @param sourcePath The path to the source file to load. + * @returns a SourceFile object if its contents could be loaded from disk, or null otherwise. + */ + loadSourceFile(sourcePath: AbsoluteFsPath): SourceFile|null; + loadSourceFile( + sourcePath: AbsoluteFsPath, contents: string|null = null, + mapAndPath: MapAndPath|null = null): SourceFile|null { + const contentsOrigin = contents !== null ? ContentOrigin.Provided : ContentOrigin.FileSystem; + const sourceMapInfo: SourceMapInfo|null = + mapAndPath && {origin: ContentOrigin.Provided, ...mapAndPath}; + return this.loadSourceFileInternal(sourcePath, contents, contentsOrigin, sourceMapInfo); + } + /** * 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. + * @param contents The contents of the source file to load, if provided inline. If `null`, + * the contents will be read from the file at the `sourcePath`. + * @param sourceOrigin Describes where the source content came from. + * @param sourceMapInfo The raw contents and path of the source-map file. If `null` the + * source-map will be computed from the contents of the source file, either inline or loaded + * from the file-system. * - * @returns a SourceFile if the content for one was provided or able to be loaded from disk, + * @returns a SourceFile if the content for one was provided or was able to be loaded from disk, * `null` otherwise. */ - loadSourceFile(sourcePath: AbsoluteFsPath, contents?: string|null, mapAndPath?: null): SourceFile - |null; - loadSourceFile( - sourcePath: AbsoluteFsPath, contents: string|null = null, - mapAndPath: MapAndPath|null = null): SourceFile|null { + private loadSourceFileInternal( + sourcePath: AbsoluteFsPath, contents: string|null, sourceOrigin: ContentOrigin, + sourceMapInfo: SourceMapInfo|null): SourceFile|null { const previousPaths = this.currentPaths.slice(); try { if (contents === null) { @@ -69,21 +98,17 @@ export class SourceFileLoader { } // If not provided try to load the source map based on the source itself - if (mapAndPath === null) { - mapAndPath = this.loadSourceMap(sourcePath, contents); + if (sourceMapInfo === null) { + sourceMapInfo = this.loadSourceMap(sourcePath, contents, sourceOrigin); } - 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; + if (sourceMapInfo !== null) { + const basePath = sourceMapInfo.mapPath || sourcePath; + sources = this.processSources(basePath, sourceMapInfo); } - return new SourceFile(sourcePath, contents, map, inline, sources, this.fs); + return new SourceFile(sourcePath, contents, sourceMapInfo, sources, this.fs); } catch (e) { this.logger.warn( `Unable to fully load ${sourcePath} for source-map flattening: ${e.message}`); @@ -100,15 +125,34 @@ export class SourceFileLoader { * * Source maps can be inline, as part of a base64 encoded comment, or external as a separate file * whose path is indicated in a comment or implied from the name of the source file itself. + * + * @param sourcePath the path to the source file. + * @param sourceContents the contents of the source file. + * @param sourceOrigin where the content of the source file came from. + * @returns the parsed contents and path of the source-map, if loading was successful, null + * otherwise. */ - private loadSourceMap(sourcePath: AbsoluteFsPath, contents: string): MapAndPath|null { + private loadSourceMap( + sourcePath: AbsoluteFsPath, sourceContents: string, + sourceOrigin: ContentOrigin): SourceMapInfo|null { // Only consider a source-map comment from the last non-empty line of the file, in case there // are embedded source-map comments elsewhere in the file (as can be the case with bundlers like // webpack). - const lastLine = this.getLastNonEmptyLine(contents); + const lastLine = this.getLastNonEmptyLine(sourceContents); const inline = commentRegex.exec(lastLine); if (inline !== null) { - return {map: fromComment(inline.pop()!).sourcemap, mapPath: null}; + return { + map: fromComment(inline.pop()!).sourcemap, + mapPath: null, + origin: ContentOrigin.Inline, + }; + } + + if (sourceOrigin === ContentOrigin.Inline) { + // The source file was provided inline and its contents did not include an inline source-map. + // So we don't try to load an external source-map from the file-system, since this can lead to + // invalid circular dependencies. + return null; } const external = mapFileCommentRegex.exec(lastLine); @@ -116,7 +160,11 @@ export class SourceFileLoader { try { const fileName = external[1] || external[2]; const externalMapPath = this.fs.resolve(this.fs.dirname(sourcePath), fileName); - return {map: this.readRawSourceMap(externalMapPath), mapPath: externalMapPath}; + return { + map: this.readRawSourceMap(externalMapPath), + mapPath: externalMapPath, + origin: ContentOrigin.FileSystem, + }; } catch (e) { this.logger.warn( `Unable to fully load ${sourcePath} for source-map flattening: ${e.message}`); @@ -126,7 +174,11 @@ export class SourceFileLoader { const impliedMapPath = this.fs.resolve(sourcePath + '.map'); if (this.fs.exists(impliedMapPath)) { - return {map: this.readRawSourceMap(impliedMapPath), mapPath: impliedMapPath}; + return { + map: this.readRawSourceMap(impliedMapPath), + mapPath: impliedMapPath, + origin: ContentOrigin.FileSystem, + }; } return null; @@ -136,13 +188,23 @@ 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): (SourceFile|null)[] { + private processSources(basePath: AbsoluteFsPath, {map, origin: sourceMapOrigin}: SourceMapInfo): + (SourceFile|null)[] { const sourceRoot = this.fs.resolve( this.fs.dirname(basePath), this.replaceSchemeWithPath(map.sourceRoot || '')); return map.sources.map((source, index) => { const path = this.fs.resolve(sourceRoot, this.replaceSchemeWithPath(source)); const content = map.sourcesContent && map.sourcesContent[index] || null; - return this.loadSourceFile(path, content, null); + // The origin of this source file is "inline" if we extracted it from the source-map's + // `sourcesContent`, except when the source-map itself was "provided" in-memory. + // An inline source file is treated as if it were from the file-system if the source-map that + // contains it was provided in-memory. The first call to `loadSourceFile()` is special in that + // if you "provide" the contents of the source-map in-memory then we don't want to block + // loading sources from the file-system just because this source-map had an inline source. + const sourceOrigin = content !== null && sourceMapOrigin !== ContentOrigin.Provided ? + ContentOrigin.Inline : + ContentOrigin.FileSystem; + return this.loadSourceFileInternal(path, content, sourceOrigin, null); }); } @@ -164,7 +226,7 @@ export class SourceFileLoader { */ private readRawSourceMap(mapPath: AbsoluteFsPath): RawSourceMap { this.trackPath(mapPath); - return JSON.parse(this.fs.readFile(mapPath)); + return JSON.parse(this.fs.readFile(mapPath)) as RawSourceMap; } /** @@ -206,11 +268,3 @@ export class SourceFileLoader { SCHEME_MATCHER, (_: string, scheme: string) => this.schemeMap[scheme.toLowerCase()] || ''); } } - -/** A small helper structure that is returned from `loadSourceMap()`. */ -interface MapAndPath { - /** The path to the source map if it was external or `null` if it was inline. */ - mapPath: AbsoluteFsPath|null; - /** The raw source map itself. */ - map: RawSourceMap; -} diff --git a/packages/compiler-cli/src/ngtsc/sourcemaps/test/segment_marker_spec.ts b/packages/compiler-cli/src/ngtsc/sourcemaps/test/segment_marker_spec.ts index d3fedc69b5..b374cafea6 100644 --- a/packages/compiler-cli/src/ngtsc/sourcemaps/test/segment_marker_spec.ts +++ b/packages/compiler-cli/src/ngtsc/sourcemaps/test/segment_marker_spec.ts @@ -79,31 +79,32 @@ describe('SegmentMarker utils', () => { it('should return a new marker offset by the given chars', () => { const startOfLinePositions = computeStartOfLinePositions('012345\n0123456789\r\n012*4567\n0123456'); - const marker = {line: 2, column: 3, position: 21, next: undefined}; + const marker = {line: 2, column: 3, position: 22, next: undefined}; + expect(offsetSegment(startOfLinePositions, marker, 1)) - .toEqual({line: 2, column: 4, position: 22, next: undefined}); + .toEqual({line: 2, column: 4, position: 23, next: undefined}); expect(offsetSegment(startOfLinePositions, marker, 2)) - .toEqual({line: 2, column: 5, position: 23, next: undefined}); + .toEqual({line: 2, column: 5, position: 24, next: undefined}); expect(offsetSegment(startOfLinePositions, marker, 4)) - .toEqual({line: 2, column: 7, position: 25, next: undefined}); + .toEqual({line: 2, column: 7, position: 26, next: undefined}); expect(offsetSegment(startOfLinePositions, marker, 6)) - .toEqual({line: 3, column: 0, position: 27, next: undefined}); + .toEqual({line: 3, column: 0, position: 28, next: undefined}); expect(offsetSegment(startOfLinePositions, marker, 8)) - .toEqual({line: 3, column: 2, position: 29, next: undefined}); + .toEqual({line: 3, column: 2, position: 30, next: undefined}); expect(offsetSegment(startOfLinePositions, marker, 20)) - .toEqual({line: 3, column: 14, position: 41, next: undefined}); + .toEqual({line: 3, column: 14, position: 42, next: undefined}); expect(offsetSegment(startOfLinePositions, marker, -1)) - .toEqual({line: 2, column: 2, position: 20, next: undefined}); + .toEqual({line: 2, column: 2, position: 21, next: undefined}); expect(offsetSegment(startOfLinePositions, marker, -2)) - .toEqual({line: 2, column: 1, position: 19, next: undefined}); + .toEqual({line: 2, column: 1, position: 20, next: undefined}); expect(offsetSegment(startOfLinePositions, marker, -3)) - .toEqual({line: 2, column: 0, position: 18, next: undefined}); + .toEqual({line: 2, column: 0, position: 19, next: undefined}); expect(offsetSegment(startOfLinePositions, marker, -4)) - .toEqual({line: 1, column: 10, position: 17, next: undefined}); + .toEqual({line: 1, column: 11, position: 18, next: undefined}); expect(offsetSegment(startOfLinePositions, marker, -6)) - .toEqual({line: 1, column: 8, position: 15, next: undefined}); + .toEqual({line: 1, column: 9, position: 16, next: undefined}); expect(offsetSegment(startOfLinePositions, marker, -16)) - .toEqual({line: 0, column: 5, position: 5, next: undefined}); + .toEqual({line: 0, column: 6, position: 6, next: undefined}); }); }); }); diff --git a/packages/compiler-cli/src/ngtsc/sourcemaps/test/source_file_loader_spec.ts b/packages/compiler-cli/src/ngtsc/sourcemaps/test/source_file_loader_spec.ts index 9686355a5e..1e86114cf4 100644 --- a/packages/compiler-cli/src/ngtsc/sourcemaps/test/source_file_loader_spec.ts +++ b/packages/compiler-cli/src/ngtsc/sourcemaps/test/source_file_loader_spec.ts @@ -11,7 +11,7 @@ import {absoluteFrom, FileSystem, getFileSystem} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {MockLogger} from '../../logging/testing'; import {RawSourceMap} from '../src/raw_source_map'; -import {SourceFileLoader as SourceFileLoader} from '../src/source_file_loader'; +import {SourceFileLoader} from '../src/source_file_loader'; runInEachFileSystem(() => { describe('SourceFileLoader', () => { @@ -34,7 +34,7 @@ runInEachFileSystem(() => { } expect(sourceFile.contents).toEqual('some inline content'); expect(sourceFile.sourcePath).toEqual(_('/foo/src/index.js')); - expect(sourceFile.rawMap).toEqual(null); + expect(sourceFile.rawMap).toBe(null); expect(sourceFile.sources).toEqual([]); }); @@ -47,7 +47,7 @@ runInEachFileSystem(() => { } expect(sourceFile.contents).toEqual('some external content'); expect(sourceFile.sourcePath).toEqual(_('/foo/src/index.js')); - expect(sourceFile.rawMap).toEqual(null); + expect(sourceFile.rawMap).toBe(null); expect(sourceFile.sources).toEqual([]); }); @@ -60,7 +60,10 @@ runInEachFileSystem(() => { if (sourceFile === null) { return fail('Expected source file to be defined'); } - expect(sourceFile.rawMap).toEqual(sourceMap); + if (sourceFile.rawMap === null) { + return fail('Expected source map to be defined'); + } + expect(sourceFile.rawMap.map).toEqual(sourceMap); }); it('should only read source-map comments from the last line of a file', () => { @@ -76,7 +79,10 @@ runInEachFileSystem(() => { if (sourceFile === null) { return fail('Expected source file to be defined'); } - expect(sourceFile.rawMap).toEqual(sourceMap); + if (sourceFile.rawMap === null) { + return fail('Expected source map to be defined'); + } + expect(sourceFile.rawMap.map).toEqual(sourceMap); }); for (const eolMarker of ['\n', '\r\n']) { @@ -97,7 +103,10 @@ runInEachFileSystem(() => { if (sourceFile === null) { return fail('Expected source file to be defined'); } - expect(sourceFile.rawMap).toEqual(sourceMap); + if (sourceFile.rawMap === null) { + return fail('Expected source map to be defined'); + } + expect(sourceFile.rawMap.map).toEqual(sourceMap); }); } @@ -121,7 +130,10 @@ runInEachFileSystem(() => { if (sourceFile === null) { return fail('Expected source file to be defined'); } - expect(sourceFile.rawMap).toEqual(sourceMap); + if (sourceFile.rawMap === null) { + return fail('Expected source map to be defined'); + } + expect(sourceFile.rawMap.map).toEqual(sourceMap); }); it('should load a file with an implied source map', () => { @@ -132,7 +144,10 @@ runInEachFileSystem(() => { if (sourceFile === null) { return fail('Expected source file to be defined'); } - expect(sourceFile.rawMap).toEqual(sourceMap); + if (sourceFile.rawMap === null) { + return fail('Expected source map to be defined'); + } + expect(sourceFile.rawMap.map).toEqual(sourceMap); }); it('should handle missing implied source-map file', () => { @@ -177,29 +192,32 @@ runInEachFileSystem(() => { expect(sourceFile.contents).toEqual('index content'); expect(sourceFile.sourcePath).toEqual(_('/foo/src/index.js')); - expect(sourceFile.rawMap).toEqual(indexSourceMap); + if (sourceFile.rawMap === null) { + return fail('Expected source map to be defined'); + } + expect(sourceFile.rawMap.map).toEqual(indexSourceMap); 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]!.rawMap).toBe(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]!.rawMap!.map).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]!.rawMap).toBe(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]!.rawMap).toBe(null); expect(sourceFile.sources[2]!.sources).toEqual([]); }); @@ -217,7 +235,10 @@ runInEachFileSystem(() => { expect(sourceFile.contents).toEqual('index content'); expect(sourceFile.sourcePath).toEqual(_('/foo/src/index.js')); - expect(sourceFile.rawMap).toEqual(indexSourceMap); + if (sourceFile.rawMap === null) { + return fail('Expected source map to be defined'); + } + expect(sourceFile.rawMap.map).toEqual(indexSourceMap); expect(sourceFile.sources.length).toEqual(1); expect(sourceFile.sources[0]).toBe(null); }); @@ -225,6 +246,10 @@ runInEachFileSystem(() => { it('should log a warning if there is a cyclic dependency in source files loaded from disk', () => { + // a.js -> a.js.map -> b.js -> b.js.map -> c.js -> c.js.map -> (external) a.js + // ^^^^^^^^^^^^^^^ + // c.js.map incorrectly links to a.js, creating a cycle + fs.ensureDir(_('/foo/src')); const aMap = createRawSourceMap({file: 'a.js', sources: ['b.js']}); @@ -248,7 +273,10 @@ runInEachFileSystem(() => { 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); + if (sourceFile.rawMap === null) { + return fail('Expected source map to be defined'); + } + expect(sourceFile.rawMap.map).toEqual(aMap); expect(sourceFile.sources.length).toEqual(1); expect(logger.logs.warn[0][0]) @@ -259,65 +287,119 @@ runInEachFileSystem(() => { it('should log a warning if there is a cyclic dependency in source maps loaded from disk', () => { + // a.js -> a.js.map -> b.js -> a.js.map -> c.js + // ^^^^^^^^ + // b.js incorrectly links to a.js.map, creating a cycle + 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!); + const aMap = createRawSourceMap({file: 'a.js', sources: ['b.js']}); + const aMapPath = _('/foo/src/a.js.map'); + fs.writeFile(aMapPath, JSON.stringify(aMap)); + + const bPath = _('/foo/src/b.js'); + fs.writeFile(bPath, 'b.js content\n//# sourceMappingURL=a.js.map'); + + const sourceFile = registry.loadSourceFile(aPath); + if (sourceFile === null) { + return fail('Expected source file to be defined'); + } expect(sourceFile.contents).toEqual('a.js content\n'); expect(sourceFile.sourcePath).toEqual(_('/foo/src/a.js')); - expect(sourceFile.rawMap).toEqual(aMap); + if (sourceFile.rawMap === null) { + return fail('Expected source map to be defined'); + } + expect(sourceFile.rawMap.map).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); + `${aPath} -> ${aMapPath} -> ${bPath} -> ${aMapPath}`); + const innerSourceFile = sourceFile.sources[0]; + if (innerSourceFile === null) { + return fail('Expected source file to be defined'); + } + expect(innerSourceFile.contents).toEqual('b.js content\n'); + expect(innerSourceFile.sourcePath).toEqual(_('/foo/src/b.js')); + // The source-map from b.js was not loaded as it would have caused a cycle + expect(innerSourceFile.rawMap).toBe(null); expect(innerSourceFile.sources.length).toEqual(0); }); - it('should not fail if there is a cyclic dependency in filenames of inline sources', () => { + it('should not fail if the filename of an inline source looks like a cyclic dependency', () => { + // a.js -> (inline) a.js.map -> (inline) a.js + // ^^^^^^^^^^^^^ + // a.js loads despite same name as previous file because it is inline + 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 aMap = createRawSourceMap( + {file: 'a.js', sources: ['a.js'], sourcesContent: ['inline original a.js content']}); + fs.writeFile(aPath, 'a content\n' + fromObject(aMap).toComment()); - const bPath = _('/foo/src/b.js'); - fs.writeFile(bPath, 'b content'); - fs.writeFile( - _('/foo/src/b.js.map'), - JSON.stringify(createRawSourceMap({file: 'b.js', sources: ['c.js']}))); + const sourceFile = registry.loadSourceFile(aPath); + if (sourceFile === null) { + return fail('Expected source file to be defined'); + } + expect(sourceFile.sources.length).toEqual(1); + expect(sourceFile.sources[0]!.contents).toEqual('inline original a.js content'); + expect(sourceFile.sources[0]!.sourcePath).toEqual(aPath); + expect(sourceFile.sources[0]!.rawMap).toBe(null); + expect(sourceFile.sources[0]!.sources).toEqual([]); - const cPath = _('/foo/src/c.js'); - fs.writeFile(cPath, 'c content'); - fs.writeFile( - _('/foo/src/c.js.map'), - JSON.stringify(createRawSourceMap( - {file: 'c.js', sources: ['a.js'], sourcesContent: ['inline a.js content']}))); - - expect(() => registry.loadSourceFile(aPath)).not.toThrow(); + expect(logger.logs.warn.length).toEqual(0); }); + it('should not load source-maps (after the initial map) from disk if the source file was inline', + () => { + // a.js -> (initial) a.js.map -> b.js -> b.js.map -> (inline) c.js -> c.js.map + // ^^^^^^^^ + // c.js.map is not loaded because the referencing source file (c.js) was inline + + fs.ensureDir(_('/foo/src')); + + const aPath = _('/foo/src/a.js'); + fs.writeFile(aPath, 'a.js content\n//# sourceMappingURL=a.js.map'); + const aMapPath = _('/foo/src/a.js.map'); + const aMap = createRawSourceMap({file: 'a.js', sources: ['b.js']}); + fs.writeFile(aMapPath, JSON.stringify(aMap)); + + const bPath = _('/foo/src/b.js'); + fs.writeFile(bPath, 'b.js content\n//# sourceMappingURL=b.js.map'); + const bMapPath = _('/foo/src/b.js.map'); + const bMap = createRawSourceMap({ + file: 'b.js', + sources: ['c.js'], + sourcesContent: ['c content\n//# sourceMappingURL=c.js.map'] + }); + fs.writeFile(bMapPath, JSON.stringify(bMap)); + + const cMapPath = _('/foo/src/c.js.map'); + const cMap = createRawSourceMap({file: 'c.js', sources: ['d.js']}); + fs.writeFile(cMapPath, JSON.stringify(cMap)); + + const sourceFile = registry.loadSourceFile(aPath); + if (sourceFile === null) { + return fail('Expected source file to be defined'); + } + const bSource = sourceFile.sources[0]; + if (!bSource) { + return fail('Expected source file to be defined'); + } + const cSource = bSource.sources[0]; + if (!cSource) { + return fail('Expected source file to be defined'); + } + // External c.js.map never gets loaded because c.js was inline source + expect(cSource.rawMap).toBe(null); + expect(cSource.sources).toEqual([]); + + expect(logger.logs.warn.length).toEqual(0); + }); + for (const {scheme, mappedPath} of [{scheme: 'WEBPACK://', mappedPath: '/foo/src/index.ts'}, {scheme: 'webpack://', mappedPath: '/foo/src/index.ts'}, @@ -342,7 +424,7 @@ runInEachFileSystem(() => { } expect(originalSource.contents).toEqual('original content'); expect(originalSource.sourcePath).toEqual(_(mappedPath)); - expect(originalSource.rawMap).toEqual(null); + expect(originalSource.rawMap).toBe(null); expect(originalSource.sources).toEqual([]); }); @@ -366,7 +448,7 @@ runInEachFileSystem(() => { } expect(originalSource.contents).toEqual('original content'); expect(originalSource.sourcePath).toEqual(_(mappedPath)); - expect(originalSource.rawMap).toEqual(null); + expect(originalSource.rawMap).toBe(null); expect(originalSource.sources).toEqual([]); }); } diff --git a/packages/compiler-cli/src/ngtsc/sourcemaps/test/source_file_spec.ts b/packages/compiler-cli/src/ngtsc/sourcemaps/test/source_file_spec.ts index a6fb591a4f..9e4e079fbb 100644 --- a/packages/compiler-cli/src/ngtsc/sourcemaps/test/source_file_spec.ts +++ b/packages/compiler-cli/src/ngtsc/sourcemaps/test/source_file_spec.ts @@ -7,15 +7,16 @@ */ import {encode} from 'sourcemap-codec'; -import {absoluteFrom, FileSystem, getFileSystem} from '../../file_system'; +import {absoluteFrom, getFileSystem, PathManipulation} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; -import {RawSourceMap} from '../src/raw_source_map'; +import {ContentOrigin} from '../src/content_origin'; +import {RawSourceMap, SourceMapInfo} from '../src/raw_source_map'; import {SegmentMarker} from '../src/segment_marker'; import {computeStartOfLinePositions, ensureOriginalSegmentLinks, extractOriginalSegments, findLastMappingIndexBefore, Mapping, parseMappings, SourceFile} from '../src/source_file'; runInEachFileSystem(() => { describe('SourceFile and utilities', () => { - let fs: FileSystem; + let fs: PathManipulation; let _: typeof absoluteFrom; beforeEach(() => { @@ -42,7 +43,7 @@ runInEachFileSystem(() => { sources: ['a.js'], version: 3 }; - const originalSource = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, false, [], fs); + const originalSource = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, [], fs); const mappings = parseMappings(rawSourceMap, [originalSource], [0, 8]); expect(mappings).toEqual([ { @@ -73,8 +74,7 @@ runInEachFileSystem(() => { it('should parse the segments in ascending order of original position from the raw source map', () => { - const originalSource = - new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, false, [], fs); + const originalSource = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, [], fs); const rawSourceMap: RawSourceMap = { mappings: encode([[[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2]]]), names: [], @@ -91,8 +91,8 @@ runInEachFileSystem(() => { }); it('should create separate arrays for each original source file', () => { - const sourceA = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, false, [], fs); - const sourceB = new SourceFile(_('/foo/src/b.js'), '1234567', null, false, [], fs); + const sourceA = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, [], fs); + const sourceB = new SourceFile(_('/foo/src/b.js'), '1234567', null, [], fs); const rawSourceMap: RawSourceMap = { mappings: encode([[[0, 0, 0, 0], [2, 1, 0, 3], [4, 0, 0, 2], [5, 1, 0, 5], [6, 1, 0, 2]]]), @@ -316,8 +316,8 @@ runInEachFileSystem(() => { describe('ensureOriginalSegmentLinks', () => { it('should add `next` properties to each segment that point to the next segment in the same source file', () => { - const sourceA = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, false, [], fs); - const sourceB = new SourceFile(_('/foo/src/b.js'), '1234567', null, false, [], fs); + const sourceA = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, [], fs); + const sourceB = new SourceFile(_('/foo/src/b.js'), '1234567', null, [], fs); const rawSourceMap: RawSourceMap = { mappings: encode([[[0, 0, 0, 0], [2, 1, 0, 3], [4, 0, 0, 2], [5, 1, 0, 5], [6, 1, 0, 2]]]), @@ -338,55 +338,68 @@ runInEachFileSystem(() => { describe('SourceFile', () => { describe('flattenedMappings', () => { it('should be an empty array for source files with no source map', () => { - const sourceFile = - new SourceFile(_('/foo/src/index.js'), 'index contents', null, false, [], fs); + const sourceFile = new SourceFile(_('/foo/src/index.js'), 'index contents', null, [], fs); expect(sourceFile.flattenedMappings).toEqual([]); }); it('should be empty array for source files with no source map mappings', () => { - const rawSourceMap: RawSourceMap = {mappings: '', names: [], sources: [], version: 3}; + const rawSourceMap: SourceMapInfo = { + map: {mappings: '', names: [], sources: [], version: 3}, + mapPath: null, + origin: ContentOrigin.Provided + }; const sourceFile = - new SourceFile(_('/foo/src/index.js'), 'index contents', rawSourceMap, false, [], fs); + new SourceFile(_('/foo/src/index.js'), 'index contents', rawSourceMap, [], fs); expect(sourceFile.flattenedMappings).toEqual([]); }); it('should be the same as non-flat mappings if there is only one level of source map', () => { - const rawSourceMap: RawSourceMap = { - mappings: encode([[[0, 0, 0, 0], [6, 0, 0, 3]]]), - names: [], - sources: ['a.js'], - version: 3 + const rawSourceMap: SourceMapInfo = { + mapPath: null, + map: { + mappings: encode([[[0, 0, 0, 0], [6, 0, 0, 3]]]), + names: [], + sources: ['a.js'], + version: 3 + }, + origin: ContentOrigin.Provided, }; - const originalSource = - new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, false, [], fs); + const originalSource = new SourceFile(_('/foo/src/a.js'), 'abcdefg', null, [], fs); const sourceFile = new SourceFile( - _('/foo/src/index.js'), 'abc123defg', rawSourceMap, false, [originalSource], fs); + _('/foo/src/index.js'), 'abc123defg', rawSourceMap, [originalSource], fs); expect(removeOriginalSegmentLinks(sourceFile.flattenedMappings)) - .toEqual(parseMappings(rawSourceMap, [originalSource], [0, 11])); + .toEqual(parseMappings(rawSourceMap.map, [originalSource], [0, 11])); }); it('should merge mappings from flattened original source files', () => { - const cSource = new SourceFile(_('/foo/src/c.js'), 'bcd123', null, false, [], fs); - const dSource = new SourceFile(_('/foo/src/d.js'), 'aef', null, false, [], fs); + const cSource = new SourceFile(_('/foo/src/c.js'), 'bcd123', null, [], fs); + const dSource = new SourceFile(_('/foo/src/d.js'), 'aef', null, [], fs); - const bSourceMap: RawSourceMap = { - mappings: encode([[[0, 1, 0, 0], [1, 0, 0, 0], [4, 1, 0, 1]]]), - names: [], - sources: ['c.js', 'd.js'], - version: 3 + const bSourceMap: SourceMapInfo = { + mapPath: null, + map: { + mappings: encode([[[0, 1, 0, 0], [1, 0, 0, 0], [4, 1, 0, 1]]]), + names: [], + sources: ['c.js', 'd.js'], + version: 3 + }, + origin: ContentOrigin.Provided, }; - const bSource = new SourceFile( - _('/foo/src/b.js'), 'abcdef', bSourceMap, false, [cSource, dSource], fs); + const bSource = + new SourceFile(_('/foo/src/b.js'), 'abcdef', bSourceMap, [cSource, dSource], fs); - const aSourceMap: RawSourceMap = { - mappings: encode([[[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2], [5, 0, 0, 5]]]), - names: [], - sources: ['b.js'], - version: 3 + const aSourceMap: SourceMapInfo = { + mapPath: null, + map: { + mappings: encode([[[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2], [5, 0, 0, 5]]]), + names: [], + sources: ['b.js'], + version: 3 + }, + origin: ContentOrigin.Provided, }; - const aSource = - new SourceFile(_('/foo/src/a.js'), 'abdecf', aSourceMap, false, [bSource], fs); + const aSource = new SourceFile(_('/foo/src/a.js'), 'abdecf', aSourceMap, [bSource], fs); expect(removeOriginalSegmentLinks(aSource.flattenedMappings)).toEqual([ { @@ -429,27 +442,34 @@ runInEachFileSystem(() => { }); it('should ignore mappings to missing source files', () => { - const bSourceMap: RawSourceMap = { - mappings: encode([[[1, 0, 0, 0], [4, 0, 0, 3], [4, 0, 0, 6], [5, 0, 0, 7]]]), - names: [], - sources: ['c.js'], - version: 3 + const bSourceMap: SourceMapInfo = { + mapPath: null, + map: { + mappings: encode([[[1, 0, 0, 0], [4, 0, 0, 3], [4, 0, 0, 6], [5, 0, 0, 7]]]), + names: [], + sources: ['c.js'], + version: 3 + }, + origin: ContentOrigin.Provided, }; - const bSource = - new SourceFile(_('/foo/src/b.js'), 'abcdef', bSourceMap, false, [null], fs); - const aSourceMap: RawSourceMap = { - mappings: encode([[[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2], [5, 0, 0, 5]]]), - names: [], - sources: ['b.js'], - version: 3 + const bSource = new SourceFile(_('/foo/src/b.js'), 'abcdef', bSourceMap, [null], fs); + const aSourceMap: SourceMapInfo = { + mapPath: null, + map: { + mappings: encode([[[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2], [5, 0, 0, 5]]]), + names: [], + sources: ['b.js'], + version: 3 + }, + origin: ContentOrigin.Provided, }; - const aSource = - new SourceFile(_('/foo/src/a.js'), 'abdecf', aSourceMap, false, [bSource], fs); + const aSource = new SourceFile(_('/foo/src/a.js'), 'abdecf', aSourceMap, [bSource], fs); // These flattened mappings are just the mappings from a to b. - // (The mappings to c are dropped since there is no source file to map to.) + // (The mappings to c are dropped since there is no source file to map + // to.) expect(removeOriginalSegmentLinks(aSource.flattenedMappings)) - .toEqual(parseMappings(aSourceMap, [bSource], [0, 7])); + .toEqual(parseMappings(aSourceMap.map, [bSource], [0, 7])); }); /** @@ -467,23 +487,31 @@ runInEachFileSystem(() => { describe('renderFlattenedSourceMap()', () => { it('should convert the flattenedMappings into a raw source-map object', () => { - const cSource = new SourceFile(_('/foo/src/c.js'), 'bcd123e', null, false, [], fs); - const bToCSourceMap: RawSourceMap = { - mappings: encode([[[1, 0, 0, 0], [4, 0, 0, 3], [4, 0, 0, 6], [5, 0, 0, 7]]]), - names: [], - sources: ['c.js'], - version: 3 + const cSource = new SourceFile(_('/foo/src/c.js'), 'bcd123e', null, [], fs); + const bToCSourceMap: SourceMapInfo = { + mapPath: null, + map: { + mappings: encode([[[1, 0, 0, 0], [4, 0, 0, 3], [4, 0, 0, 6], [5, 0, 0, 7]]]), + names: [], + sources: ['c.js'], + version: 3 + }, + origin: ContentOrigin.Provided, }; const bSource = - new SourceFile(_('/foo/src/b.js'), 'abcdef', bToCSourceMap, false, [cSource], fs); - const aToBSourceMap: RawSourceMap = { - mappings: encode([[[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2], [5, 0, 0, 5]]]), - names: [], - sources: ['b.js'], - version: 3 + new SourceFile(_('/foo/src/b.js'), 'abcdef', bToCSourceMap, [cSource], fs); + const aToBSourceMap: SourceMapInfo = { + mapPath: null, + map: { + mappings: encode([[[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2], [5, 0, 0, 5]]]), + names: [], + sources: ['b.js'], + version: 3 + }, + origin: ContentOrigin.Provided, }; const aSource = - new SourceFile(_('/foo/src/a.js'), 'abdecf', aToBSourceMap, false, [bSource], fs); + new SourceFile(_('/foo/src/a.js'), 'abdecf', aToBSourceMap, [bSource], fs); const aTocSourceMap = aSource.renderFlattenedSourceMap(); expect(aTocSourceMap.version).toEqual(3); @@ -498,20 +526,24 @@ runInEachFileSystem(() => { }); it('should handle mappings that map from lines outside of the actual content lines', () => { - const bSource = new SourceFile(_('/foo/src/b.js'), 'abcdef', null, false, [], fs); - const aToBSourceMap: RawSourceMap = { - mappings: encode([ - [[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2], [5, 0, 0, 5]], - [ - [0, 0, 0, 0], // Extra mapping from a non-existent line - ] - ]), - names: [], - sources: ['b.js'], - version: 3 + const bSource = new SourceFile(_('/foo/src/b.js'), 'abcdef', null, [], fs); + const aToBSourceMap: SourceMapInfo = { + mapPath: null, + map: { + mappings: encode([ + [[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2], [5, 0, 0, 5]], + [ + [0, 0, 0, 0], // Extra mapping from a non-existent line + ] + ]), + names: [], + sources: ['b.js'], + version: 3 + }, + origin: ContentOrigin.Provided, }; const aSource = - new SourceFile(_('/foo/src/a.js'), 'abdecf', aToBSourceMap, false, [bSource], fs); + new SourceFile(_('/foo/src/a.js'), 'abdecf', aToBSourceMap, [bSource], fs); const aTocSourceMap = aSource.renderFlattenedSourceMap(); expect(aTocSourceMap.version).toEqual(3); @@ -520,52 +552,104 @@ runInEachFileSystem(() => { expect(aTocSourceMap.sourceRoot).toBeUndefined(); expect(aTocSourceMap.sources).toEqual(['b.js']); expect(aTocSourceMap.sourcesContent).toEqual(['abcdef']); - expect(aTocSourceMap.mappings).toEqual(aToBSourceMap.mappings); + expect(aTocSourceMap.mappings).toEqual(aToBSourceMap.map.mappings); + }); + + it('should consolidate source-files with the same relative path', () => { + const cSource1 = new SourceFile(_('/foo/src/lib/c.js'), 'bcd123e', null, [], fs); + const cSource2 = new SourceFile(_('/foo/src/lib/c.js'), 'bcd123e', null, [], fs); + + const bToCSourceMap: SourceMapInfo = { + mapPath: null, + map: { + mappings: encode([[[1, 0, 0, 0], [4, 0, 0, 3], [4, 0, 0, 6], [5, 0, 0, 7]]]), + names: [], + sources: ['c.js'], + version: 3 + }, + origin: ContentOrigin.Provided, + }; + const bSource = + new SourceFile(_('/foo/src/lib/b.js'), 'abcdef', bToCSourceMap, [cSource1], fs); + + const aToBCSourceMap: SourceMapInfo = { + mapPath: null, + map: { + mappings: + encode([[[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2], [5, 0, 0, 5], [6, 1, 0, 3]]]), + names: [], + sources: ['lib/b.js', 'lib/c.js'], + version: 3, + }, + origin: ContentOrigin.Provided, + }; + const aSource = new SourceFile( + _('/foo/src/a.js'), 'abdecf123', aToBCSourceMap, [bSource, cSource2], fs); + + const aTocSourceMap = aSource.renderFlattenedSourceMap(); + expect(aTocSourceMap.version).toEqual(3); + expect(aTocSourceMap.file).toEqual('a.js'); + expect(aTocSourceMap.names).toEqual([]); + expect(aTocSourceMap.sourceRoot).toBeUndefined(); + expect(aTocSourceMap.sources).toEqual(['lib/c.js']); + expect(aTocSourceMap.sourcesContent).toEqual(['bcd123e']); + expect(aTocSourceMap.mappings).toEqual(encode([[ + [1, 0, 0, 0], [2, 0, 0, 2], [3, 0, 0, 3], [3, 0, 0, 6], [4, 0, 0, 1], [5, 0, 0, 7], + [6, 0, 0, 3] + ]])); }); }); describe('getOriginalLocation()', () => { it('should return null for source files with no flattened mappings', () => { - const sourceFile = - new SourceFile(_('/foo/src/index.js'), 'index contents', null, false, [], fs); + const sourceFile = new SourceFile(_('/foo/src/index.js'), 'index contents', null, [], fs); expect(sourceFile.getOriginalLocation(1, 1)).toEqual(null); }); it('should return offset locations in multiple flattened original source files', () => { - const cSource = new SourceFile(_('/foo/src/c.js'), 'bcd123', null, false, [], fs); - const dSource = new SourceFile(_('/foo/src/d.js'), 'aef', null, false, [], fs); + const cSource = new SourceFile(_('/foo/src/c.js'), 'bcd123', null, [], fs); + const dSource = new SourceFile(_('/foo/src/d.js'), 'aef', null, [], fs); - const bSourceMap: RawSourceMap = { - mappings: encode([ - [ - [0, 1, 0, 0], // "a" is in d.js [source 1] - [1, 0, 0, 0], // "bcd" are in c.js [source 0] - [4, 1, 0, 1], // "ef" are in d.js [source 1] - ], - ]), - names: [], - sources: ['c.js', 'd.js'], - version: 3 + const bSourceMap: SourceMapInfo = { + mapPath: null, + map: { + mappings: encode([ + [ + [0, 1, 0, 0], // "a" is in d.js [source 1] + [1, 0, 0, 0], // "bcd" are in c.js [source 0] + [4, 1, 0, 1], // "ef" are in d.js [source 1] + ], + ]), + names: [], + sources: ['c.js', 'd.js'], + version: 3 + }, + origin: ContentOrigin.Provided, }; - const bSource = new SourceFile( - _('/foo/src/b.js'), 'abcdef', bSourceMap, false, [cSource, dSource], fs); + const bSource = + new SourceFile(_('/foo/src/b.js'), 'abcdef', bSourceMap, [cSource, dSource], fs); - const aSourceMap: RawSourceMap = { - mappings: encode([ - [ - [0, 0, 0, 0], [2, 0, 0, 3], // "c" is missing from first line - ], - [ - [4, 0, 0, 2], // second line has new indentation, and starts with "c" - [5, 0, 0, 5], // "f" is here - ], - ]), - names: [], - sources: ['b.js'], - version: 3 + const aSourceMap: SourceMapInfo = { + mapPath: null, + map: { + mappings: encode([ + [ + [0, 0, 0, 0], [2, 0, 0, 3], // "c" is missing from first line + ], + [ + [4, 0, 0, 2], // second line has new indentation, and starts + // with "c" + [5, 0, 0, 5], // "f" is here + ], + ]), + names: [], + sources: ['b.js'], + version: 3 + }, + origin: ContentOrigin.Provided, }; const aSource = - new SourceFile(_('/foo/src/a.js'), 'abde\n cf', aSourceMap, false, [bSource], fs); + new SourceFile(_('/foo/src/a.js'), 'abde\n cf', aSourceMap, [bSource], fs); // Line 0 expect(aSource.getOriginalLocation(0, 0)) // a @@ -597,28 +681,32 @@ runInEachFileSystem(() => { }); it('should return offset locations across multiple lines', () => { - const originalSource = new SourceFile( - _('/foo/src/original.js'), 'abcdef\nghijk\nlmnop', null, false, [], fs); - const generatedSourceMap: RawSourceMap = { - mappings: encode([ - [ - [0, 0, 0, 0], // "ABC" [0,0] => [0,0] - ], - [ - [0, 0, 1, 0], // "GHIJ" [1, 0] => [1,0] - [4, 0, 0, 3], // "DEF" [1, 4] => [0,3] - [7, 0, 1, 4], // "K" [1, 7] => [1,4] - ], - [ - [0, 0, 2, 0], // "LMNOP" [2,0] => [2,0] - ], - ]), - names: [], - sources: ['original.js'], - version: 3 + const originalSource = + new SourceFile(_('/foo/src/original.js'), 'abcdef\nghijk\nlmnop', null, [], fs); + const generatedSourceMap: SourceMapInfo = { + mapPath: null, + map: { + mappings: encode([ + [ + [0, 0, 0, 0], // "ABC" [0,0] => [0,0] + ], + [ + [0, 0, 1, 0], // "GHIJ" [1, 0] => [1,0] + [4, 0, 0, 3], // "DEF" [1, 4] => [0,3] + [7, 0, 1, 4], // "K" [1, 7] => [1,4] + ], + [ + [0, 0, 2, 0], // "LMNOP" [2,0] => [2,0] + ], + ]), + names: [], + sources: ['original.js'], + version: 3 + }, + origin: ContentOrigin.Provided, }; const generatedSource = new SourceFile( - _('/foo/src/generated.js'), 'ABC\nGHIJDEFK\nLMNOP', generatedSourceMap, false, + _('/foo/src/generated.js'), 'ABC\nGHIJDEFK\nLMNOP', generatedSourceMap, [originalSource], fs); // Line 0 @@ -677,8 +765,9 @@ runInEachFileSystem(() => { expect(computeStartOfLinePositions('abc\n')).toEqual([0, 4]); expect(computeStartOfLinePositions('\nabc')).toEqual([0, 1]); expect(computeStartOfLinePositions('abc\ndefg')).toEqual([0, 4]); - expect(computeStartOfLinePositions('abc\r\n')).toEqual([0, 4]); - expect(computeStartOfLinePositions('abc\r\ndefg')).toEqual([0, 4]); + expect(computeStartOfLinePositions('abc\r\n')).toEqual([0, 5]); + expect(computeStartOfLinePositions('abc\r\ndefg')).toEqual([0, 5]); + expect(computeStartOfLinePositions('abc\uD83D\uDE80\ndef🚀\r\n')).toEqual([0, 6, 13]); }); }); }); diff --git a/packages/compiler-cli/src/ngtsc/testing/fake_common/index.ts b/packages/compiler-cli/src/ngtsc/testing/fake_common/index.ts index f2d0c5f257..c32be348b1 100644 --- a/packages/compiler-cli/src/ngtsc/testing/fake_common/index.ts +++ b/packages/compiler-cli/src/ngtsc/testing/fake_common/index.ts @@ -6,11 +6,17 @@ * found in the LICENSE file at https://angular.io/license */ -import {NgIterable, TemplateRef, ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta, ɵɵPipeDefWithMeta} from '@angular/core'; +import {NgIterable, TemplateRef, ɵɵDirectiveDeclaration, ɵɵNgModuleDeclaration, ɵɵPipeDeclaration} from '@angular/core'; export interface NgForOfContext> { $implicit: T; ngForOf: U; + odd: boolean; + event: boolean; + first: boolean; + last: boolean; + count: number; + index: number; } export interface TrackByFunction { @@ -30,7 +36,7 @@ export declare class NgForOf> { ngForTrackBy: TrackByFunction; ngForTemplate: TemplateRef>; - static ɵdir: ɵɵDirectiveDefWithMeta < NgForOf, '[ngFor][ngForOf]', never, { + static ɵdir: ɵɵDirectiveDeclaration < NgForOf, '[ngFor][ngForOf]', never, { 'ngForOf': 'ngForOf'; 'ngForTrackBy': 'ngForTrackBy'; 'ngForTemplate': 'ngForTemplate'; @@ -44,7 +50,7 @@ export declare class NgIf { ngIf: T; ngIfThen: TemplateRef>|null; ngIfElse: TemplateRef>|null; - static ɵdir: ɵɵDirectiveDefWithMeta < NgIf, '[ngIf]', never, { + static ɵdir: ɵɵDirectiveDeclaration < NgIf, '[ngIf]', never, { 'ngIf': 'ngIf'; 'ngIfThen': 'ngIfThen'; 'ngIfElse': 'ngIfElse'; @@ -54,6 +60,19 @@ export declare class NgIf { ctx is NgIfContext>; } +export declare class NgTemplateOutlet { + ngTemplateOutlet: TemplateRef|null; + ngTemplateOutletContext: Object|null; + + static ɵdir: ɵɵDirectiveDeclaration < NgTemplateOutlet, '[ngTemplateOutlet]', never, { + 'ngTemplateOutlet': 'ngTemplateOutlet'; + 'ngTemplateOutletContext': 'ngTemplateOutletContext'; + } + , {}, never > ; + static ngTemplateContextGuard(dir: NgIf, ctx: any): + ctx is NgIfContext>; +} + export declare class DatePipe { transform(value: Date|string|number, format?: string, timezone?: string, locale?: string): string |null; @@ -61,12 +80,11 @@ export declare class DatePipe { transform( value: Date|string|number|null|undefined, format?: string, timezone?: string, locale?: string): string|null; - static ɵpipe: ɵɵPipeDefWithMeta; + static ɵpipe: ɵɵPipeDeclaration; } export declare class CommonModule { - static ɵmod: - ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration< + CommonModule, [typeof NgForOf, typeof NgIf, typeof DatePipe, typeof NgTemplateOutlet], never, + [typeof NgForOf, typeof NgIf, typeof DatePipe, typeof NgTemplateOutlet]>; } diff --git a/packages/compiler-cli/src/ngtsc/testing/fake_core/index.ts b/packages/compiler-cli/src/ngtsc/testing/fake_core/index.ts index 3acc21ab07..35fe9a2575 100644 --- a/packages/compiler-cli/src/ngtsc/testing/fake_core/index.ts +++ b/packages/compiler-cli/src/ngtsc/testing/fake_core/index.ts @@ -70,9 +70,10 @@ export interface SimpleChanges { [propName: string]: any; } -export type ɵɵNgModuleDefWithMeta = any; -export type ɵɵDirectiveDefWithMeta = any; -export type ɵɵPipeDefWithMeta = any; +export type ɵɵNgModuleDeclaration = unknown; +export type ɵɵDirectiveDeclaration = + unknown; +export type ɵɵPipeDeclaration = unknown; export enum ViewEncapsulation { Emulated = 0, @@ -90,7 +91,10 @@ export const CUSTOM_ELEMENTS_SCHEMA: any = false; export const NO_ERRORS_SCHEMA: any = false; export class EventEmitter { - subscribe(generatorOrNext?: any, error?: any, complete?: any): unknown { + subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): + unknown; + subscribe(observerOrNext?: any, error?: any, complete?: any): unknown; + subscribe(observerOrNext?: any, error?: any, complete?: any): unknown { return null; } } @@ -105,4 +109,8 @@ export class NgZone {} export interface PipeTransform { transform(value: any, ...args: any[]): any; -} \ No newline at end of file +} + +export interface OnDestroy { + ngOnDestroy(): void; +} diff --git a/packages/compiler-cli/src/ngtsc/testing/index.ts b/packages/compiler-cli/src/ngtsc/testing/index.ts index fac534ec1d..fdb53352de 100644 --- a/packages/compiler-cli/src/ngtsc/testing/index.ts +++ b/packages/compiler-cli/src/ngtsc/testing/index.ts @@ -7,5 +7,6 @@ */ export * from './src/utils'; export * from './src/cached_source_files'; +export * from './src/compiler_host'; export * from './src/mock_file_loading'; export * from './src/runfile_helpers'; diff --git a/packages/compiler-cli/src/ngtsc/testing/src/mock_file_loading.ts b/packages/compiler-cli/src/ngtsc/testing/src/mock_file_loading.ts index 8915e105c1..63c86058b4 100644 --- a/packages/compiler-cli/src/ngtsc/testing/src/mock_file_loading.ts +++ b/packages/compiler-cli/src/ngtsc/testing/src/mock_file_loading.ts @@ -109,6 +109,11 @@ function loadAngularFolder(): Folder { /** * Load real files from the real file-system into a mock file-system. + * + * Note that this function contains a mix of `FileSystem` calls and NodeJS `fs` calls. + * This is because the function is a bridge between the "real" file-system (via `fs`) and the "mock" + * file-system (via `FileSystem`). + * * @param fs the file-system where the directory is to be loaded. * @param directoryPath the path to the directory we want to load. * @param mockPath the path within the mock file-system where the directory is to be loaded. diff --git a/packages/compiler-cli/src/ngtsc/transform/BUILD.bazel b/packages/compiler-cli/src/ngtsc/transform/BUILD.bazel index ea7ca9453a..1ecafaa34f 100644 --- a/packages/compiler-cli/src/ngtsc/transform/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/transform/BUILD.bazel @@ -13,6 +13,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/diagnostics", "//packages/compiler-cli/src/ngtsc/imports", "//packages/compiler-cli/src/ngtsc/incremental:api", + "//packages/compiler-cli/src/ngtsc/incremental/semantic_graph", "//packages/compiler-cli/src/ngtsc/indexer", "//packages/compiler-cli/src/ngtsc/modulewithproviders", "//packages/compiler-cli/src/ngtsc/perf", diff --git a/packages/compiler-cli/src/ngtsc/transform/index.ts b/packages/compiler-cli/src/ngtsc/transform/index.ts index 6d9d3a61a0..a3fe99dfe6 100644 --- a/packages/compiler-cli/src/ngtsc/transform/index.ts +++ b/packages/compiler-cli/src/ngtsc/transform/index.ts @@ -10,5 +10,5 @@ export * from './src/api'; export {aliasTransformFactory} from './src/alias'; export {ClassRecord, TraitCompiler} from './src/compilation'; export {declarationTransformFactory, DtsTransformRegistry, IvyDeclarationDtsTransform, ReturnTypeTransform} from './src/declaration'; -export {AnalyzedTrait, ErroredTrait, PendingTrait, ResolvedTrait, SkippedTrait, Trait, TraitState} from './src/trait'; +export {AnalyzedTrait, PendingTrait, ResolvedTrait, SkippedTrait, Trait, TraitState} from './src/trait'; export {ivyTransformFactory} from './src/transform'; diff --git a/packages/compiler-cli/src/ngtsc/transform/src/api.ts b/packages/compiler-cli/src/ngtsc/transform/src/api.ts index fe09717966..01054b5f71 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/api.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/api.ts @@ -10,6 +10,7 @@ import {ConstantPool, Expression, Statement, Type} from '@angular/compiler'; import * as ts from 'typescript'; import {Reexport} from '../../imports'; +import {SemanticSymbol} from '../../incremental/semantic_graph'; import {IndexingContext} from '../../indexer'; import {ClassDeclaration, Decorator} from '../../reflection'; import {ImportManager} from '../../translator'; @@ -87,7 +88,7 @@ export enum HandlerFlags { * @param `A` The type of analysis metadata produced by `analyze`. * @param `R` The type of resolution metadata produced by `resolve`. */ -export interface DecoratorHandler { +export interface DecoratorHandler { readonly name: string; /** @@ -128,6 +129,26 @@ export interface DecoratorHandler { analyze(node: ClassDeclaration, metadata: Readonly, handlerFlags?: HandlerFlags): AnalysisOutput; + /** + * React to a change in a resource file by updating the `analysis` or `resolution`, under the + * assumption that nothing in the TypeScript code has changed. + */ + updateResources?(node: ClassDeclaration, analysis: A, resolution: R): void; + + /** + * Produces a `SemanticSymbol` that represents the class, which is registered into the semantic + * dependency graph. The symbol is used in incremental compilations to let the compiler determine + * how a change to the class affects prior emit results. See the `incremental` target's README for + * details on how this works. + * + * The symbol is passed in to `resolve`, where it can be extended with references into other parts + * of the compilation as needed. + * + * Only primary handlers are allowed to have symbols; handlers with `precedence` other than + * `HandlerPrecedence.PRIMARY` must return a `null` symbol. + */ + symbol(node: ClassDeclaration, analysis: Readonly): S; + /** * Post-process the analysis of a decorator/class combination and record any necessary information * in the larger compilation. @@ -153,7 +174,7 @@ export interface DecoratorHandler { * `DecoratorHandler` a chance to leverage information from the whole compilation unit to enhance * the `analysis` before the emit phase. */ - resolve?(node: ClassDeclaration, analysis: Readonly): ResolveResult; + resolve?(node: ClassDeclaration, analysis: Readonly, symbol: S): ResolveResult; typeCheck? (ctx: TypeCheckContext, node: ClassDeclaration, analysis: Readonly, diff --git a/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts b/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts index 01415026c3..d26059c9b6 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts @@ -11,8 +11,9 @@ import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {IncrementalBuild} from '../../incremental/api'; +import {SemanticDepGraphUpdater, SemanticSymbol} from '../../incremental/semantic_graph'; import {IndexingContext} from '../../indexer'; -import {PerfRecorder} from '../../perf'; +import {PerfEvent, PerfRecorder} from '../../perf'; import {ClassDeclaration, DeclarationNode, Decorator, ReflectionHost} from '../../reflection'; import {ProgramTypeCheckAdapter, TypeCheckContext} from '../../typecheck/api'; import {getSourceFile, isExported} from '../../util/src/typescript'; @@ -34,7 +35,7 @@ export interface ClassRecord { /** * All traits which matched on the class. */ - traits: Trait[]; + traits: Trait[]; /** * Meta-diagnostics about the class, which are usually related to whether certain combinations of @@ -82,14 +83,16 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { private reexportMap = new Map>(); - private handlersByName = new Map>(); + private handlersByName = + new Map>(); constructor( - private handlers: DecoratorHandler[], + private handlers: DecoratorHandler[], private reflector: ReflectionHost, private perf: PerfRecorder, private incrementalBuild: IncrementalBuild, private compileNonExportedClasses: boolean, private compilationMode: CompilationMode, - private dtsTransforms: DtsTransformRegistry) { + private dtsTransforms: DtsTransformRegistry, + private semanticDepGraphUpdater: SemanticDepGraphUpdater|null) { for (const handler of handlers) { this.handlersByName.set(handler.name, handler); } @@ -121,6 +124,9 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { this.adopt(priorRecord); } + this.perf.eventCount(PerfEvent.SourceFileReuseAnalysis); + this.perf.eventCount(PerfEvent.TraitReuseAnalysis, priorWork.length); + // Skip the rest of analysis, as this file's prior traits are being reused. return; } @@ -179,17 +185,17 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { for (const priorTrait of priorRecord.traits) { const handler = this.handlersByName.get(priorTrait.handler.name)!; - let trait: Trait = Trait.pending(handler, priorTrait.detected); + let trait: Trait = + Trait.pending(handler, priorTrait.detected); - if (priorTrait.state === TraitState.ANALYZED || priorTrait.state === TraitState.RESOLVED) { - trait = trait.toAnalyzed(priorTrait.analysis); - if (trait.handler.register !== undefined) { + if (priorTrait.state === TraitState.Analyzed || priorTrait.state === TraitState.Resolved) { + const symbol = this.makeSymbolForTrait(handler, record.node, priorTrait.analysis); + trait = trait.toAnalyzed(priorTrait.analysis, priorTrait.analysisDiagnostics, symbol); + if (trait.analysis !== null && trait.handler.register !== undefined) { trait.handler.register(record.node, trait.analysis); } - } else if (priorTrait.state === TraitState.SKIPPED) { + } else if (priorTrait.state === TraitState.Skipped) { trait = trait.toSkipped(); - } else if (priorTrait.state === TraitState.ERRORED) { - trait = trait.toErrored(priorTrait.diagnostics); } record.traits.push(trait); @@ -204,7 +210,7 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { } private scanClassForTraits(clazz: ClassDeclaration): - PendingTrait[]|null { + PendingTrait[]|null { if (!this.compileNonExportedClasses && !isExported(clazz)) { return null; } @@ -215,9 +221,9 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { } protected detectTraits(clazz: ClassDeclaration, decorators: Decorator[]|null): - PendingTrait[]|null { + PendingTrait[]|null { let record: ClassRecord|null = this.recordFor(clazz); - let foundTraits: PendingTrait[] = []; + let foundTraits: PendingTrait[] = []; for (const handler of this.handlers) { const result = handler.detect(clazz, decorators); @@ -295,6 +301,25 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { return foundTraits.length > 0 ? foundTraits : null; } + private makeSymbolForTrait( + handler: DecoratorHandler, + decl: ClassDeclaration, analysis: Readonly|null): SemanticSymbol|null { + if (analysis === null) { + return null; + } + const symbol = handler.symbol(decl, analysis); + if (symbol !== null && this.semanticDepGraphUpdater !== null) { + const isPrimary = handler.precedence === HandlerPrecedence.PRIMARY; + if (!isPrimary) { + throw new Error( + `AssertionError: ${handler.name} returned a symbol but is not a primary handler.`); + } + this.semanticDepGraphUpdater.registerSymbol(symbol); + } + + return symbol; + } + protected analyzeClass(clazz: ClassDeclaration, preanalyzeQueue: Promise[]|null): void { const traits = this.scanClassForTraits(clazz); @@ -314,7 +339,7 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { preanalysis = trait.handler.preanalyze(clazz, trait.detected.metadata) || null; } catch (err) { if (err instanceof FatalDiagnosticError) { - trait.toErrored([err.toDiagnostic()]); + trait.toAnalyzed(null, [err.toDiagnostic()], null); return; } else { throw err; @@ -330,39 +355,33 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { } protected analyzeTrait( - clazz: ClassDeclaration, trait: Trait, + clazz: ClassDeclaration, trait: Trait, flags?: HandlerFlags): void { - if (trait.state !== TraitState.PENDING) { + if (trait.state !== TraitState.Pending) { throw new Error(`Attempt to analyze trait of ${clazz.name.text} in state ${ TraitState[trait.state]} (expected DETECTED)`); } + this.perf.eventCount(PerfEvent.TraitAnalyze); + // Attempt analysis. This could fail with a `FatalDiagnosticError`; catch it if it does. let result: AnalysisOutput; try { result = trait.handler.analyze(clazz, trait.detected.metadata, flags); } catch (err) { if (err instanceof FatalDiagnosticError) { - trait = trait.toErrored([err.toDiagnostic()]); + trait.toAnalyzed(null, [err.toDiagnostic()], null); return; } else { throw err; } } - if (result.diagnostics !== undefined) { - trait = trait.toErrored(result.diagnostics); - } else if (result.analysis !== undefined) { - // Analysis was successful. Trigger registration. - if (trait.handler.register !== undefined) { - trait.handler.register(clazz, result.analysis); - } - - // Successfully analyzed and registered. - trait = trait.toAnalyzed(result.analysis); - } else { - trait = trait.toSkipped(); + const symbol = this.makeSymbolForTrait(trait.handler, clazz, result.analysis ?? null); + if (result.analysis !== undefined && trait.handler.register !== undefined) { + trait.handler.register(clazz, result.analysis); } + trait = trait.toAnalyzed(result.analysis ?? null, result.diagnostics ?? null, symbol); } resolve(): void { @@ -372,43 +391,39 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { for (let trait of record.traits) { const handler = trait.handler; switch (trait.state) { - case TraitState.SKIPPED: - case TraitState.ERRORED: + case TraitState.Skipped: continue; - case TraitState.PENDING: + case TraitState.Pending: throw new Error(`Resolving a trait that hasn't been analyzed: ${clazz.name.text} / ${ Object.getPrototypeOf(trait.handler).constructor.name}`); - case TraitState.RESOLVED: + case TraitState.Resolved: throw new Error(`Resolving an already resolved trait`); } + if (trait.analysis === null) { + // No analysis results, cannot further process this trait. + continue; + } + if (handler.resolve === undefined) { // No resolution of this trait needed - it's considered successful by default. - trait = trait.toResolved(null); + trait = trait.toResolved(null, null); continue; } let result: ResolveResult; try { - result = handler.resolve(clazz, trait.analysis as Readonly); + result = handler.resolve(clazz, trait.analysis as Readonly, trait.symbol); } catch (err) { if (err instanceof FatalDiagnosticError) { - trait = trait.toErrored([err.toDiagnostic()]); + trait = trait.toResolved(null, [err.toDiagnostic()]); continue; } else { throw err; } } - if (result.diagnostics !== undefined && result.diagnostics.length > 0) { - trait = trait.toErrored(result.diagnostics); - } else { - if (result.data !== undefined) { - trait = trait.toResolved(result.data); - } else { - trait = trait.toResolved(null); - } - } + trait = trait.toResolved(result.data ?? null, result.diagnostics ?? null); if (result.reexports !== undefined) { const fileName = clazz.getSourceFile().fileName; @@ -436,12 +451,14 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { for (const clazz of this.fileToClasses.get(sf)!) { const record = this.classes.get(clazz)!; for (const trait of record.traits) { - if (trait.state !== TraitState.RESOLVED) { + if (trait.state !== TraitState.Resolved) { continue; } else if (trait.handler.typeCheck === undefined) { continue; } - trait.handler.typeCheck(ctx, clazz, trait.analysis, trait.resolution); + if (trait.resolution !== null) { + trait.handler.typeCheck(ctx, clazz, trait.analysis, trait.resolution); + } } } } @@ -450,7 +467,7 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { for (const clazz of this.classes.keys()) { const record = this.classes.get(clazz)!; for (const trait of record.traits) { - if (trait.state !== TraitState.RESOLVED) { + if (trait.state !== TraitState.Resolved) { // Skip traits that haven't been resolved successfully. continue; } else if (trait.handler.index === undefined) { @@ -458,11 +475,27 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { continue; } - trait.handler.index(ctx, clazz, trait.analysis, trait.resolution); + if (trait.resolution !== null) { + trait.handler.index(ctx, clazz, trait.analysis, trait.resolution); + } } } } + updateResources(clazz: DeclarationNode): void { + if (!this.reflector.isClass(clazz) || !this.classes.has(clazz)) { + return; + } + const record = this.classes.get(clazz)!; + for (const trait of record.traits) { + if (trait.state !== TraitState.Resolved || trait.handler.updateResources === undefined) { + continue; + } + + trait.handler.updateResources(clazz, trait.analysis, trait.resolution); + } + } + compile(clazz: DeclarationNode, constantPool: ConstantPool): CompileResult[]|null { const original = ts.getOriginalNode(clazz) as typeof clazz; if (!this.reflector.isClass(clazz) || !this.reflector.isClass(original) || @@ -475,23 +508,26 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { let res: CompileResult[] = []; for (const trait of record.traits) { - if (trait.state !== TraitState.RESOLVED) { + if (trait.state !== TraitState.Resolved || trait.analysisDiagnostics !== null || + trait.resolveDiagnostics !== null) { + // Cannot compile a trait that is not resolved, or had any errors in its declaration. continue; } - const compileSpan = this.perf.start('compileClass', original); + // `trait.resolution` is non-null asserted here because TypeScript does not recognize that + // `Readonly` is nullable (as `unknown` itself is nullable) due to the way that + // `Readonly` works. let compileRes: CompileResult|CompileResult[]; if (this.compilationMode === CompilationMode.PARTIAL && trait.handler.compilePartial !== undefined) { - compileRes = trait.handler.compilePartial(clazz, trait.analysis, trait.resolution); + compileRes = trait.handler.compilePartial(clazz, trait.analysis, trait.resolution!); } else { compileRes = - trait.handler.compileFull(clazz, trait.analysis, trait.resolution, constantPool); + trait.handler.compileFull(clazz, trait.analysis, trait.resolution!, constantPool); } const compileMatchRes = compileRes; - this.perf.stop(compileSpan); if (Array.isArray(compileMatchRes)) { for (const result of compileMatchRes) { if (!res.some(r => r.name === result.name)) { @@ -522,7 +558,7 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { const decorators: ts.Decorator[] = []; for (const trait of record.traits) { - if (trait.state !== TraitState.RESOLVED) { + if (trait.state !== TraitState.Resolved) { continue; } @@ -542,8 +578,12 @@ export class TraitCompiler implements ProgramTypeCheckAdapter { diagnostics.push(...record.metaDiagnostics); } for (const trait of record.traits) { - if (trait.state === TraitState.ERRORED) { - diagnostics.push(...trait.diagnostics); + if ((trait.state === TraitState.Analyzed || trait.state === TraitState.Resolved) && + trait.analysisDiagnostics !== null) { + diagnostics.push(...trait.analysisDiagnostics); + } + if (trait.state === TraitState.Resolved && trait.resolveDiagnostics !== null) { + diagnostics.push(...trait.resolveDiagnostics); } } } diff --git a/packages/compiler-cli/src/ngtsc/transform/src/trait.ts b/packages/compiler-cli/src/ngtsc/transform/src/trait.ts index 2d60ffc370..65dc3ce371 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/trait.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/trait.ts @@ -7,34 +7,29 @@ */ import * as ts from 'typescript'; +import {SemanticSymbol} from '../../incremental/semantic_graph'; import {DecoratorHandler, DetectResult} from './api'; export enum TraitState { /** * Pending traits are freshly created and have never been analyzed. */ - PENDING = 0x01, + Pending, /** * Analyzed traits have successfully been analyzed, but are pending resolution. */ - ANALYZED = 0x02, + Analyzed, /** * Resolved traits have successfully been analyzed and resolved and are ready for compilation. */ - RESOLVED = 0x04, - - /** - * Errored traits have failed either analysis or resolution and as a result contain diagnostics - * describing the failure(s). - */ - ERRORED = 0x08, + Resolved, /** * Skipped traits are no longer considered for compilation. */ - SKIPPED = 0x10, + Skipped, } /** @@ -50,22 +45,23 @@ 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 = PendingTrait|SkippedTrait|AnalyzedTrait| - ResolvedTrait|ErroredTrait; +export type Trait = PendingTrait| + SkippedTrait|AnalyzedTrait|ResolvedTrait; /** * The value side of `Trait` exposes a helper to create a `Trait` in a pending state (by delegating * to `TraitImpl`). */ export const Trait = { - pending: (handler: DecoratorHandler, detected: DetectResult): - PendingTrait => TraitImpl.pending(handler, detected), + pending: ( + handler: DecoratorHandler, detected: DetectResult): PendingTrait => + TraitImpl.pending(handler, detected), }; /** * The part of the `Trait` interface that's common to all trait states. */ -export interface TraitBase { +export interface TraitBase { /** * Current state of the trait. * @@ -76,7 +72,7 @@ export interface TraitBase { /** * The `DecoratorHandler` which matched on the class to create this trait. */ - handler: DecoratorHandler; + handler: DecoratorHandler; /** * The detection result (of `handler.detect`) which indicated that this trait applied to the @@ -92,42 +88,22 @@ export interface TraitBase { * * Pending traits have yet to be analyzed in any way. */ -export interface PendingTrait extends TraitBase { - state: TraitState.PENDING; +export interface PendingTrait extends + TraitBase { + state: TraitState.Pending; /** * This pending trait has been successfully analyzed, and should transition to the "analyzed" * state. */ - toAnalyzed(analysis: A): AnalyzedTrait; - - /** - * This trait failed analysis, and should transition to the "errored" state with the resulting - * diagnostics. - */ - toErrored(errors: ts.Diagnostic[]): ErroredTrait; + toAnalyzed(analysis: A|null, diagnostics: ts.Diagnostic[]|null, symbol: S): + AnalyzedTrait; /** * During analysis it was determined that this trait is not eligible for compilation after all, * and should be transitioned to the "skipped" state. */ - toSkipped(): SkippedTrait; -} - -/** - * A trait in the "errored" state. - * - * Errored traits contain `ts.Diagnostic`s indicating any problem(s) with the class. - * - * This is a terminal state. - */ -export interface ErroredTrait extends TraitBase { - state: TraitState.ERRORED; - - /** - * Diagnostics which were produced while attempting to analyze the trait. - */ - diagnostics: ts.Diagnostic[]; + toSkipped(): SkippedTrait; } /** @@ -137,21 +113,9 @@ export interface ErroredTrait extends TraitBase { * * This is a terminal state. */ -export interface SkippedTrait extends TraitBase { - state: TraitState.SKIPPED; -} - -/** - * The part of the `Trait` interface for any trait which has been successfully analyzed. - * - * Mainly, this is used to share the comment on the `analysis` field. - */ -export interface TraitWithAnalysis { - /** - * The results returned by a successful analysis of the given class/`DecoratorHandler` - * combination. - */ - analysis: Readonly; +export interface SkippedTrait extends + TraitBase { + state: TraitState.Skipped; } /** @@ -159,20 +123,27 @@ export interface TraitWithAnalysis { * * Analyzed traits have analysis results available, and are eligible for resolution. */ -export interface AnalyzedTrait extends TraitBase, TraitWithAnalysis { - state: TraitState.ANALYZED; +export interface AnalyzedTrait extends + TraitBase { + state: TraitState.Analyzed; + symbol: S; + + /** + * Analysis results of the given trait (if able to be produced), or `null` if analysis failed + * completely. + */ + analysis: Readonly|null; + + /** + * Any diagnostics that resulted from analysis, or `null` if none. + */ + analysisDiagnostics: ts.Diagnostic[]|null; /** * This analyzed trait has been successfully resolved, and should be transitioned to the * "resolved" state. */ - toResolved(resolution: R): ResolvedTrait; - - /** - * This trait failed resolution, and should transition to the "errored" state with the resulting - * diagnostics. - */ - toErrored(errors: ts.Diagnostic[]): ErroredTrait; + toResolved(resolution: R|null, diagnostics: ts.Diagnostic[]|null): ResolvedTrait; } /** @@ -183,64 +154,80 @@ export interface AnalyzedTrait extends TraitBase, TraitWithAna * * This is a terminal state. */ -export interface ResolvedTrait extends TraitBase, TraitWithAnalysis { - state: TraitState.RESOLVED; +export interface ResolvedTrait extends + TraitBase { + state: TraitState.Resolved; + symbol: S; + + /** + * Resolved traits must have produced valid analysis results. + */ + analysis: Readonly; + + /** + * Analysis may have still resulted in diagnostics. + */ + analysisDiagnostics: ts.Diagnostic[]|null; + + /** + * Diagnostics resulting from resolution are tracked separately from + */ + resolveDiagnostics: ts.Diagnostic[]|null; /** * The results returned by a successful resolution of the given class/`DecoratorHandler` * combination. */ - resolution: Readonly; + resolution: Readonly|null; } /** * An implementation of the `Trait` type which transitions safely between the various * `TraitState`s. */ -class TraitImpl { - state: TraitState = TraitState.PENDING; - handler: DecoratorHandler; +class TraitImpl { + state: TraitState = TraitState.Pending; + handler: DecoratorHandler; detected: DetectResult; analysis: Readonly|null = null; + symbol: S|null = null; resolution: Readonly|null = null; - diagnostics: ts.Diagnostic[]|null = null; + analysisDiagnostics: ts.Diagnostic[]|null = null; + resolveDiagnostics: ts.Diagnostic[]|null = null; - constructor(handler: DecoratorHandler, detected: DetectResult) { + constructor(handler: DecoratorHandler, detected: DetectResult) { this.handler = handler; this.detected = detected; } - toAnalyzed(analysis: A): AnalyzedTrait { + toAnalyzed(analysis: A|null, diagnostics: ts.Diagnostic[]|null, symbol: S): + AnalyzedTrait { // Only pending traits can be analyzed. - this.assertTransitionLegal(TraitState.PENDING, TraitState.ANALYZED); + this.assertTransitionLegal(TraitState.Pending, TraitState.Analyzed); this.analysis = analysis; - this.state = TraitState.ANALYZED; - return this as AnalyzedTrait; + this.analysisDiagnostics = diagnostics; + this.symbol = symbol; + this.state = TraitState.Analyzed; + return this as AnalyzedTrait; } - toErrored(diagnostics: ts.Diagnostic[]): ErroredTrait { - // Pending traits (during analysis) or analyzed traits (during resolution) can produce - // diagnostics and enter an errored state. - this.assertTransitionLegal(TraitState.PENDING | TraitState.ANALYZED, TraitState.RESOLVED); - this.diagnostics = diagnostics; - this.analysis = null; - this.state = TraitState.ERRORED; - return this as ErroredTrait; - } - - toResolved(resolution: R): ResolvedTrait { + toResolved(resolution: R|null, diagnostics: ts.Diagnostic[]|null): ResolvedTrait { // Only analyzed traits can be resolved. - this.assertTransitionLegal(TraitState.ANALYZED, TraitState.RESOLVED); + this.assertTransitionLegal(TraitState.Analyzed, TraitState.Resolved); + if (this.analysis === null) { + throw new Error(`Cannot transition an Analyzed trait with a null analysis to Resolved`); + } this.resolution = resolution; - this.state = TraitState.RESOLVED; - return this as ResolvedTrait; + this.state = TraitState.Resolved; + this.resolveDiagnostics = diagnostics; + return this as ResolvedTrait; } - toSkipped(): SkippedTrait { + toSkipped(): SkippedTrait { // Only pending traits can be skipped. - this.assertTransitionLegal(TraitState.PENDING, TraitState.SKIPPED); - this.state = TraitState.SKIPPED; - return this as SkippedTrait; + this.assertTransitionLegal(TraitState.Pending, TraitState.Skipped); + this.state = TraitState.Skipped; + return this as SkippedTrait; } /** @@ -252,7 +239,7 @@ class TraitImpl { * transitions to take place. Hence, this assertion provides a little extra runtime protection. */ private assertTransitionLegal(allowedState: TraitState, transitionTo: TraitState): void { - if (!(this.state & allowedState)) { + if (!(this.state === allowedState)) { throw new Error(`Assertion failure: cannot transition from ${TraitState[this.state]} to ${ TraitState[transitionTo]}.`); } @@ -261,8 +248,8 @@ class TraitImpl { /** * Construct a new `TraitImpl` in the pending state. */ - static pending(handler: DecoratorHandler, detected: DetectResult): - PendingTrait { - return new TraitImpl(handler, detected) as PendingTrait; + static pending( + handler: DecoratorHandler, detected: DetectResult): PendingTrait { + return new TraitImpl(handler, detected) as PendingTrait; } } diff --git a/packages/compiler-cli/src/ngtsc/transform/src/transform.ts b/packages/compiler-cli/src/ngtsc/transform/src/transform.ts index 96ea10d553..c9a0d7bd10 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/transform.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/transform.ts @@ -10,8 +10,9 @@ import {ConstantPool} from '@angular/compiler'; import * as ts from 'typescript'; import {DefaultImportRecorder, ImportRewriter} from '../../imports'; +import {PerfPhase, PerfRecorder} from '../../perf'; import {Decorator, ReflectionHost} from '../../reflection'; -import {ImportManager, RecordWrappedNodeExprFn, translateExpression, translateStatement} from '../../translator'; +import {ImportManager, RecordWrappedNodeExprFn, translateExpression, translateStatement, TranslatorOptions} from '../../translator'; import {visit, VisitListEntryResult, Visitor} from '../../util/src/visitor'; import {CompileResult} from './api'; @@ -33,14 +34,16 @@ interface FileOverviewMeta { export function ivyTransformFactory( compilation: TraitCompiler, reflector: ReflectionHost, importRewriter: ImportRewriter, - defaultImportRecorder: DefaultImportRecorder, isCore: boolean, + defaultImportRecorder: DefaultImportRecorder, perf: PerfRecorder, isCore: boolean, isClosureCompilerEnabled: boolean): ts.TransformerFactory { const recordWrappedNodeExpr = createRecorderFn(defaultImportRecorder); return (context: ts.TransformationContext): ts.Transformer => { return (file: ts.SourceFile): ts.SourceFile => { - return transformIvySourceFile( - compilation, context, reflector, importRewriter, file, isCore, isClosureCompilerEnabled, - recordWrappedNodeExpr); + return perf.inPhase( + PerfPhase.Compile, + () => transformIvySourceFile( + compilation, context, reflector, importRewriter, file, isCore, + isClosureCompilerEnabled, recordWrappedNodeExpr)); }; }; } @@ -91,15 +94,18 @@ class IvyTransformationVisitor extends Visitor { return {node}; } + const translateOptions: TranslatorOptions = { + recordWrappedNodeExpr: this.recordWrappedNodeExpr, + annotateForClosureCompiler: this.isClosureCompilerEnabled, + }; + // There is at least one field to add. const statements: ts.Statement[] = []; const members = [...node.members]; for (const field of this.classCompilationMap.get(node)!) { // Translate the initializer for the field into TS nodes. - const exprNode = translateExpression( - field.initializer, this.importManager, - {recordWrappedNodeExpr: this.recordWrappedNodeExpr}); + const exprNode = translateExpression(field.initializer, this.importManager, translateOptions); // Create a static property declaration for the new field. const property = ts.createProperty( @@ -116,10 +122,7 @@ class IvyTransformationVisitor extends Visitor { /* hasTrailingNewLine */ false); } - field.statements - .map( - stmt => translateStatement( - stmt, this.importManager, {recordWrappedNodeExpr: this.recordWrappedNodeExpr})) + field.statements.map(stmt => translateStatement(stmt, this.importManager, translateOptions)) .forEach(stmt => statements.push(stmt)); members.push(property); @@ -280,8 +283,9 @@ function transformIvySourceFile( const constants = constantPool.statements.map(stmt => translateStatement(stmt, importManager, { recordWrappedNodeExpr, - downlevelLocalizedStrings: downlevelTranslatedCode, + downlevelTaggedTemplates: downlevelTranslatedCode, downlevelVariableDeclarations: downlevelTranslatedCode, + annotateForClosureCompiler: isClosureCompilerEnabled, })); // Preserve @fileoverview comments required by Closure, since the location might change as a diff --git a/packages/compiler-cli/src/ngtsc/transform/src/utils.ts b/packages/compiler-cli/src/ngtsc/transform/src/utils.ts index ddbdfe7397..57f08b9fa6 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/utils.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/utils.ts @@ -19,15 +19,27 @@ export function addImports( extraStatements: ts.Statement[] = []): ts.SourceFile { // Generate the import statements to prepend. const addedImports = importManager.getAllImports(sf.fileName).map(i => { - const qualifier = ts.createIdentifier(i.qualifier); + const qualifier = ts.createIdentifier(i.qualifier.text); const importClause = ts.createImportClause( /* name */ undefined, /* namedBindings */ ts.createNamespaceImport(qualifier)); - return ts.createImportDeclaration( + const decl = ts.createImportDeclaration( /* decorators */ undefined, /* modifiers */ undefined, /* importClause */ importClause, /* moduleSpecifier */ ts.createLiteral(i.specifier)); + + // Set the qualifier's original TS node to the `ts.ImportDeclaration`. This allows downstream + // transforms such as tsickle to properly process references to this import. + // + // This operation is load-bearing in g3 as some imported modules contain special metadata + // generated by clutz, which tsickle uses to transform imports and references to those imports. + // + // TODO(alxhub): add a test for this when tsickle is updated externally to depend on this + // behavior. + ts.setOriginalNode(i.qualifier, decl); + + return decl; }); // Filter out the existing imports and the source file body. All new statements 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 4d655636b0..d0431fe2e3 100644 --- a/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts +++ b/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts @@ -23,7 +23,7 @@ runInEachFileSystem(() => { beforeEach(() => _ = absoluteFrom); it('should not run decoration handlers against declaration files', () => { - class FakeDecoratorHandler implements DecoratorHandler<{}|null, unknown, unknown> { + class FakeDecoratorHandler implements DecoratorHandler<{}|null, unknown, null, unknown> { name = 'FakeDecoratorHandler'; precedence = HandlerPrecedence.PRIMARY; @@ -33,6 +33,9 @@ runInEachFileSystem(() => { analyze(): AnalysisOutput { throw new Error('analyze should not have been called'); } + symbol(): null { + throw new Error('symbol should not have been called'); + } compileFull(): CompileResult { throw new Error('compile should not have been called'); } @@ -46,7 +49,7 @@ runInEachFileSystem(() => { const reflectionHost = new TypeScriptReflectionHost(checker); const compiler = new TraitCompiler( [new FakeDecoratorHandler()], reflectionHost, NOOP_PERF_RECORDER, NOOP_INCREMENTAL_BUILD, - true, CompilationMode.FULL, new DtsTransformRegistry()); + true, CompilationMode.FULL, new DtsTransformRegistry(), null); const sourceFile = program.getSourceFile('lib.d.ts')!; const analysis = compiler.analyzeSync(sourceFile); @@ -55,7 +58,7 @@ runInEachFileSystem(() => { }); describe('compilation mode', () => { - class PartialDecoratorHandler implements DecoratorHandler<{}, {}, unknown> { + class PartialDecoratorHandler implements DecoratorHandler<{}, {}, null, unknown> { name = 'PartialDecoratorHandler'; precedence = HandlerPrecedence.PRIMARY; @@ -70,6 +73,10 @@ runInEachFileSystem(() => { return {analysis: {}}; } + symbol(): null { + return null; + } + compileFull(): CompileResult { return { name: 'compileFull', @@ -89,7 +96,7 @@ runInEachFileSystem(() => { } } - class FullDecoratorHandler implements DecoratorHandler<{}, {}, unknown> { + class FullDecoratorHandler implements DecoratorHandler<{}, {}, null, unknown> { name = 'FullDecoratorHandler'; precedence = HandlerPrecedence.PRIMARY; @@ -104,6 +111,10 @@ runInEachFileSystem(() => { return {analysis: {}}; } + symbol(): null { + return null; + } + compileFull(): CompileResult { return { name: 'compileFull', @@ -127,7 +138,7 @@ runInEachFileSystem(() => { const compiler = new TraitCompiler( [new PartialDecoratorHandler(), new FullDecoratorHandler()], reflectionHost, NOOP_PERF_RECORDER, NOOP_INCREMENTAL_BUILD, true, CompilationMode.PARTIAL, - new DtsTransformRegistry()); + new DtsTransformRegistry(), null); const sourceFile = program.getSourceFile('test.ts')!; compiler.analyzeSync(sourceFile); compiler.resolve(); @@ -157,7 +168,7 @@ runInEachFileSystem(() => { const compiler = new TraitCompiler( [new PartialDecoratorHandler(), new FullDecoratorHandler()], reflectionHost, NOOP_PERF_RECORDER, NOOP_INCREMENTAL_BUILD, true, CompilationMode.FULL, - new DtsTransformRegistry()); + new DtsTransformRegistry(), null); const sourceFile = program.getSourceFile('test.ts')!; compiler.analyzeSync(sourceFile); compiler.resolve(); diff --git a/packages/compiler-cli/src/ngtsc/translator/index.ts b/packages/compiler-cli/src/ngtsc/translator/index.ts index 8c0dfef49f..b6300ba02a 100644 --- a/packages/compiler-cli/src/ngtsc/translator/index.ts +++ b/packages/compiler-cli/src/ngtsc/translator/index.ts @@ -7,10 +7,10 @@ */ export {AstFactory, BinaryOperator, LeadingComment, ObjectLiteralProperty, SourceMapLocation, SourceMapRange, TemplateElement, TemplateLiteral, UnaryOperator, VariableDeclarationType} from './src/api/ast_factory'; -export {Import, ImportGenerator, NamedImport} from './src/api/import_generator'; +export {ImportGenerator, NamedImport} from './src/api/import_generator'; export {Context} from './src/context'; -export {ImportManager} from './src/import_manager'; +export {Import, ImportManager} from './src/import_manager'; export {ExpressionTranslatorVisitor, RecordWrappedNodeExprFn, TranslatorOptions} from './src/translator'; export {translateType} from './src/type_translator'; -export {attachComments, TypeScriptAstFactory} from './src/typescript_ast_factory'; +export {attachComments, createTemplateMiddle, createTemplateTail, TypeScriptAstFactory} from './src/typescript_ast_factory'; export {translateExpression, translateStatement} from './src/typescript_translator'; diff --git a/packages/compiler-cli/src/ngtsc/translator/src/api/import_generator.ts b/packages/compiler-cli/src/ngtsc/translator/src/api/import_generator.ts index 5e5fd3474d..07a1bd22f5 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/api/import_generator.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/api/import_generator.ts @@ -6,16 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -/** - * Information about an import that has been added to a module. - */ -export interface Import { - /** The name of the module that has been imported. */ - specifier: string; - /** The alias of the imported module. */ - qualifier: string; -} - /** * The symbol name and import namespace of an imported symbol, * which has been registered through the ImportGenerator. diff --git a/packages/compiler-cli/src/ngtsc/translator/src/import_manager.ts b/packages/compiler-cli/src/ngtsc/translator/src/import_manager.ts index f5eeec0f4b..65a083308e 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/import_manager.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/import_manager.ts @@ -7,7 +7,17 @@ */ import * as ts from 'typescript'; import {ImportRewriter, NoopImportRewriter} from '../../imports'; -import {Import, ImportGenerator, NamedImport} from './api/import_generator'; +import {ImportGenerator, NamedImport} from './api/import_generator'; + +/** + * Information about an import that has been added to a module. + */ +export interface Import { + /** The name of the module that has been imported. */ + specifier: string; + /** The `ts.Identifer` by which the imported module is known. */ + qualifier: ts.Identifier; +} export class ImportManager implements ImportGenerator { private specifierToIdentifier = new Map(); @@ -42,11 +52,14 @@ export class ImportManager implements ImportGenerator { } getAllImports(contextPath: string): Import[] { - const imports: {specifier: string, qualifier: string}[] = []; - this.specifierToIdentifier.forEach((qualifier, specifier) => { - specifier = this.rewriter.rewriteSpecifier(specifier, contextPath); - imports.push({specifier, qualifier: qualifier.text}); - }); + const imports: Import[] = []; + for (const [originalSpecifier, qualifier] of this.specifierToIdentifier) { + const specifier = this.rewriter.rewriteSpecifier(originalSpecifier, contextPath); + imports.push({ + specifier, + qualifier, + }); + } return imports; } } diff --git a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts index 2a5c16f61a..f2d71023a6 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import * as o from '@angular/compiler'; +import {createTaggedTemplate} from 'typescript'; import {AstFactory, BinaryOperator, ObjectLiteralProperty, SourceMapRange, TemplateElement, TemplateLiteral, UnaryOperator} from './api/ast_factory'; import {ImportGenerator} from './api/import_generator'; @@ -38,21 +39,22 @@ const BINARY_OPERATORS = new Map([ export type RecordWrappedNodeExprFn = (expr: TExpression) => void; export interface TranslatorOptions { - downlevelLocalizedStrings?: boolean; + downlevelTaggedTemplates?: boolean; downlevelVariableDeclarations?: boolean; recordWrappedNodeExpr?: RecordWrappedNodeExprFn; + annotateForClosureCompiler?: boolean; } export class ExpressionTranslatorVisitor implements o.ExpressionVisitor, o.StatementVisitor { - private downlevelLocalizedStrings: boolean; + private downlevelTaggedTemplates: boolean; private downlevelVariableDeclarations: boolean; private recordWrappedNodeExpr: RecordWrappedNodeExprFn; constructor( private factory: AstFactory, private imports: ImportGenerator, options: TranslatorOptions) { - this.downlevelLocalizedStrings = options.downlevelLocalizedStrings === true; + this.downlevelTaggedTemplates = options.downlevelTaggedTemplates === true; this.downlevelVariableDeclarations = options.downlevelVariableDeclarations === true; this.recordWrappedNodeExpr = options.recordWrappedNodeExpr || (() => {}); } @@ -168,6 +170,19 @@ export class ExpressionTranslatorVisitor implements o.E ast.sourceSpan); } + visitTaggedTemplateExpr(ast: o.TaggedTemplateExpr, context: Context): TExpression { + return this.setSourceMapRange( + this.createTaggedTemplateExpression(ast.tag.visitExpression(this, context), { + elements: ast.template.elements.map(e => createTemplateElement({ + cooked: e.text, + raw: e.rawText, + range: e.sourceSpan ?? ast.sourceSpan, + })), + expressions: ast.template.expressions.map(e => e.visitExpression(this, context)) + }), + ast.sourceSpan); + } + visitInstantiateExpr(ast: o.InstantiateExpr, context: Context): TExpression { return this.factory.createNewExpression( ast.classExpr.visitExpression(this, context), @@ -202,13 +217,14 @@ export class ExpressionTranslatorVisitor implements o.E } const localizeTag = this.factory.createIdentifier('$localize'); + return this.setSourceMapRange( + this.createTaggedTemplateExpression(localizeTag, {elements, expressions}), ast.sourceSpan); + } - // Now choose which implementation to use to actually create the necessary AST nodes. - const localizeCall = this.downlevelLocalizedStrings ? - this.createES5TaggedTemplateFunctionCall(localizeTag, {elements, expressions}) : - this.factory.createTaggedTemplate(localizeTag, {elements, expressions}); - - return this.setSourceMapRange(localizeCall, ast.sourceSpan); + private createTaggedTemplateExpression(tag: TExpression, template: TemplateLiteral): + TExpression { + return this.downlevelTaggedTemplates ? this.createES5TaggedTemplateFunctionCall(tag, template) : + this.factory.createTaggedTemplate(tag, template); } /** diff --git a/packages/compiler-cli/src/ngtsc/translator/src/type_translator.ts b/packages/compiler-cli/src/ngtsc/translator/src/type_translator.ts index 180d221f7a..15d2f9c6bd 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/type_translator.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/type_translator.ts @@ -98,6 +98,10 @@ export class TypeTranslatorVisitor implements o.ExpressionVisitor, o.TypeVisitor throw new Error('Method not implemented.'); } + visitTaggedTemplateExpr(ast: o.TaggedTemplateExpr, context: Context): never { + throw new Error('Method not implemented.'); + } + visitInstantiateExpr(ast: o.InstantiateExpr, context: Context): never { throw new Error('Method not implemented.'); } diff --git a/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts b/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts index a75c03c324..6de1302c31 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/typescript_ast_factory.ts @@ -9,6 +9,21 @@ import * as ts from 'typescript'; import {AstFactory, BinaryOperator, LeadingComment, ObjectLiteralProperty, SourceMapRange, TemplateLiteral, UnaryOperator, VariableDeclarationType} from './api/ast_factory'; +/** + * Different optimizers use different annotations on a function or method call to indicate its pure + * status. + */ +enum PureAnnotation { + /** + * Closure's annotation for purity is `@pureOrBreakMyCode`, but this needs to be in a semantic + * (jsdoc) enabled comment. Thus, the actual comment text for Closure must include the `*` that + * turns a `/*` comment into a `/**` comment, as well as surrounding whitespace. + */ + CLOSURE = '* @pureOrBreakMyCode ', + + TERSER = '@__PURE__', +} + const UNARY_OPERATORS: Record = { '+': ts.SyntaxKind.PlusToken, '-': ts.SyntaxKind.MinusToken, @@ -46,6 +61,8 @@ const VAR_TYPES: Record = { export class TypeScriptAstFactory implements AstFactory { private externalSourceFiles = new Map(); + constructor(private annotateForClosureCompiler: boolean) {} + attachComments = attachComments; createArrayLiteral = ts.createArrayLiteral; @@ -68,7 +85,9 @@ export class TypeScriptAstFactory implements AstFactory = {}): ts.Expression { return expression.visitExpression( new ExpressionTranslatorVisitor( - new TypeScriptAstFactory(), imports, options), + new TypeScriptAstFactory(options.annotateForClosureCompiler === true), imports, options), new Context(false)); } @@ -28,6 +28,6 @@ export function translateStatement( options: TranslatorOptions = {}): ts.Statement { return statement.visitStatement( new ExpressionTranslatorVisitor( - new TypeScriptAstFactory(), imports, options), + new TypeScriptAstFactory(options.annotateForClosureCompiler === true), imports, options), new Context(true)); } diff --git a/packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts b/packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts index a341535f60..f213c2abc2 100644 --- a/packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts +++ b/packages/compiler-cli/src/ngtsc/translator/test/typescript_ast_factory_spec.ts @@ -12,7 +12,7 @@ import {TypeScriptAstFactory} from '../src/typescript_ast_factory'; describe('TypeScriptAstFactory', () => { let factory: TypeScriptAstFactory; - beforeEach(() => factory = new TypeScriptAstFactory()); + beforeEach(() => factory = new TypeScriptAstFactory(/* annotateForClosureCompiler */ false)); describe('attachComments()', () => { it('should add the comments to the given statement', () => { @@ -77,6 +77,14 @@ describe('TypeScriptAstFactory', () => { const call = factory.createCallExpression(callee, [arg1, arg2], true); expect(generate(call)).toEqual('/*@__PURE__*/ foo(42, "moo")'); }); + + it('should create a call marked with a closure-style pure comment if `pure` is true', () => { + factory = new TypeScriptAstFactory(/* annotateForClosureCompiler */ true); + + const {items: [callee, arg1, arg2], generate} = setupExpressions(`foo`, `42`, `"moo"`); + const call = factory.createCallExpression(callee, [arg1, arg2], true); + expect(generate(call)).toEqual('/** @pureOrBreakMyCode */ foo(42, "moo")'); + }); }); describe('createConditional()', () => { diff --git a/packages/compiler-cli/src/ngtsc/tsc_plugin.ts b/packages/compiler-cli/src/ngtsc/tsc_plugin.ts index dcd62d5093..292c472a19 100644 --- a/packages/compiler-cli/src/ngtsc/tsc_plugin.ts +++ b/packages/compiler-cli/src/ngtsc/tsc_plugin.ts @@ -8,12 +8,13 @@ import * as ts from 'typescript'; -import {NgCompiler, NgCompilerHost} from './core'; +import {CompilationTicket, freshCompilationTicket, incrementalFromDriverTicket, NgCompiler, NgCompilerHost} from './core'; import {NgCompilerOptions, UnifiedModulesHost} from './core/api'; import {NodeJSFileSystem, setFileSystem} from './file_system'; import {PatchedProgramIncrementalBuildStrategy} from './incremental'; -import {NOOP_PERF_RECORDER} from './perf'; +import {ActivePerfRecorder, NOOP_PERF_RECORDER, PerfPhase} from './perf'; import {untagAllTsFiles} from './shims'; +import {OptimizeFor} from './typecheck/api'; import {ReusedProgramStrategy} from './typecheck/src/augmented_program'; // The following is needed to fix a the chicken-and-egg issue where the sync (into g3) script will @@ -93,6 +94,13 @@ export class NgTscPlugin implements TscPlugin { ignoreForDiagnostics: Set, ignoreForEmit: Set, } { + // TODO(alxhub): we provide a `PerfRecorder` to the compiler, but because we're not driving the + // compilation, the information captured within it is incomplete, and may not include timings + // for phases such as emit. + // + // Additionally, nothing actually captures the perf results here, so recording stats at all is + // somewhat moot for now :) + const perfRecorder = ActivePerfRecorder.zeroedToNow(); if (this.host === null || this.options === null) { throw new Error('Lifecycle error: setupCompilation() before wrapHost().'); } @@ -100,10 +108,29 @@ export class NgTscPlugin implements TscPlugin { untagAllTsFiles(program); const typeCheckStrategy = new ReusedProgramStrategy( program, this.host, this.options, this.host.shimExtensionPrefixes); - this._compiler = new NgCompiler( - this.host, this.options, program, typeCheckStrategy, - new PatchedProgramIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, - oldProgram, NOOP_PERF_RECORDER); + const strategy = new PatchedProgramIncrementalBuildStrategy(); + const oldDriver = oldProgram !== undefined ? strategy.getIncrementalDriver(oldProgram) : null; + let ticket: CompilationTicket; + + let modifiedResourceFiles: Set|undefined = undefined; + if (this.host.getModifiedResourceFiles !== undefined) { + modifiedResourceFiles = this.host.getModifiedResourceFiles(); + } + if (modifiedResourceFiles === undefined) { + modifiedResourceFiles = new Set(); + } + + if (oldProgram === undefined || oldDriver === null) { + ticket = freshCompilationTicket( + program, this.options, strategy, typeCheckStrategy, perfRecorder, + /* enableTemplateTypeChecker */ false, /* usePoisonedData */ false); + } else { + strategy.toNextBuildStrategy().getIncrementalDriver(oldProgram); + ticket = incrementalFromDriverTicket( + oldProgram, oldDriver, program, this.options, strategy, typeCheckStrategy, + modifiedResourceFiles, perfRecorder, false, false); + } + this._compiler = NgCompiler.fromTicket(ticket, this.host); return { ignoreForDiagnostics: this._compiler.ignoreForDiagnostics, ignoreForEmit: this._compiler.ignoreForEmit, @@ -111,7 +138,10 @@ export class NgTscPlugin implements TscPlugin { } getDiagnostics(file?: ts.SourceFile): ts.Diagnostic[] { - return this.compiler.getDiagnostics(file); + if (file === undefined) { + return this.compiler.getDiagnostics(); + } + return this.compiler.getDiagnosticsForFile(file, OptimizeFor.WholeProgram); } getOptionDiagnostics(): ts.Diagnostic[] { @@ -123,6 +153,9 @@ export class NgTscPlugin implements TscPlugin { } createTransformers(): ts.CustomTransformers { + // The plugin consumer doesn't know about our perf tracing system, so we consider the emit phase + // as beginning now. + this.compiler.perfRecorder.phase(PerfPhase.TypeScriptEmit); return this.compiler.prepareEmit().transformers; } } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/BUILD.bazel b/packages/compiler-cli/src/ngtsc/typecheck/BUILD.bazel index f1553346ed..a258bcd9be 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/typecheck/BUILD.bazel @@ -15,6 +15,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/imports", "//packages/compiler-cli/src/ngtsc/incremental:api", "//packages/compiler-cli/src/ngtsc/metadata", + "//packages/compiler-cli/src/ngtsc/perf", "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/scope", "//packages/compiler-cli/src/ngtsc/shims", diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts index 014fa84036..8f3c5a7d07 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts @@ -260,6 +260,35 @@ export interface TypeCheckingConfig { * literals are cast to `any` when declared. */ strictLiteralTypes: boolean; + + /** + * Whether to use inline type constructors. + * + * If this is `true`, create inline type constructors when required. For example, if a type + * constructor's parameters has private types, it cannot be created normally, so we inline it in + * the directives definition file. + * + * If false, do not create inline type constructors. Fall back to using `any` type for + * constructors that normally require inlining. + * + * This option requires the environment to support inlining. If the environment does not support + * inlining, this must be set to `false`. + */ + useInlineTypeConstructors: boolean; + + /** + * Whether or not to produce diagnostic suggestions in cases where the compiler could have + * inferred a better type for a construct, but was prevented from doing so by the current type + * checking configuration. + * + * For example, if the compiler could have used a template context guard to infer a better type + * for a structural directive's context and `let-` variables, but the user is in + * `fullTemplateTypeCheck` mode and such guards are therefore disabled. + * + * This mode is useful for clients like the Language Service which want to inform users of + * opportunities to improve their own developer experience. + */ + suggestionsForSuboptimalTypeInference: boolean; } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/checker.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/checker.ts index 366d059625..da1d0bb294 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/checker.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/checker.ts @@ -6,13 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, ParseError, TmplAstNode, TmplAstTemplate} from '@angular/compiler'; +import {AST, MethodCall, ParseError, PropertyRead, SafeMethodCall, SafePropertyRead, TmplAstElement, TmplAstNode, TmplAstTemplate} from '@angular/compiler'; +import {AbsoluteFsPath} from '@angular/compiler-cli/src/ngtsc/file_system'; import * as ts from 'typescript'; -import {FullTemplateMapping} from './api'; +import {FullTemplateMapping, TypeCheckableDirectiveMeta} from './api'; import {GlobalCompletion} from './completion'; import {DirectiveInScope, PipeInScope} from './scope'; -import {ShimLocation, Symbol} from './symbols'; +import {DirectiveSymbol, ElementSymbol, ShimLocation, Symbol, TemplateSymbol} from './symbols'; /** * Interface to the Angular Template Type Checker to extract diagnostics and intelligence from the @@ -28,29 +29,11 @@ import {ShimLocation, Symbol} from './symbols'; * query, depending on the method either `null` will be returned or an error will be thrown. */ export interface TemplateTypeChecker { - /** - * Clear all overrides and return the template type-checker to the original input program state. - */ - resetOverrides(): void; - /** * Retrieve the template in use for the given component. - * - * If the template has been overridden via `overrideComponentTemplate`, this will retrieve the - * overridden template nodes. */ getTemplate(component: ts.ClassDeclaration): TmplAstNode[]|null; - /** - * Provide a new template string that will be used in place of the user-defined template when - * checking or operating on the given component. - * - * The compiler will parse this template for diagnostics, and will return any parsing errors if it - * is not valid. If the template cannot be parsed correctly, no override will occur. - */ - overrideComponentTemplate(component: ts.ClassDeclaration, template: string): - {nodes: TmplAstNode[], errors?: ParseError[]}; - /** * Get all `ts.Diagnostic`s currently available for the given `ts.SourceFile`. * @@ -80,6 +63,19 @@ export interface TemplateTypeChecker { */ getDiagnosticsForComponent(component: ts.ClassDeclaration): ts.Diagnostic[]; + /** + * Ensures shims for the whole program are generated. This type of operation would be required by + * operations like "find references" and "refactor/rename" because references may appear in type + * check blocks generated from templates anywhere in the program. + */ + generateAllTypeCheckBlocks(): void; + + /** + * Returns `true` if the given file is in the record of known shims generated by the compiler, + * `false` if we cannot find the file in the shim records. + */ + isTrackedTypeCheckFile(filePath: AbsoluteFsPath): boolean; + /** * Retrieve the top-level node representing the TCB for the given component. * @@ -96,6 +92,8 @@ export interface TemplateTypeChecker { * * @see Symbol */ + getSymbolOfNode(node: TmplAstElement, component: ts.ClassDeclaration): ElementSymbol|null; + getSymbolOfNode(node: TmplAstTemplate, component: ts.ClassDeclaration): TemplateSymbol|null; getSymbolOfNode(node: AST|TmplAstNode, component: ts.ClassDeclaration): Symbol|null; /** @@ -110,6 +108,15 @@ export interface TemplateTypeChecker { getGlobalCompletions(context: TmplAstTemplate|null, component: ts.ClassDeclaration): GlobalCompletion|null; + + /** + * For the given expression node, retrieve a `ShimLocation` that can be used to perform + * autocompletion at that point in the expression, if such a location exists. + */ + getExpressionCompletionLocation( + expr: PropertyRead|SafePropertyRead|MethodCall|SafeMethodCall, + component: ts.ClassDeclaration): ShimLocation|null; + /** * Get basic metadata on the directives which are in scope for the given component. */ @@ -119,6 +126,33 @@ export interface TemplateTypeChecker { * Get basic metadata on the pipes which are in scope for the given component. */ getPipesInScope(component: ts.ClassDeclaration): PipeInScope[]|null; + + /** + * Retrieve a `Map` of potential template element tags, to either the `DirectiveInScope` that + * declares them (if the tag is from a directive/component), or `null` if the tag originates from + * the DOM schema. + */ + getPotentialElementTags(component: ts.ClassDeclaration): Map; + + /** + * Retrieve any potential DOM bindings for the given element. + * + * This returns an array of objects which list both the attribute and property names of each + * binding, which are usually identical but can vary if the HTML attribute name is for example a + * reserved keyword in JS, like the `for` attribute which corresponds to the `htmlFor` property. + */ + getPotentialDomBindings(tagName: string): {attribute: string, property: string}[]; + + /** + * Retrieve the type checking engine's metadata for the given directive class, if available. + */ + getDirectiveMetadata(dir: ts.ClassDeclaration): TypeCheckableDirectiveMeta|null; + + /** + * Reset the `TemplateTypeChecker`'s state for the given class, so that it will be recomputed on + * the next request. + */ + invalidateClass(clazz: ts.ClassDeclaration): void; } /** diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/context.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/context.ts index 9895075cd4..e738aa46b3 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/context.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/context.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ParseSourceFile, R3TargetBinder, SchemaMetadata, TmplAstNode} from '@angular/compiler'; +import {ParseError, ParseSourceFile, R3TargetBinder, SchemaMetadata, TmplAstNode} from '@angular/compiler'; import * as ts from 'typescript'; import {Reference} from '../../imports'; @@ -35,12 +35,14 @@ export interface TypeCheckContext { * @param sourceMapping a `TemplateSourceMapping` instance which describes the origin of the * template text described by the AST. * @param file the `ParseSourceFile` associated with the template. + * @param parseErrors the `ParseError`'s associated with the template. */ addTemplate( ref: Reference>, binder: R3TargetBinder, template: TmplAstNode[], pipes: Map>>, - schemas: SchemaMetadata[], sourceMapping: TemplateSourceMapping, file: ParseSourceFile): void; + schemas: SchemaMetadata[], sourceMapping: TemplateSourceMapping, file: ParseSourceFile, + parseErrors: ParseError[]|null): void; } /** diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/scope.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/scope.ts index 4395c0934b..6593d0a63b 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/scope.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/scope.ts @@ -7,6 +7,7 @@ */ import * as ts from 'typescript'; +import {ClassDeclaration} from '../../reflection'; /** * Metadata on a directive which is available in the scope of a template. @@ -17,6 +18,11 @@ export interface DirectiveInScope { */ tsSymbol: ts.Symbol; + /** + * The module which declares the directive. + */ + ngModule: ClassDeclaration|null; + /** * The selector for the directive or component. */ @@ -26,6 +32,11 @@ export interface DirectiveInScope { * `true` if this directive is a component. */ isComponent: boolean; + + /** + * `true` if this directive is a structural directive. + */ + isStructural: boolean; } /** diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts index af72642041..0640ac103b 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/symbols.ts @@ -24,13 +24,19 @@ export enum SymbolKind { Template, Expression, DomBinding, + Pipe, } /** * A representation of an entity in the `TemplateAst`. */ export type Symbol = InputBindingSymbol|OutputBindingSymbol|ElementSymbol|ReferenceSymbol| - VariableSymbol|ExpressionSymbol|DirectiveSymbol|TemplateSymbol|DomBindingSymbol; + VariableSymbol|ExpressionSymbol|DirectiveSymbol|TemplateSymbol|DomBindingSymbol|PipeSymbol; + +/** + * A `Symbol` which declares a new named entity in the template scope. + */ +export type TemplateDeclarationSymbol = ReferenceSymbol|VariableSymbol; /** Information about where a `ts.Node` can be found in the type check block shim file. */ export interface ShimLocation { @@ -255,9 +261,6 @@ export interface DirectiveSymbol extends DirectiveInScope { /** The location in the shim file for the variable that holds the type of the directive. */ shimLocation: ShimLocation; - - /** The `NgModule` that this directive is declared in or `null` if it could not be determined. */ - ngModule: ClassDeclaration|null; } /** @@ -271,3 +274,37 @@ export interface DomBindingSymbol { /** The symbol for the element or template of the text attribute. */ host: ElementSymbol|TemplateSymbol; } + +/** + * A representation for a call to a pipe's transform method in the TCB. + */ +export interface PipeSymbol { + kind: SymbolKind.Pipe; + + /** The `ts.Type` of the transform node. */ + tsType: ts.Type; + + /** + * The `ts.Symbol` for the transform call. This could be `null` when `checkTypeOfPipes` is set to + * `false` because the transform call would be of the form `(_pipe1 as any).transform()` + */ + tsSymbol: ts.Symbol|null; + + /** The position of the transform call in the template. */ + shimLocation: ShimLocation; + + /** The symbol for the pipe class as an instance that appears in the TCB. */ + classSymbol: ClassSymbol; +} + +/** Represents an instance of a class found in the TCB, i.e. `var _pipe1: MyPipe = null!; */ +export interface ClassSymbol { + /** The `ts.Type` of class. */ + tsType: ts.Type; + + /** The `ts.Symbol` for class. */ + tsSymbol: ts.Symbol; + + /** The position for the variable declaration for the class instance. */ + shimLocation: ShimLocation; +} diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts index 2e77230c88..92ba82b43f 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts @@ -6,17 +6,18 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, ParseError, parseTemplate, TmplAstNode, TmplAstTemplate,} from '@angular/compiler'; +import {AST, CssSelector, DomElementSchemaRegistry, MethodCall, ParseError, parseTemplate, PropertyRead, SafeMethodCall, SafePropertyRead, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstVariable} from '@angular/compiler'; import * as ts from 'typescript'; import {absoluteFrom, absoluteFromSourceFile, AbsoluteFsPath, getSourceFileOrError} from '../../file_system'; -import {ReferenceEmitter} from '../../imports'; +import {Reference, ReferenceEmitter} from '../../imports'; import {IncrementalBuild} from '../../incremental/api'; -import {isNamedClassDeclaration, ReflectionHost} from '../../reflection'; -import {ComponentScopeReader} from '../../scope'; +import {PerfCheckpoint, PerfEvent, PerfPhase, PerfRecorder} from '../../perf'; +import {ClassDeclaration, isNamedClassDeclaration, ReflectionHost} from '../../reflection'; +import {ComponentScopeReader, TypeCheckScopeRegistry} from '../../scope'; import {isShim} from '../../shims'; import {getSourceFileOrNull} from '../../util/src/typescript'; -import {DirectiveInScope, FullTemplateMapping, GlobalCompletion, OptimizeFor, PipeInScope, ProgramTypeCheckAdapter, ShimLocation, Symbol, TemplateId, TemplateTypeChecker, TypeCheckingConfig, TypeCheckingProgramStrategy, UpdateMode} from '../api'; +import {DirectiveInScope, ElementSymbol, FullTemplateMapping, GlobalCompletion, OptimizeFor, PipeInScope, ProgramTypeCheckAdapter, ShimLocation, Symbol, TemplateId, TemplateSymbol, TemplateTypeChecker, TypeCheckableDirectiveMeta, TypeCheckingConfig, TypeCheckingProgramStrategy, UpdateMode} from '../api'; import {TemplateDiagnostic} from '../diagnostics'; import {CompletionEngine} from './completion'; @@ -26,6 +27,8 @@ import {TemplateSourceManager} from './source'; import {findTypeCheckBlock, getTemplateMapping, TemplateSourceResolver} from './tcb_util'; import {SymbolBuilder} from './template_symbol_builder'; + +const REGISTRY = new DomElementSchemaRegistry(); /** * Primary template type-checking engine, which performs type-checking using a * `TypeCheckingProgramStrategy` for type-checking program maintenance, and the @@ -54,13 +57,22 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { /** * Stores directives and pipes that are in scope for each component. * - * Unlike the other caches, the scope of a component is not affected by its template, so this - * cache does not need to be invalidate if the template is overridden. It will be destroyed when - * the `ts.Program` changes and the `TemplateTypeCheckerImpl` as a whole is destroyed and - * replaced. + * Unlike other caches, the scope of a component is not affected by its template. It will be + * destroyed when the `ts.Program` changes and the `TemplateTypeCheckerImpl` as a whole is + * destroyed and replaced. */ private scopeCache = new Map(); + /** + * Stores potential element tags for each component (a union of DOM tags as well as directive + * tags). + * + * Unlike other caches, the scope of a component is not affected by its template. It will be + * destroyed when the `ts.Program` changes and the `TemplateTypeCheckerImpl` as a whole is + * destroyed and replaced. + */ + private elementTagCache = new Map>(); + private isComplete = false; constructor( @@ -70,23 +82,9 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { private refEmitter: ReferenceEmitter, private reflector: ReflectionHost, private compilerHost: Pick, private priorBuild: IncrementalBuild, - private readonly componentScopeReader: ComponentScopeReader) {} - - resetOverrides(): void { - for (const fileRecord of this.state.values()) { - if (fileRecord.templateOverrides !== null) { - fileRecord.templateOverrides = null; - fileRecord.shimData.clear(); - fileRecord.isComplete = false; - } - } - - // Ideally only those components with overridden templates would have their caches invalidated, - // but the `TemplateTypeCheckerImpl` does not track the class for components with overrides. As - // a quick workaround, clear the entire cache instead. - this.completionCache.clear(); - this.symbolBuilderCache.clear(); - } + private readonly componentScopeReader: ComponentScopeReader, + private readonly typeCheckScopeRegistry: TypeCheckScopeRegistry, + private readonly perf: PerfRecorder) {} getTemplate(component: ts.ClassDeclaration): TmplAstNode[]|null { const {data} = this.getLatestComponentState(component); @@ -121,12 +119,12 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { throw new Error(`Error: no shim file in program: ${shimPath}`); } - let tcb: ts.Node|null = findTypeCheckBlock(shimSf, id); + let tcb: ts.Node|null = findTypeCheckBlock(shimSf, id, /*isDiagnosticsRequest*/ false); if (tcb === null) { // Try for an inline block. const inlineSf = getSourceFileOrError(program, sfPath); - tcb = findTypeCheckBlock(inlineSf, id); + tcb = findTypeCheckBlock(inlineSf, id, /*isDiagnosticsRequest*/ false); } let data: TemplateData|null = null; @@ -137,39 +135,8 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { return {data, tcb, shimPath}; } - overrideComponentTemplate(component: ts.ClassDeclaration, template: string): - {nodes: TmplAstNode[], errors?: ParseError[]} { - const {nodes, errors} = parseTemplate(template, 'override.html', { - preserveWhitespaces: true, - leadingTriviaChars: [], - }); - - if (errors !== null) { - return {nodes, errors}; - } - - const filePath = absoluteFromSourceFile(component.getSourceFile()); - - const fileRecord = this.getFileData(filePath); - const id = fileRecord.sourceManager.getTemplateId(component); - - if (fileRecord.templateOverrides === null) { - fileRecord.templateOverrides = new Map(); - } - - fileRecord.templateOverrides.set(id, nodes); - - // Clear data for the shim in question, so it'll be regenerated on the next request. - const shimFile = this.typeCheckingStrategy.shimPathForComponent(component); - fileRecord.shimData.delete(shimFile); - fileRecord.isComplete = false; - this.isComplete = false; - - // Overriding a component's template invalidates its cached results. - this.completionCache.delete(component); - this.symbolBuilderCache.delete(component); - - return {nodes}; + isTrackedTypeCheckFile(filePath: AbsoluteFsPath): boolean { + return this.getFileAndShimRecordsForPath(filePath) !== null; } private getFileAndShimRecordsForPath(shimPath: AbsoluteFsPath): @@ -194,12 +161,17 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { if (shimSf === undefined) { return null; } - return getTemplateMapping(shimSf, positionInShimFile, fileRecord.sourceManager); + return getTemplateMapping( + shimSf, positionInShimFile, fileRecord.sourceManager, /*isDiagnosticsRequest*/ false); + } + + generateAllTypeCheckBlocks() { + this.ensureAllShimsForAllFiles(); } /** - * Retrieve type-checking diagnostics from the given `ts.SourceFile` using the most recent - * type-checking program. + * Retrieve type-checking and template parse diagnostics from the given `ts.SourceFile` using the + * most recent type-checking program. */ getDiagnosticsForFile(sf: ts.SourceFile, optimizeFor: OptimizeFor): ts.Diagnostic[] { switch (optimizeFor) { @@ -211,61 +183,73 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { break; } - const sfPath = absoluteFromSourceFile(sf); - const fileRecord = this.state.get(sfPath)!; + return this.perf.inPhase(PerfPhase.TtcDiagnostics, () => { + const sfPath = absoluteFromSourceFile(sf); + const fileRecord = this.state.get(sfPath)!; - const typeCheckProgram = this.typeCheckingStrategy.getProgram(); + const typeCheckProgram = this.typeCheckingStrategy.getProgram(); - const diagnostics: (ts.Diagnostic|null)[] = []; - if (fileRecord.hasInlines) { - const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath); - diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map( - diag => convertDiagnostic(diag, fileRecord.sourceManager))); - } + const diagnostics: (ts.Diagnostic|null)[] = []; + if (fileRecord.hasInlines) { + const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath); + diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map( + diag => convertDiagnostic(diag, fileRecord.sourceManager))); + } - for (const [shimPath, shimRecord] of fileRecord.shimData) { - const shimSf = getSourceFileOrError(typeCheckProgram, shimPath); - diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map( - diag => convertDiagnostic(diag, fileRecord.sourceManager))); - diagnostics.push(...shimRecord.genesisDiagnostics); - } + for (const [shimPath, shimRecord] of fileRecord.shimData) { + const shimSf = getSourceFileOrError(typeCheckProgram, shimPath); + diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map( + diag => convertDiagnostic(diag, fileRecord.sourceManager))); + diagnostics.push(...shimRecord.genesisDiagnostics); - return diagnostics.filter((diag: ts.Diagnostic|null): diag is ts.Diagnostic => diag !== null); + for (const templateData of shimRecord.templates.values()) { + diagnostics.push(...templateData.templateDiagnostics); + } + } + + return diagnostics.filter((diag: ts.Diagnostic|null): diag is ts.Diagnostic => diag !== null); + }); } getDiagnosticsForComponent(component: ts.ClassDeclaration): ts.Diagnostic[] { this.ensureShimForComponent(component); - const sf = component.getSourceFile(); - const sfPath = absoluteFromSourceFile(sf); - const shimPath = this.typeCheckingStrategy.shimPathForComponent(component); + return this.perf.inPhase(PerfPhase.TtcDiagnostics, () => { + const sf = component.getSourceFile(); + const sfPath = absoluteFromSourceFile(sf); + const shimPath = this.typeCheckingStrategy.shimPathForComponent(component); - const fileRecord = this.getFileData(sfPath); + const fileRecord = this.getFileData(sfPath); - if (!fileRecord.shimData.has(shimPath)) { - return []; - } + if (!fileRecord.shimData.has(shimPath)) { + return []; + } - const templateId = fileRecord.sourceManager.getTemplateId(component); - const shimRecord = fileRecord.shimData.get(shimPath)!; + const templateId = fileRecord.sourceManager.getTemplateId(component); + const shimRecord = fileRecord.shimData.get(shimPath)!; - const typeCheckProgram = this.typeCheckingStrategy.getProgram(); + const typeCheckProgram = this.typeCheckingStrategy.getProgram(); - const diagnostics: (TemplateDiagnostic|null)[] = []; - if (shimRecord.hasInlines) { - const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath); - diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map( + const diagnostics: (TemplateDiagnostic|null)[] = []; + if (shimRecord.hasInlines) { + const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath); + diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map( + diag => convertDiagnostic(diag, fileRecord.sourceManager))); + } + + const shimSf = getSourceFileOrError(typeCheckProgram, shimPath); + diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map( diag => convertDiagnostic(diag, fileRecord.sourceManager))); - } + diagnostics.push(...shimRecord.genesisDiagnostics); - const shimSf = getSourceFileOrError(typeCheckProgram, shimPath); - diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map( - diag => convertDiagnostic(diag, fileRecord.sourceManager))); - diagnostics.push(...shimRecord.genesisDiagnostics); + for (const templateData of shimRecord.templates.values()) { + diagnostics.push(...templateData.templateDiagnostics); + } - return diagnostics.filter( - (diag: TemplateDiagnostic|null): diag is TemplateDiagnostic => - diag !== null && diag.templateId === templateId); + return diagnostics.filter( + (diag: TemplateDiagnostic|null): diag is TemplateDiagnostic => + diag !== null && diag.templateId === templateId); + }); } getTypeCheckBlock(component: ts.ClassDeclaration): ts.Node|null { @@ -278,7 +262,37 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { if (engine === null) { return null; } - return engine.getGlobalCompletions(context); + return this.perf.inPhase( + PerfPhase.TtcAutocompletion, () => engine.getGlobalCompletions(context)); + } + + getExpressionCompletionLocation( + ast: PropertyRead|SafePropertyRead|MethodCall|SafeMethodCall, + component: ts.ClassDeclaration): ShimLocation|null { + const engine = this.getOrCreateCompletionEngine(component); + if (engine === null) { + return null; + } + return this.perf.inPhase( + PerfPhase.TtcAutocompletion, () => engine.getExpressionCompletionLocation(ast)); + } + + invalidateClass(clazz: ts.ClassDeclaration): void { + this.completionCache.delete(clazz); + this.symbolBuilderCache.delete(clazz); + this.scopeCache.delete(clazz); + this.elementTagCache.delete(clazz); + + const sf = clazz.getSourceFile(); + const sfPath = absoluteFromSourceFile(sf); + const shimPath = this.typeCheckingStrategy.shimPathForComponent(clazz); + const fileData = this.getFileData(sfPath); + const templateId = fileData.sourceManager.getTemplateId(clazz); + + fileData.shimData.delete(shimPath); + fileData.isComplete = false; + + this.isComplete = false; } private getOrCreateCompletionEngine(component: ts.ClassDeclaration): CompletionEngine|null { @@ -300,10 +314,6 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { const sfPath = absoluteFromSourceFile(sf); if (this.state.has(sfPath)) { const existingResults = this.state.get(sfPath)!; - if (existingResults.templateOverrides !== null) { - // Cannot adopt prior results if template overrides have been requested. - return; - } if (existingResults.isComplete) { // All data for this file has already been generated, so no need to adopt anything. @@ -312,11 +322,11 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { } const previousResults = this.priorBuild.priorTypeCheckingResultsFor(sf); - if (previousResults === null || !previousResults.isComplete || - previousResults.templateOverrides !== null) { + if (previousResults === null || !previousResults.isComplete) { return; } + this.perf.eventCount(PerfEvent.ReuseTypeCheckFile); this.state.set(sfPath, previousResults); } @@ -325,50 +335,55 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { return; } - const host = new WholeProgramTypeCheckingHost(this); - const ctx = this.newContext(host); + this.perf.inPhase(PerfPhase.TcbGeneration, () => { + const host = new WholeProgramTypeCheckingHost(this); + const ctx = this.newContext(host); - for (const sf of this.originalProgram.getSourceFiles()) { - if (sf.isDeclarationFile || isShim(sf)) { - continue; + for (const sf of this.originalProgram.getSourceFiles()) { + if (sf.isDeclarationFile || isShim(sf)) { + continue; + } + + this.maybeAdoptPriorResultsForFile(sf); + + const sfPath = absoluteFromSourceFile(sf); + const fileData = this.getFileData(sfPath); + if (fileData.isComplete) { + continue; + } + + this.typeCheckAdapter.typeCheck(sf, ctx); + + fileData.isComplete = true; } + this.updateFromContext(ctx); + this.isComplete = true; + }); + } + + private ensureAllShimsForOneFile(sf: ts.SourceFile): void { + this.perf.inPhase(PerfPhase.TcbGeneration, () => { this.maybeAdoptPriorResultsForFile(sf); const sfPath = absoluteFromSourceFile(sf); + const fileData = this.getFileData(sfPath); if (fileData.isComplete) { - continue; + // All data for this file is present and accounted for already. + return; } + const host = + new SingleFileTypeCheckingHost(sfPath, fileData, this.typeCheckingStrategy, this); + const ctx = this.newContext(host); + this.typeCheckAdapter.typeCheck(sf, ctx); fileData.isComplete = true; - } - this.updateFromContext(ctx); - this.isComplete = true; - } - - private ensureAllShimsForOneFile(sf: ts.SourceFile): void { - this.maybeAdoptPriorResultsForFile(sf); - - const sfPath = absoluteFromSourceFile(sf); - - const fileData = this.getFileData(sfPath); - if (fileData.isComplete) { - // All data for this file is present and accounted for already. - return; - } - - const host = new SingleFileTypeCheckingHost(sfPath, fileData, this.typeCheckingStrategy, this); - const ctx = this.newContext(host); - - this.typeCheckAdapter.typeCheck(sf, ctx); - - fileData.isComplete = true; - - this.updateFromContext(ctx); + this.updateFromContext(ctx); + }); } private ensureShimForComponent(component: ts.ClassDeclaration): void { @@ -398,7 +413,7 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { InliningMode.Error; return new TypeCheckContextImpl( this.config, this.compilerHost, this.typeCheckingStrategy, this.refEmitter, this.reflector, - host, inlining); + host, inlining, this.perf); } /** @@ -427,15 +442,20 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { private updateFromContext(ctx: TypeCheckContextImpl): void { const updates = ctx.finalize(); - this.typeCheckingStrategy.updateFiles(updates, UpdateMode.Incremental); - this.priorBuild.recordSuccessfulTypeCheck(this.state); + return this.perf.inPhase(PerfPhase.TcbUpdateProgram, () => { + if (updates.size > 0) { + this.perf.eventCount(PerfEvent.UpdateTypeCheckProgram); + } + this.typeCheckingStrategy.updateFiles(updates, UpdateMode.Incremental); + this.priorBuild.recordSuccessfulTypeCheck(this.state); + this.perf.memory(PerfCheckpoint.TtcUpdateProgram); + }); } getFileData(path: AbsoluteFsPath): FileTypeCheckingData { if (!this.state.has(path)) { this.state.set(path, { hasInlines: false, - templateOverrides: null, sourceManager: new TemplateSourceManager(), isComplete: false, shimData: new Map(), @@ -443,13 +463,14 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { } return this.state.get(path)!; } - + getSymbolOfNode(node: TmplAstTemplate, component: ts.ClassDeclaration): TemplateSymbol|null; + getSymbolOfNode(node: TmplAstElement, component: ts.ClassDeclaration): ElementSymbol|null; getSymbolOfNode(node: AST|TmplAstNode, component: ts.ClassDeclaration): Symbol|null { const builder = this.getOrCreateSymbolBuilder(component); if (builder === null) { return null; } - return builder.getSymbol(node); + return this.perf.inPhase(PerfPhase.TtcSymbol, () => builder.getSymbol(node)); } private getOrCreateSymbolBuilder(component: ts.ClassDeclaration): SymbolBuilder|null { @@ -485,6 +506,51 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { return data.pipes; } + getDirectiveMetadata(dir: ts.ClassDeclaration): TypeCheckableDirectiveMeta|null { + if (!isNamedClassDeclaration(dir)) { + return null; + } + return this.typeCheckScopeRegistry.getTypeCheckDirectiveMetadata(new Reference(dir)); + } + + getPotentialElementTags(component: ts.ClassDeclaration): Map { + if (this.elementTagCache.has(component)) { + return this.elementTagCache.get(component)!; + } + + const tagMap = new Map(); + + for (const tag of REGISTRY.allKnownElementNames()) { + tagMap.set(tag, null); + } + + const scope = this.getScopeData(component); + if (scope !== null) { + for (const directive of scope.directives) { + for (const selector of CssSelector.parse(directive.selector)) { + if (selector.element === null || tagMap.has(selector.element)) { + // Skip this directive if it doesn't match an element tag, or if another directive has + // already been included with the same element name. + continue; + } + + tagMap.set(selector.element, directive); + } + } + } + + this.elementTagCache.set(component, tagMap); + return tagMap; + } + + getPotentialDomBindings(tagName: string): {attribute: string, property: string}[] { + const attributes = REGISTRY.allKnownAttributesOfElement(tagName); + return attributes.map(attribute => ({ + attribute, + property: REGISTRY.getMappedPropName(attribute), + })); + } + private getScopeData(component: ts.ClassDeclaration): ScopeData|null { if (this.scopeCache.has(component)) { return this.scopeCache.get(component)!; @@ -494,18 +560,19 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { throw new Error(`AssertionError: components must have names`); } - const data: ScopeData = { - directives: [], - pipes: [], - }; - const scope = this.componentScopeReader.getScopeForComponent(component); - if (scope === null || scope === 'error') { + if (scope === null) { return null; } + const data: ScopeData = { + directives: [], + pipes: [], + isPoisoned: scope.compilation.isPoisoned, + }; + const typeChecker = this.typeCheckingStrategy.getProgram().getTypeChecker(); - for (const dir of scope.exported.directives) { + for (const dir of scope.compilation.directives) { if (dir.selector === null) { // Skip this directive, it can't be added to a template anyway. continue; @@ -514,14 +581,23 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { if (tsSymbol === undefined) { continue; } + + let ngModule: ClassDeclaration|null = null; + const moduleScopeOfDir = this.componentScopeReader.getScopeForComponent(dir.ref.node); + if (moduleScopeOfDir !== null) { + ngModule = moduleScopeOfDir.ngModule; + } + data.directives.push({ isComponent: dir.isComponent, + isStructural: dir.isStructural, selector: dir.selector, tsSymbol, + ngModule, }); } - for (const pipe of scope.exported.pipes) { + for (const pipe of scope.compilation.pipes) { const tsSymbol = typeChecker.getSymbolAtLocation(pipe.ref.node.name); if (tsSymbol === undefined) { continue; @@ -562,11 +638,6 @@ export interface FileTypeCheckingData { */ sourceManager: TemplateSourceManager; - /** - * Map of template overrides applied to any components in this input file. - */ - templateOverrides: Map|null; - /** * Data for each shim generated from this input file. * @@ -599,20 +670,6 @@ class WholeProgramTypeCheckingHost implements TypeCheckingHost { return !fileData.shimData.has(shimPath); } - getTemplateOverride(sfPath: AbsoluteFsPath, node: ts.ClassDeclaration): TmplAstNode[]|null { - const fileData = this.impl.getFileData(sfPath); - if (fileData.templateOverrides === null) { - return null; - } - - const templateId = fileData.sourceManager.getTemplateId(node); - if (fileData.templateOverrides.has(templateId)) { - return fileData.templateOverrides.get(templateId)!; - } - - return null; - } - recordShimData(sfPath: AbsoluteFsPath, data: ShimTypeCheckingData): void { const fileData = this.impl.getFileData(sfPath); fileData.shimData.set(data.path, data); @@ -657,20 +714,6 @@ class SingleFileTypeCheckingHost implements TypeCheckingHost { return !this.fileData.shimData.has(shimPath); } - getTemplateOverride(sfPath: AbsoluteFsPath, node: ts.ClassDeclaration): TmplAstNode[]|null { - this.assertPath(sfPath); - if (this.fileData.templateOverrides === null) { - return null; - } - - const templateId = this.fileData.sourceManager.getTemplateId(node); - if (this.fileData.templateOverrides.has(templateId)) { - return this.fileData.templateOverrides.get(templateId)!; - } - - return null; - } - recordShimData(sfPath: AbsoluteFsPath, data: ShimTypeCheckingData): void { this.assertPath(sfPath); @@ -731,4 +774,5 @@ class SingleShimTypeCheckingHost extends SingleFileTypeCheckingHost { interface ScopeData { directives: DirectiveInScope[]; pipes: PipeInScope[]; + isPoisoned: boolean; } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts index 10b6c59dda..7e114f4774 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/comments.ts @@ -43,6 +43,7 @@ export enum CommentTriviaType { export enum ExpressionIdentifier { DIRECTIVE = 'DIR', COMPONENT_COMPLETION = 'COMPCOMP', + EVENT_PARAMETER = 'EP', } /** Tags the node with the given expression identifier. */ @@ -53,7 +54,7 @@ export function addExpressionIdentifier(node: ts.Node, identifier: ExpressionIde /* hasTrailingNewLine */ false); } -const IGNORE_MARKER = `${CommentTriviaType.DIAGNOSTIC}:ignore`; +const IGNORE_FOR_DIAGNOSTICS_MARKER = `${CommentTriviaType.DIAGNOSTIC}:ignore`; /** * Tag the `ts.Node` with an indication that any errors arising from the evaluation of the node @@ -61,17 +62,18 @@ const IGNORE_MARKER = `${CommentTriviaType.DIAGNOSTIC}:ignore`; */ export function markIgnoreDiagnostics(node: ts.Node): void { ts.addSyntheticTrailingComment( - node, ts.SyntaxKind.MultiLineCommentTrivia, IGNORE_MARKER, /* hasTrailingNewLine */ false); + node, ts.SyntaxKind.MultiLineCommentTrivia, IGNORE_FOR_DIAGNOSTICS_MARKER, + /* hasTrailingNewLine */ false); } /** Returns true if the node has a marker that indicates diagnostics errors should be ignored. */ -export function hasIgnoreMarker(node: ts.Node, sourceFile: ts.SourceFile): boolean { +export function hasIgnoreForDiagnosticsMarker(node: ts.Node, sourceFile: ts.SourceFile): boolean { return ts.forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => { if (kind !== ts.SyntaxKind.MultiLineCommentTrivia) { return null; } const commentText = sourceFile.text.substring(pos + 2, end - 2); - return commentText === IGNORE_MARKER; + return commentText === IGNORE_FOR_DIAGNOSTICS_MARKER; }) === true; } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/completion.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/completion.ts index 7421ef557a..6157a663d7 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/completion.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/completion.ts @@ -7,10 +7,11 @@ */ import {TmplAstReference, TmplAstTemplate} from '@angular/compiler'; +import {MethodCall, PropertyRead, PropertyWrite, SafeMethodCall, SafePropertyRead} from '@angular/compiler/src/compiler'; import * as ts from 'typescript'; import {AbsoluteFsPath} from '../../file_system'; -import {CompletionKind, GlobalCompletion, ReferenceCompletion, VariableCompletion} from '../api'; +import {CompletionKind, GlobalCompletion, ReferenceCompletion, ShimLocation, VariableCompletion} from '../api'; import {ExpressionIdentifier, findFirstMatchingNode} from './comments'; import {TemplateData} from './context'; @@ -28,6 +29,9 @@ export class CompletionEngine { */ private globalCompletionCache = new Map(); + private expressionCompletionCache = + new Map(); + constructor(private tcb: ts.Node, private data: TemplateData, private shimPath: AbsoluteFsPath) {} /** @@ -79,4 +83,52 @@ export class CompletionEngine { this.globalCompletionCache.set(context, completion); return completion; } + + getExpressionCompletionLocation(expr: PropertyRead|PropertyWrite|MethodCall| + SafeMethodCall): ShimLocation|null { + if (this.expressionCompletionCache.has(expr)) { + return this.expressionCompletionCache.get(expr)!; + } + + // Completion works inside property reads and method calls. + let tsExpr: ts.PropertyAccessExpression|null = null; + if (expr instanceof PropertyRead || expr instanceof MethodCall || + expr instanceof PropertyWrite) { + // Non-safe navigation operations are trivial: `foo.bar` or `foo.bar()` + tsExpr = findFirstMatchingNode(this.tcb, { + filter: ts.isPropertyAccessExpression, + withSpan: expr.nameSpan, + }); + } else if (expr instanceof SafePropertyRead || expr instanceof SafeMethodCall) { + // Safe navigation operations are a little more complex, and involve a ternary. Completion + // happens in the "true" case of the ternary. + const ternaryExpr = findFirstMatchingNode(this.tcb, { + filter: ts.isParenthesizedExpression, + withSpan: expr.sourceSpan, + }); + if (ternaryExpr === null || !ts.isConditionalExpression(ternaryExpr.expression)) { + return null; + } + const whenTrue = ternaryExpr.expression.whenTrue; + + if (expr instanceof SafePropertyRead && ts.isPropertyAccessExpression(whenTrue)) { + tsExpr = whenTrue; + } else if ( + expr instanceof SafeMethodCall && ts.isCallExpression(whenTrue) && + ts.isPropertyAccessExpression(whenTrue.expression)) { + tsExpr = whenTrue.expression; + } + } + + if (tsExpr === null) { + return null; + } + + const res: ShimLocation = { + shimPath: this.shimPath, + positionInShimFile: tsExpr.name.getEnd(), + }; + this.expressionCompletionCache.set(expr, res); + return res; + } } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts index e122c3e94b..b06bdcfce3 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts @@ -6,15 +6,17 @@ * found in the LICENSE file at https://angular.io/license */ -import {BoundTarget, ParseSourceFile, R3TargetBinder, SchemaMetadata, TmplAstNode} from '@angular/compiler'; +import {BoundTarget, ParseError, ParseSourceFile, R3TargetBinder, SchemaMetadata, TemplateParseError, TmplAstNode} from '@angular/compiler'; +import {ErrorCode, ngErrorCode} from '@angular/compiler-cli/src/ngtsc/diagnostics'; import * as ts from 'typescript'; import {absoluteFromSourceFile, AbsoluteFsPath} from '../../file_system'; import {NoopImportRewriter, Reference, ReferenceEmitter} from '../../imports'; +import {PerfEvent, PerfRecorder} from '../../perf'; import {ClassDeclaration, ReflectionHost} from '../../reflection'; import {ImportManager} from '../../translator'; import {ComponentToShimMappingStrategy, TemplateId, TemplateSourceMapping, TypeCheckableDirectiveMeta, TypeCheckBlockMetadata, TypeCheckContext, TypeCheckingConfig, TypeCtorMetadata} from '../api'; -import {TemplateDiagnostic} from '../diagnostics'; +import {makeTemplateDiagnostic, TemplateDiagnostic} from '../diagnostics'; import {DomSchemaChecker, RegistryDomSchemaChecker} from './dom'; import {Environment} from './environment'; @@ -64,6 +66,11 @@ export interface TemplateData { * template nodes. */ boundTarget: BoundTarget; + + /** + * Errors found while parsing them template, which have been converted to diagnostics. + */ + templateDiagnostics: TemplateDiagnostic[]; } /** @@ -132,12 +139,6 @@ export interface TypeCheckingHost { */ shouldCheckComponent(node: ts.ClassDeclaration): boolean; - /** - * Check if the given component has had its template overridden, and retrieve the new template - * nodes if so. - */ - getTemplateOverride(sfPath: AbsoluteFsPath, node: ts.ClassDeclaration): TmplAstNode[]|null; - /** * Report data from a shim generated from the given input file path. */ @@ -179,7 +180,12 @@ export class TypeCheckContextImpl implements TypeCheckContext { private compilerHost: Pick, private componentMappingStrategy: ComponentToShimMappingStrategy, private refEmitter: ReferenceEmitter, private reflector: ReflectionHost, - private host: TypeCheckingHost, private inlining: InliningMode) {} + private host: TypeCheckingHost, private inlining: InliningMode, private perf: PerfRecorder) { + if (inlining === InliningMode.Error && config.useInlineTypeConstructors) { + // We cannot use inlining for type checking since this environment does not support it. + throw new Error(`AssertionError: invalid inlining configuration.`); + } + } /** * A `Map` of `ts.SourceFile`s that the context has seen to the operations (additions of methods @@ -194,48 +200,46 @@ export class TypeCheckContextImpl implements TypeCheckContext { private typeCtorPending = new Set(); /** - * Record a template for the given component `node`, with a `SelectorMatcher` for directive - * matching. + * Register a template to potentially be type-checked. * - * @param node class of the node being recorded. - * @param template AST nodes of the template being recorded. - * @param matcher `SelectorMatcher` which tracks directives that are in scope for this template. + * Implements `TypeCheckContext.addTemplate`. */ addTemplate( ref: Reference>, binder: R3TargetBinder, template: TmplAstNode[], pipes: Map>>, - schemas: SchemaMetadata[], sourceMapping: TemplateSourceMapping, - file: ParseSourceFile): void { + schemas: SchemaMetadata[], sourceMapping: TemplateSourceMapping, file: ParseSourceFile, + parseErrors: ParseError[]|null): void { if (!this.host.shouldCheckComponent(ref.node)) { return; } - const sfPath = absoluteFromSourceFile(ref.node.getSourceFile()); - const overrideTemplate = this.host.getTemplateOverride(sfPath, ref.node); - if (overrideTemplate !== null) { - template = overrideTemplate; - } - - // Accumulate a list of any directives which could not have type constructors generated due to - // unsupported inlining operations. - let missingInlines: ClassDeclaration[] = []; - const fileData = this.dataForFile(ref.node.getSourceFile()); const shimData = this.pendingShimForComponent(ref.node); + const templateId = fileData.sourceManager.getTemplateId(ref.node); + + const templateDiagnostics: TemplateDiagnostic[] = []; + + if (parseErrors !== null) { + templateDiagnostics.push( + ...this.getTemplateDiagnostics(parseErrors, templateId, sourceMapping)); + } + const boundTarget = binder.bind({template}); - // Get all of the directives used in the template and record type constructors for all of them. - for (const dir of boundTarget.getUsedDirectives()) { - const dirRef = dir.ref as Reference>; - const dirNode = dirRef.node; + if (this.inlining === InliningMode.InlineOps) { + // Get all of the directives used in the template and record inline type constructors when + // required. + for (const dir of boundTarget.getUsedDirectives()) { + const dirRef = dir.ref as Reference>; + const dirNode = dirRef.node; - if (dir.isGeneric && requiresInlineTypeCtor(dirNode, this.reflector)) { - if (this.inlining === InliningMode.Error) { - missingInlines.push(dirNode); + if (!dir.isGeneric || !requiresInlineTypeCtor(dirNode, this.reflector)) { + // inlining not required continue; } - // Add a type constructor operation for the directive. + + // Add an inline type constructor operation for the directive. this.addInlineTypeCtor(fileData, dirNode.getSourceFile(), dirRef, { fnName: 'ngTypeCtor', // The constructor should have a body if the directive comes from a .ts file, but not if @@ -251,30 +255,26 @@ export class TypeCheckContextImpl implements TypeCheckContext { }); } } - const templateId = fileData.sourceManager.getTemplateId(ref.node); + shimData.templates.set(templateId, { template, boundTarget, + templateDiagnostics, }); - const tcbRequiresInline = requiresInlineTypeCheckBlock(ref.node); + const tcbRequiresInline = requiresInlineTypeCheckBlock(ref.node, pipes); // If inlining is not supported, but is required for either the TCB or one of its directive // dependencies, then exit here with an error. - if (this.inlining === InliningMode.Error && (tcbRequiresInline || missingInlines.length > 0)) { + if (this.inlining === InliningMode.Error && tcbRequiresInline) { // This template cannot be supported because the underlying strategy does not support inlining // and inlining would be required. // Record diagnostics to indicate the issues with this template. - if (tcbRequiresInline) { - shimData.oobRecorder.requiresInlineTcb(templateId, ref.node); - } - - if (missingInlines.length > 0) { - shimData.oobRecorder.requiresInlineTypeConstructors(templateId, ref.node, missingInlines); - } + shimData.oobRecorder.requiresInlineTcb(templateId, ref.node); // Checking this template would be unsupported, so don't try. + this.perf.eventCount(PerfEvent.SkipGenerateTcbNoInline); return; } @@ -284,6 +284,7 @@ export class TypeCheckContextImpl implements TypeCheckContext { pipes, schemas, }; + this.perf.eventCount(PerfEvent.GenerateTcb); if (tcbRequiresInline) { // This class didn't meet the requirements for external type checking, so generate an inline // TCB for the class. @@ -354,7 +355,7 @@ export class TypeCheckContextImpl implements TypeCheckContext { // Write out the imports that need to be added to the beginning of the file. let imports = importManager.getAllImports(sf.fileName) - .map(i => `import * as ${i.qualifier} from '${i.specifier}';`) + .map(i => `import * as ${i.qualifier.text} from '${i.specifier}';`) .join('\n'); code = imports + '\n' + code; @@ -384,7 +385,8 @@ export class TypeCheckContextImpl implements TypeCheckContext { path: pendingShimData.file.fileName, templates: pendingShimData.templates, }); - updates.set(pendingShimData.file.fileName, pendingShimData.file.render()); + updates.set( + pendingShimData.file.fileName, pendingShimData.file.render(false /* removeComments */)); } } @@ -435,6 +437,26 @@ export class TypeCheckContextImpl implements TypeCheckContext { return this.fileMap.get(sfPath)!; } + + private getTemplateDiagnostics( + parseErrors: ParseError[], templateId: TemplateId, + sourceMapping: TemplateSourceMapping): TemplateDiagnostic[] { + return parseErrors.map(error => { + const span = error.span; + + if (span.start.offset === span.end.offset) { + // Template errors can contain zero-length spans, if the error occurs at a single point. + // However, TypeScript does not handle displaying a zero-length diagnostic very well, so + // increase the ending offset by 1 for such errors, to ensure the position is shown in the + // diagnostic. + span.end.offset++; + } + + return makeTemplateDiagnostic( + templateId, sourceMapping, span, ts.DiagnosticCategory.Error, + ngErrorCode(ErrorCode.TEMPLATE_PARSE_ERROR), error.msg); + }); + } } /** diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts index 02a22ef292..cd828872b6 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts @@ -91,7 +91,8 @@ export function translateDiagnostic( if (diagnostic.file === undefined || diagnostic.start === undefined) { return null; } - const fullMapping = getTemplateMapping(diagnostic.file, diagnostic.start, resolver); + const fullMapping = getTemplateMapping( + diagnostic.file, diagnostic.start, resolver, /*isDiagnosticsRequest*/ true); if (fullMapping === null) { return null; } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts index d0c25504ba..2d2fdb734d 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts @@ -41,12 +41,9 @@ export class Environment { private pipeInsts = new Map(); protected pipeInstStatements: ts.Statement[] = []; - private outputHelperIdent: ts.Identifier|null = null; - protected helperStatements: ts.Statement[] = []; - constructor( readonly config: TypeCheckingConfig, protected importManager: ImportManager, - private refEmitter: ReferenceEmitter, private reflector: ReflectionHost, + private refEmitter: ReferenceEmitter, readonly reflector: ReflectionHost, protected contextFile: ts.SourceFile) {} /** @@ -113,93 +110,6 @@ export class Environment { return pipeInstId; } - /** - * Declares a helper function to be able to cast directive outputs of type `EventEmitter` to - * have an accurate `subscribe()` method that properly carries over the generic type `T` into the - * listener function passed as argument to `subscribe`. This is done to work around a typing - * deficiency in `EventEmitter.subscribe`, where the listener function is typed as any. - */ - declareOutputHelper(): ts.Expression { - if (this.outputHelperIdent !== null) { - return this.outputHelperIdent; - } - - const outputHelperIdent = ts.createIdentifier('_outputHelper'); - const genericTypeDecl = ts.createTypeParameterDeclaration('T'); - const genericTypeRef = ts.createTypeReferenceNode('T', /* typeParameters */ undefined); - - const eventEmitter = this.referenceExternalType( - '@angular/core', 'EventEmitter', [new ExpressionType(new WrappedNodeExpr(genericTypeRef))]); - - // Declare a type that has a `subscribe` method that carries over type `T` as parameter - // into the callback. The below code generates the following type literal: - // `{subscribe(cb: (event: T) => any): void;}` - const observableLike = ts.createTypeLiteralNode([ts.createMethodSignature( - /* typeParameters */ undefined, - /* parameters */[ts.createParameter( - /* decorators */ undefined, - /* modifiers */ undefined, - /* dotDotDotToken */ undefined, - /* name */ 'cb', - /* questionToken */ undefined, - /* type */ - ts.createFunctionTypeNode( - /* typeParameters */ undefined, - /* parameters */[ts.createParameter( - /* decorators */ undefined, - /* modifiers */ undefined, - /* dotDotDotToken */ undefined, - /* name */ 'event', - /* questionToken */ undefined, - /* type */ genericTypeRef)], - /* type */ ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)))], - /* type */ ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword), - /* name */ 'subscribe', - /* questionToken */ undefined)]); - - // Declares the first signature of `_outputHelper` that matches arguments of type - // `EventEmitter`, to convert them into `observableLike` defined above. The following - // statement is generated: - // `declare function _outputHelper(output: EventEmitter): observableLike;` - this.helperStatements.push(ts.createFunctionDeclaration( - /* decorators */ undefined, - /* modifiers */[ts.createModifier(ts.SyntaxKind.DeclareKeyword)], - /* asteriskToken */ undefined, - /* name */ outputHelperIdent, - /* typeParameters */[genericTypeDecl], - /* parameters */[ts.createParameter( - /* decorators */ undefined, - /* modifiers */ undefined, - /* dotDotDotToken */ undefined, - /* name */ 'output', - /* questionToken */ undefined, - /* type */ eventEmitter)], - /* type */ observableLike, - /* body */ undefined)); - - // Declares the second signature of `_outputHelper` that matches all other argument types, - // i.e. ensures type identity for output types other than `EventEmitter`. This corresponds - // with the following statement: - // `declare function _outputHelper(output: T): T;` - this.helperStatements.push(ts.createFunctionDeclaration( - /* decorators */ undefined, - /* modifiers */[ts.createModifier(ts.SyntaxKind.DeclareKeyword)], - /* asteriskToken */ undefined, - /* name */ outputHelperIdent, - /* typeParameters */[genericTypeDecl], - /* parameters */[ts.createParameter( - /* decorators */ undefined, - /* modifiers */ undefined, - /* dotDotDotToken */ undefined, - /* name */ 'output', - /* questionToken */ undefined, - /* type */ genericTypeRef)], - /* type */ genericTypeRef, - /* body */ undefined)); - - return this.outputHelperIdent = outputHelperIdent; - } - /** * Generate a `ts.Expression` that references the given node. * @@ -213,7 +123,7 @@ export class Environment { const ngExpr = this.refEmitter.emit(ref, this.contextFile, ImportFlags.NoAliasing); // Use `translateExpression` to convert the `Expression` into a `ts.Expression`. - return translateExpression(ngExpr, this.importManager); + return translateExpression(ngExpr.expression, this.importManager); } /** @@ -227,7 +137,7 @@ export class Environment { // Create an `ExpressionType` from the `Expression` and translate it via `translateType`. // TODO(alxhub): support references to types with generic arguments in a clean way. - return translateType(new ExpressionType(ngExpr), this.importManager); + return translateType(new ExpressionType(ngExpr.expression), this.importManager); } private emitTypeParameters(declaration: ClassDeclaration): @@ -250,7 +160,6 @@ export class Environment { getPreludeStatements(): ts.Statement[] { return [ - ...this.helperStatements, ...this.pipeInstStatements, ...this.typeCtorStatements, ]; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts index f4360a183c..1ecb5061b8 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts @@ -8,6 +8,7 @@ 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, ThisReceiver, Unary} from '@angular/compiler'; import * as ts from 'typescript'; + import {TypeCheckingConfig} from '../api'; import {addParseSpanInfo, wrapForDiagnostics, wrapForTypeChecker} from './diagnostics'; @@ -164,7 +165,7 @@ class AstTranslator implements AstVisitor { const left = ts.createElementAccess(receiver, this.translate(ast.key)); // TODO(joost): annotate `left` with the span of the element access, which is not currently // available on `ast`. - const right = this.translate(ast.value); + const right = wrapForTypeChecker(this.translate(ast.value)); const node = wrapForDiagnostics(ts.createBinary(left, ts.SyntaxKind.EqualsToken, right)); addParseSpanInfo(node, ast.sourceSpan); return node; @@ -255,14 +256,11 @@ class AstTranslator implements AstVisitor { // ^ nameSpan const leftWithPath = wrapForDiagnostics(left); addParseSpanInfo(leftWithPath, ast.sourceSpan); - let right = this.translate(ast.value); // The right needs to be wrapped in parens as well or we cannot accurately match its // span to just the RHS. For example, the span in `e = $event /*0,10*/` is ambiguous. // It could refer to either the whole binary expression or just the RHS. // We should instead generate `e = ($event /*0,10*/)` so we know the span 0,10 matches RHS. - if (!ts.isParenthesizedExpression(right)) { - right = wrapForTypeChecker(right); - } + const right = wrapForTypeChecker(this.translate(ast.value)); const node = wrapForDiagnostics(ts.createBinary(leftWithPath, ts.SyntaxKind.EqualsToken, right)); addParseSpanInfo(node, ast.sourceSpan); @@ -309,6 +307,7 @@ class AstTranslator implements AstVisitor { // "a?.b" becomes (null as any ? a!.b : undefined) // The type of this expression is (typeof a!.b) | undefined, which is exactly as desired. const expr = ts.createPropertyAccess(ts.createNonNullExpression(receiver), ast.name); + addParseSpanInfo(expr, ast.nameSpan); node = ts.createParen(ts.createConditional(NULL_AS_ANY, expr, UNDEFINED)); } else if (VeSafeLhsInferenceBugDetector.veWillInferAnyFor(ast)) { // Emulate a View Engine bug where 'any' is inferred for the left-hand side of the safe @@ -322,6 +321,7 @@ class AstTranslator implements AstVisitor { // result is still inferred as `any`. // "a?.b" becomes (a!.b as any) const expr = ts.createPropertyAccess(ts.createNonNullExpression(receiver), ast.name); + addParseSpanInfo(expr, ast.nameSpan); node = tsCastToAny(expr); } addParseSpanInfo(node, ast.sourceSpan); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/oob.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/oob.ts index aaec8d7927..446debb4ee 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/oob.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/oob.ts @@ -68,6 +68,12 @@ export interface OutOfBandDiagnosticRecorder { requiresInlineTypeConstructors( templateId: TemplateId, node: ClassDeclaration, directives: ClassDeclaration[]): void; + + /** + * Report a warning when structural directives support context guards, but the current + * type-checking configuration prohibits their usage. + */ + suboptimalTypeInference(templateId: TemplateId, variables: TmplAstVariable[]): void; } export class OutOfBandDiagnosticRecorderImpl implements OutOfBandDiagnosticRecorder { @@ -174,6 +180,37 @@ export class OutOfBandDiagnosticRecorderImpl implements OutOfBandDiagnosticRecor directives.map( dir => makeRelatedInformation(dir.name, `Requires an inline type constructor.`)))); } + + suboptimalTypeInference(templateId: TemplateId, variables: TmplAstVariable[]): void { + const mapping = this.resolver.getSourceMapping(templateId); + + // Select one of the template variables that's most suitable for reporting the diagnostic. Any + // variable will do, but prefer one bound to the context's $implicit if present. + let diagnosticVar: TmplAstVariable|null = null; + for (const variable of variables) { + if (diagnosticVar === null || (variable.value === '' || variable.value === '$implicit')) { + diagnosticVar = variable; + } + } + if (diagnosticVar === null) { + // There is no variable on which to report the diagnostic. + return; + } + + let varIdentification = `'${diagnosticVar.name}'`; + if (variables.length === 2) { + varIdentification += ` (and 1 other)`; + } else if (variables.length > 2) { + varIdentification += ` (and ${variables.length - 1} others)`; + } + const message = + `This structural directive supports advanced type inference, but the current compiler configuration prevents its usage. The variable ${ + varIdentification} will have type 'any' as a result.\n\nConsider enabling the 'strictTemplates' option in your tsconfig.json for better type inference within this template.`; + + this._diagnostics.push(makeTemplateDiagnostic( + templateId, mapping, diagnosticVar.keySpan, ts.DiagnosticCategory.Suggestion, + ngErrorCode(ErrorCode.SUGGEST_SUBOPTIMAL_TYPE_INFERENCE), message)); + } } function makeInlineDiagnostic( diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/tcb_util.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/tcb_util.ts index 16ac8b874f..43b944d4e2 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/tcb_util.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/tcb_util.ts @@ -10,10 +10,11 @@ import {AbsoluteSourceSpan, ParseSourceSpan} from '@angular/compiler'; import {ClassDeclaration} from '@angular/compiler-cli/src/ngtsc/reflection'; import * as ts from 'typescript'; +import {Reference} from '../../imports'; import {getTokenAtPosition} from '../../util/src/typescript'; import {FullTemplateMapping, SourceLocation, TemplateId, TemplateSourceMapping} from '../api'; -import {hasIgnoreMarker, readSpanComment} from './comments'; +import {hasIgnoreForDiagnosticsMarker, readSpanComment} from './comments'; import {checkIfClassIsExported, checkIfGenericTypesAreUnbound} from './ts_util'; /** @@ -37,7 +38,9 @@ export interface TemplateSourceResolver { toParseSourceSpan(id: TemplateId, span: AbsoluteSourceSpan): ParseSourceSpan|null; } -export function requiresInlineTypeCheckBlock(node: ClassDeclaration): boolean { +export function requiresInlineTypeCheckBlock( + node: ClassDeclaration, + usedPipes: Map>>): boolean { // In order to qualify for a declared TCB (not inline) two conditions must be met: // 1) the class must be exported // 2) it must not have constrained generic types @@ -47,6 +50,11 @@ export function requiresInlineTypeCheckBlock(node: ClassDeclaration !checkIfClassIsExported(pipeRef.node))) { + // If one of the pipes used by the component is not exported, a non-inline TCB will not be able + // to import it, so this requires an inline TCB. + return true; } else { return false; } @@ -54,10 +62,10 @@ export function requiresInlineTypeCheckBlock(node: ClassDeclaration 0 && + this.tcb.env.config.suggestionsForSuboptimalTypeInference) { + // The compiler could have inferred a better type for the variables in this template, + // but was prevented from doing so by the type-checking configuration. Issue a warning + // diagnostic. + this.tcb.oobRecorder.suboptimalTypeInference(this.tcb.id, this.template.variables); + } } } } @@ -357,18 +367,13 @@ class TcbTextInterpolationOp extends TcbOp { } /** - * A `TcbOp` which constructs an instance of a directive _without_ setting any of its inputs. Inputs - * are later set in the `TcbDirectiveInputsOp`. Type checking was found to be faster when done in - * this way as opposed to `TcbDirectiveCtorOp` which is only necessary when the directive is - * generic. - * - * Executing this operation returns a reference to the directive instance variable with its inferred - * type. + * A `TcbOp` which constructs an instance of a directive. For generic directives, generic + * parameters are set to `any` type. */ -class TcbDirectiveTypeOp extends TcbOp { +abstract class TcbDirectiveTypeOpBase extends TcbOp { constructor( - private tcb: Context, private scope: Scope, private node: TmplAstTemplate|TmplAstElement, - private dir: TypeCheckableDirectiveMeta) { + protected tcb: Context, protected scope: Scope, + protected node: TmplAstTemplate|TmplAstElement, protected dir: TypeCheckableDirectiveMeta) { super(); } @@ -380,9 +385,24 @@ class TcbDirectiveTypeOp extends TcbOp { } execute(): ts.Identifier { - const id = this.tcb.allocateId(); + const dirRef = this.dir.ref as Reference>; - const type = this.tcb.env.referenceType(this.dir.ref); + const rawType = this.tcb.env.referenceType(this.dir.ref); + + let type: ts.TypeNode; + if (this.dir.isGeneric === false || dirRef.node.typeParameters === undefined) { + type = rawType; + } else { + if (!ts.isTypeReferenceNode(rawType)) { + throw new Error( + `Expected TypeReferenceNode when referencing the type for ${this.dir.ref.debugName}`); + } + const typeArguments = dirRef.node.typeParameters.map( + () => ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)); + type = ts.factory.createTypeReferenceNode(rawType.typeName, typeArguments); + } + + const id = this.tcb.allocateId(); addExpressionIdentifier(type, ExpressionIdentifier.DIRECTIVE); addParseSpanInfo(type, this.node.startSourceSpan || this.node.sourceSpan); this.scope.addStatement(tsDeclareVariable(id, type)); @@ -390,6 +410,49 @@ class TcbDirectiveTypeOp extends TcbOp { } } +/** + * A `TcbOp` which constructs an instance of a non-generic directive _without_ setting any of its + * inputs. Inputs are later set in the `TcbDirectiveInputsOp`. Type checking was found to be + * faster when done in this way as opposed to `TcbDirectiveCtorOp` which is only necessary when the + * directive is generic. + * + * Executing this operation returns a reference to the directive instance variable with its inferred + * type. + */ +class TcbNonGenericDirectiveTypeOp extends TcbDirectiveTypeOpBase { + /** + * Creates a variable declaration for this op's directive of the argument type. Returns the id of + * the newly created variable. + */ + execute(): ts.Identifier { + const dirRef = this.dir.ref as Reference>; + if (this.dir.isGeneric) { + throw new Error(`Assertion Error: expected ${dirRef.debugName} not to be generic.`); + } + return super.execute(); + } +} + +/** + * A `TcbOp` which constructs an instance of a generic directive with its generic parameters set + * to `any` type. This op is like `TcbDirectiveTypeOp`, except that generic parameters are set to + * `any` type. This is used for situations where we want to avoid inlining. + * + * Executing this operation returns a reference to the directive instance variable with its generic + * type parameters set to `any`. + */ +class TcbGenericDirectiveTypeWithAnyParamsOp extends TcbDirectiveTypeOpBase { + execute(): ts.Identifier { + const dirRef = this.dir.ref as Reference>; + if (dirRef.node.typeParameters === undefined) { + throw new Error(`Assertion Error: expected typeParameters when creating a declaration for ${ + dirRef.debugName}`); + } + + return super.execute(); + } +} + /** * A `TcbOp` which creates a variable for a local ref in a template. * The initializer for the variable is the variable expression for the directive, template, or @@ -512,6 +575,11 @@ class TcbDirectiveCtorOp extends TcbOp { const inputs = getBoundInputs(this.dir, this.node, this.tcb); for (const input of inputs) { + // Skip text attributes if configured to do so. + if (!this.tcb.env.config.checkTypeOfAttributes && + input.attribute instanceof TmplAstTextAttribute) { + continue; + } for (const fieldName of input.fieldNames) { // Skip the field if an attribute has already been bound to it; we can't have a duplicate // key in the type constructor call. @@ -654,6 +722,12 @@ class TcbDirectiveInputsOp extends TcbOp { } addParseSpanInfo(assignment, input.attribute.sourceSpan); + // Ignore diagnostics for text attributes if configured to do so. + if (!this.tcb.env.config.checkTypeOfAttributes && + input.attribute instanceof TmplAstTextAttribute) { + markIgnoreDiagnostics(assignment); + } + this.scope.addStatement(ts.createExpressionStatement(assignment)); } @@ -854,33 +928,28 @@ export class TcbDirectiveOutputsOp extends TcbOp { // TODO(alxhub): consider supporting multiple fields with the same property name for outputs. const field = outputs.getByBindingPropertyName(output.name)![0].classPropertyName; + if (dirId === null) { + dirId = this.scope.resolve(this.node, this.dir); + } + const outputField = ts.createElementAccess(dirId, ts.createStringLiteral(field)); + addParseSpanInfo(outputField, output.keySpan); if (this.tcb.env.config.checkTypeOfOutputEvents) { // For strict checking of directive events, generate a call to the `subscribe` method // on the directive's output field to let type information flow into the handler function's // `$event` parameter. - // - // Note that the `EventEmitter` type from '@angular/core' that is typically used for - // outputs has a typings deficiency in its `subscribe` method. The generic type `T` is not - // carried into the handler function, which is vital for inference of the type of `$event`. - // As a workaround, the directive's field is passed into a helper function that has a - // specially crafted set of signatures, to effectively cast `EventEmitter` to something - // that has a `subscribe` method that properly carries the `T` into the handler function. const handler = tcbCreateEventHandler(output, this.tcb, this.scope, EventParamType.Infer); - - if (dirId === null) { - dirId = this.scope.resolve(this.node, this.dir); - } - const outputField = ts.createElementAccess(dirId, ts.createStringLiteral(field)); - addParseSpanInfo(outputField, output.keySpan); - const outputHelper = - ts.createCall(this.tcb.env.declareOutputHelper(), undefined, [outputField]); - const subscribeFn = ts.createPropertyAccess(outputHelper, 'subscribe'); + const subscribeFn = ts.createPropertyAccess(outputField, 'subscribe'); const call = ts.createCall(subscribeFn, /* typeArguments */ undefined, [handler]); addParseSpanInfo(call, output.sourceSpan); this.scope.addStatement(ts.createExpressionStatement(call)); } else { - // If strict checking of directive events is disabled, emit a handler function where the - // `$event` parameter has an explicit `any` type. + // If strict checking of directive events is disabled: + // + // * We still generate the access to the output field as a statement in the TCB so consumers + // of the `TemplateTypeChecker` can still find the node for the class member for the + // output. + // * Emit a handler function where the `$event` parameter has an explicit `any` type. + this.scope.addStatement(ts.createExpressionStatement(outputField)); const handler = tcbCreateEventHandler(output, this.tcb, this.scope, EventParamType.Any); this.scope.addStatement(ts.createExpressionStatement(handler)); } @@ -891,39 +960,6 @@ export class TcbDirectiveOutputsOp extends TcbOp { return null; } - - /** - * Outputs are a `ts.CallExpression` that look like one of the two: - * - `_outputHelper(_t1["outputField"]).subscribe(handler);` - * - `_t1.addEventListener(handler);` - * This method reverses the operations to create a call expression for a directive output. - * It unpacks the given call expression and returns the original element access (i.e. - * `_t1["outputField"]` in the example above). Returns `null` if the given call expression is not - * the expected structure of an output binding - */ - static decodeOutputCallExpression(node: ts.CallExpression): ts.ElementAccessExpression|null { - // `node.expression` === `_outputHelper(_t1["outputField"]).subscribe` or `_t1.addEventListener` - if (!ts.isPropertyAccessExpression(node.expression) || - node.expression.name.text === 'addEventListener') { - // `addEventListener` outputs do not have an `ElementAccessExpression` for the output field. - return null; - } - - if (!ts.isCallExpression(node.expression.expression)) { - return null; - } - - // `node.expression.expression` === `_outputHelper(_t1["outputField"])` - if (node.expression.expression.arguments.length === 0) { - return null; - } - - const [outputFieldAccess] = node.expression.expression.arguments; - if (!ts.isElementAccessExpression(outputFieldAccess)) { - return null; - } - return outputFieldAccess; - } } /** @@ -973,8 +1009,10 @@ class TcbUnclaimedOutputsOp extends TcbOp { if (elId === null) { elId = this.scope.resolve(this.element); } + const propertyAccess = ts.createPropertyAccess(elId, 'addEventListener'); + addParseSpanInfo(propertyAccess, output.keySpan); const call = ts.createCall( - /* expression */ ts.createPropertyAccess(elId, 'addEventListener'), + /* expression */ propertyAccess, /* typeArguments */ undefined, /* arguments */[ts.createStringLiteral(output.name), handler]); addParseSpanInfo(call, output.sourceSpan); @@ -1408,8 +1446,27 @@ class Scope { const dirMap = new Map(); for (const dir of directives) { - const directiveOp = dir.isGeneric ? new TcbDirectiveCtorOp(this.tcb, this, node, dir) : - new TcbDirectiveTypeOp(this.tcb, this, node, dir); + let directiveOp: TcbOp; + const host = this.tcb.env.reflector; + const dirRef = dir.ref as Reference>; + + if (!dir.isGeneric) { + // The most common case is that when a directive is not generic, we use the normal + // `TcbNonDirectiveTypeOp`. + directiveOp = new TcbNonGenericDirectiveTypeOp(this.tcb, this, node, dir); + } else if ( + !requiresInlineTypeCtor(dirRef.node, host) || + this.tcb.env.config.useInlineTypeConstructors) { + // For generic directives, we use a type constructor to infer types. If a directive requires + // an inline type constructor, then inlining must be available to use the + // `TcbDirectiveCtorOp`. If not we, we fallback to using `any` – see below. + directiveOp = new TcbDirectiveCtorOp(this.tcb, this, node, dir); + } else { + // If inlining is not available, then we give up on infering the generic params, and use + // `any` type for the directive's generic parameters. + directiveOp = new TcbGenericDirectiveTypeWithAnyParamsOp(this.tcb, this, node, dir); + } + const dirIndex = this.opQueue.push(directiveOp) - 1; dirMap.set(dir, dirIndex); @@ -1615,10 +1672,16 @@ class TcbExpressionTranslator { pipe = this.tcb.env.pipeInst(pipeRef); } else { // Use an 'any' value when not checking the type of the pipe. - pipe = NULL_AS_ANY; + pipe = ts.createAsExpression( + this.tcb.env.pipeInst(pipeRef), ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)); } const args = ast.args.map(arg => this.translate(arg)); - const result = tsCallMethod(pipe, 'transform', [expr, ...args]); + const methodAccess = ts.createPropertyAccess(pipe, 'transform'); + addParseSpanInfo(methodAccess, ast.nameSpan); + const result = ts.createCall( + /* expression */ methodAccess, + /* typeArguments */ undefined, + /* argumentsArray */[expr, ...args]); addParseSpanInfo(result, ast.sourceSpan); return result; } else if ( @@ -1727,11 +1790,6 @@ function getBoundInputs( return; } - // Skip text attributes if configured to do so. - if (!tcb.env.config.checkTypeOfAttributes && attr instanceof TmplAstTextAttribute) { - return; - } - // Skip the attribute if the directive does not have an input for it. const inputs = directive.inputs.getByBindingPropertyName(attr.name); if (inputs === null) { @@ -1852,6 +1910,7 @@ function tcbCreateEventHandler( /* name */ EVENT_PARAMETER, /* questionToken */ undefined, /* type */ eventParamType); + addExpressionIdentifier(eventParam, ExpressionIdentifier.EVENT_PARAMETER); return ts.createFunctionExpression( /* modifier */ 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 767f78e861..56c2f85e71 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 @@ -49,16 +49,13 @@ export class TypeCheckFile extends Environment { this.tcbStatements.push(fn); } - render(): string { + render(removeComments: boolean): string { let source: string = this.importManager.getAllImports(this.contextFile.fileName) - .map(i => `import * as ${i.qualifier} from '${i.specifier}';`) + .map(i => `import * as ${i.qualifier.text} from '${i.specifier}';`) .join('\n') + '\n\n'; - const printer = ts.createPrinter(); + const printer = ts.createPrinter({removeComments}); source += '\n'; - for (const stmt of this.helperStatements) { - source += printer.printNode(ts.EmitHint.Unspecified, stmt, this.contextFile) + '\n'; - } for (const stmt of this.pipeInstStatements) { source += printer.printNode(ts.EmitHint.Unspecified, stmt, this.contextFile) + '\n'; } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/BUILD.bazel b/packages/compiler-cli/src/ngtsc/typecheck/test/BUILD.bazel index 958d0c02fb..853ecffa1a 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/BUILD.bazel @@ -17,6 +17,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/imports", "//packages/compiler-cli/src/ngtsc/incremental", "//packages/compiler-cli/src/ngtsc/metadata", + "//packages/compiler-cli/src/ngtsc/perf", "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/scope", "//packages/compiler-cli/src/ngtsc/shims", 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 6d8236e23e..231a1e1712 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,9 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ +import {initMockFileSystem} from '../../file_system/testing'; import {tcb, TestDeclaration} from './test_utils'; describe('type check blocks diagnostics', () => { + beforeEach(() => initMockFileSystem('Native')); + describe('parse spans', () => { it('should annotate unary ops', () => { expect(tcbWithSpans('{{ -a }}')).toContain('(-((ctx).a /*4,5*/) /*4,5*/) /*3,5*/'); @@ -91,7 +94,7 @@ describe('type check blocks diagnostics', () => { const TEMPLATE = `
    `; expect(tcbWithSpans(TEMPLATE)) .toContain( - '(((((((ctx).a /*14,15*/) /*14,15*/).b /*16,17*/) /*14,17*/).c /*18,19*/) /*14,23*/ = ((ctx).d /*22,23*/) /*22,23*/) /*14,23*/'); + '(((((((ctx).a /*14,15*/) /*14,15*/).b /*16,17*/) /*14,17*/).c /*18,19*/) /*14,23*/ = (((ctx).d /*22,23*/) /*22,23*/)) /*14,23*/'); }); it('should $event property writes', () => { @@ -110,20 +113,20 @@ describe('type check blocks diagnostics', () => { const TEMPLATE = `
    `; expect(tcbWithSpans(TEMPLATE)) .toContain( - '((((ctx).a /*14,15*/) /*14,15*/)[((ctx).b /*16,17*/) /*16,17*/] = ((ctx).c /*21,22*/) /*21,22*/) /*14,22*/'); + '((((ctx).a /*14,15*/) /*14,15*/)[((ctx).b /*16,17*/) /*16,17*/] = (((ctx).c /*21,22*/) /*21,22*/)) /*14,22*/'); }); it('should annotate safe property access', () => { const TEMPLATE = `{{ a?.b }}`; expect(tcbWithSpans(TEMPLATE)) - .toContain('((null as any) ? (((ctx).a /*3,4*/) /*3,4*/)!.b : undefined) /*3,7*/'); + .toContain('(null as any ? (((ctx).a /*3,4*/) /*3,4*/)!.b /*6,7*/ : undefined) /*3,7*/'); }); it('should annotate safe method calls', () => { const TEMPLATE = `{{ a?.method(b) }}`; expect(tcbWithSpans(TEMPLATE)) .toContain( - '((null as any) ? (((ctx).a /*3,4*/) /*3,4*/)!.method /*6,12*/(((ctx).b /*13,14*/) /*13,14*/) : undefined) /*3,15*/'); + '(null as any ? (((ctx).a /*3,4*/) /*3,4*/)!.method /*6,12*/(((ctx).b /*13,14*/) /*13,14*/) : undefined) /*3,15*/'); }); it('should annotate $any casts', () => { @@ -146,8 +149,9 @@ describe('type check blocks diagnostics', () => { pipeName: 'test', }]; const block = tcbWithSpans(TEMPLATE, PIPES); + expect(block).toContain('var _pipe1: i0.TestPipe = null!'); expect(block).toContain( - '((null as TestPipe).transform(((ctx).a /*3,4*/) /*3,4*/, ((ctx).b /*12,13*/) /*12,13*/) /*3,13*/);'); + '(_pipe1.transform /*7,11*/(((ctx).a /*3,4*/) /*3,4*/, ((ctx).b /*12,13*/) /*12,13*/) /*3,13*/);'); }); describe('attaching multiple comments for multiple references', () => { 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 07bd93cb96..71dc6a073e 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts @@ -11,11 +11,12 @@ import * as ts from 'typescript'; import {absoluteFrom, AbsoluteFsPath, getSourceFileOrError, LogicalFileSystem} from '../../file_system'; import {TestFile} from '../../file_system/testing'; -import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, Reexport, Reference, ReferenceEmitter} from '../../imports'; +import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, Reexport, Reference, ReferenceEmitter, RelativePathStrategy} from '../../imports'; import {NOOP_INCREMENTAL_BUILD} from '../../incremental'; -import {ClassPropertyMapping} from '../../metadata'; +import {ClassPropertyMapping, CompoundMetadataReader} from '../../metadata'; +import {NOOP_PERF_RECORDER} from '../../perf'; import {ClassDeclaration, isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; -import {ComponentScopeReader, LocalModuleScope, ScopeData} from '../../scope'; +import {ComponentScopeReader, LocalModuleScope, ScopeData, TypeCheckScopeRegistry} from '../../scope'; import {makeProgram} from '../../testing'; import {getRootDirs} from '../../util/src/typescript'; import {ProgramTypeCheckAdapter, TemplateTypeChecker, TypeCheckContext} from '../api'; @@ -28,6 +29,7 @@ import {Environment} from '../src/environment'; import {OutOfBandDiagnosticRecorder} from '../src/oob'; import {TypeCheckShimGenerator} from '../src/shim'; import {generateTypeCheckBlock} from '../src/type_check_block'; +import {TypeCheckFile} from '../src/type_check_file'; export function typescriptLibDts(): TestFile { return { @@ -97,7 +99,8 @@ export function angularCoreDts(): TestFile { } export declare class EventEmitter { - subscribe(generatorOrNext?: any, error?: any, complete?: any): unknown; + subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): unknown; + subscribe(observerOrNext?: any, error?: any, complete?: any): unknown; } export declare type NgIterable = Array | Iterable; @@ -164,7 +167,7 @@ export function ngForTypeCheckTarget(): TypeCheckingTarget { }; } -export const ALL_ENABLED_CONFIG: TypeCheckingConfig = { +export const ALL_ENABLED_CONFIG: Readonly = { applyTemplateContextGuards: true, checkQueries: false, checkTemplateBodies: true, @@ -186,34 +189,59 @@ export const ALL_ENABLED_CONFIG: TypeCheckingConfig = { useContextGenericType: true, strictLiteralTypes: true, enableTemplateTypeChecker: false, + useInlineTypeConstructors: true, + suggestionsForSuboptimalTypeInference: false, }; // Remove 'ref' from TypeCheckableDirectiveMeta and add a 'selector' instead. -export type TestDirective = Partial>>&{ - selector: string, name: string, file?: AbsoluteFsPath, type: 'directive', - inputs?: {[fieldName: string]: string}, outputs?: {[fieldName: string]: string}, - coercedInputFields?: string[], restrictedInputFields?: string[], - stringLiteralInputFields?: string[], undeclaredInputFields?: string[], isGeneric?: boolean; -}; -export type TestPipe = { - name: string, - file?: AbsoluteFsPath, pipeName: string, type: 'pipe', -}; + 'undeclaredInputFields'|'inputs'|'outputs'>>> { + selector: string; + name: string; + file?: AbsoluteFsPath; + type: 'directive'; + inputs?: {[fieldName: string]: string}; + outputs?: {[fieldName: string]: string}; + coercedInputFields?: string[]; + restrictedInputFields?: string[]; + stringLiteralInputFields?: string[]; + undeclaredInputFields?: string[]; + isGeneric?: boolean; + code?: string; +} + +export interface TestPipe { + name: string; + file?: AbsoluteFsPath; + pipeName: string; + type: 'pipe'; + code?: string; +} export type TestDeclaration = TestDirective|TestPipe; export function tcb( - template: string, declarations: TestDeclaration[] = [], config?: TypeCheckingConfig, + template: string, declarations: TestDeclaration[] = [], config?: Partial, options?: {emitSpans?: boolean}): string { - const classes = ['Test', ...declarations.map(decl => decl.name)]; - const code = classes.map(name => `class ${name} {}`).join('\n'); + const codeLines = [`export class Test {}`]; - const sf = ts.createSourceFile('synthetic.ts', code, ts.ScriptTarget.Latest, true); + for (const decl of declarations) { + if (decl.code !== undefined) { + codeLines.push(decl.code); + } else { + codeLines.push(`export class ${decl.name} {}`); + } + } + const rootFilePath = absoluteFrom('/synthetic.ts'); + const {program, host} = makeProgram([ + {name: rootFilePath, contents: codeLines.join('\n'), isRoot: true}, + ]); + + const sf = getSourceFileOrError(program, rootFilePath); const clazz = getClass(sf, 'Test'); const templateUrl = 'synthetic.html'; const {nodes} = parseTemplate(template, templateUrl); @@ -225,7 +253,7 @@ export function tcb( const id = 'tcb' as TemplateId; const meta: TypeCheckBlockMetadata = {id, boundTarget, pipes, schemas: []}; - config = config || { + const fullConfig: TypeCheckingConfig = { applyTemplateContextGuards: true, checkQueries: false, checkTypeOfInputBindings: true, @@ -245,18 +273,33 @@ export function tcb( useContextGenericType: true, strictLiteralTypes: true, enableTemplateTypeChecker: false, + useInlineTypeConstructors: true, + suggestionsForSuboptimalTypeInference: false, + ...config }; options = options || { emitSpans: false, }; - const tcb = generateTypeCheckBlock( - FakeEnvironment.newFake(config), new Reference(clazz), ts.createIdentifier('Test_TCB'), meta, - new NoopSchemaChecker(), new NoopOobRecorder()); + const fileName = absoluteFrom('/type-check-file.ts'); - const removeComments = !options.emitSpans; - const res = ts.createPrinter({removeComments}).printNode(ts.EmitHint.Unspecified, tcb, sf); - return res.replace(/\s+/g, ' '); + const reflectionHost = new TypeScriptReflectionHost(program.getTypeChecker()); + + const refEmmiter: ReferenceEmitter = new ReferenceEmitter( + [new LocalIdentifierStrategy(), new RelativePathStrategy(reflectionHost)]); + + const env = new TypeCheckFile(fileName, fullConfig, refEmmiter, reflectionHost, host); + + const ref = new Reference(clazz); + + const tcb = generateTypeCheckBlock( + env, ref, ts.createIdentifier('Test_TCB'), meta, new NoopSchemaChecker(), + new NoopOobRecorder()); + + env.addTypeCheckBlock(ref, meta, new NoopSchemaChecker(), new NoopOobRecorder()); + + const rendered = env.render(!options.emitSpans /* removeComments */); + return rendered.replace(/\s+/g, ' '); } /** @@ -353,7 +396,14 @@ export function setup(targets: TypeCheckingTarget[], overrides: { program, checker, moduleResolver, new TypeScriptReflectionHost(checker)), new LogicalProjectStrategy(reflectionHost, logicalFs), ]); - const fullConfig = {...ALL_ENABLED_CONFIG, ...config}; + + const fullConfig = { + ...ALL_ENABLED_CONFIG, + useInlineTypeConstructors: overrides.inlining !== undefined ? + overrides.inlining : + ALL_ENABLED_CONFIG.useInlineTypeConstructors, + ...config + }; // Map out the scope of each target component, which is needed for the ComponentScopeReader. const scopeMap = new Map(); @@ -407,7 +457,7 @@ export function setup(targets: TypeCheckingTarget[], overrides: { node: classRef.node.name, }; - ctx.addTemplate(classRef, binder, nodes, pipes, [], sourceMapping, templateFile); + ctx.addTemplate(classRef, binder, nodes, pipes, [], sourceMapping, templateFile, errors); } } }); @@ -435,6 +485,7 @@ export function setup(targets: TypeCheckingTarget[], overrides: { directives: [], ngModules: [], pipes: [], + isPoisoned: false, }; return { ngModule, @@ -460,9 +511,12 @@ export function setup(targets: TypeCheckingTarget[], overrides: { } }; + const typeCheckScopeRegistry = + new TypeCheckScopeRegistry(fakeScopeReader, new CompoundMetadataReader([])); + const templateTypeChecker = new TemplateTypeCheckerImpl( program, programStrategy, checkAdapter, fullConfig, emitter, reflectionHost, host, - NOOP_INCREMENTAL_BUILD, fakeScopeReader); + NOOP_INCREMENTAL_BUILD, fakeScopeReader, typeCheckScopeRegistry, NOOP_PERF_RECORDER); return { templateTypeChecker, program, @@ -501,6 +555,7 @@ function prepareDeclarations( isGeneric: decl.isGeneric ?? false, outputs: ClassPropertyMapping.fromMappedObject(decl.outputs || {}), queries: decl.queries || [], + isStructural: false, }; matcher.addSelectables(selector, meta); } @@ -532,6 +587,7 @@ function makeScope(program: ts.Program, sf: ts.SourceFile, decls: TestDeclaratio ngModules: [], directives: [], pipes: [], + isPoisoned: false, }; for (const decl of decls) { @@ -561,6 +617,8 @@ function makeScope(program: ts.Program, sf: ts.SourceFile, decls: TestDeclaratio stringLiteralInputFields: new Set(decl.stringLiteralInputFields ?? []), undeclaredInputFields: new Set(decl.undeclaredInputFields ?? []), isGeneric: decl.isGeneric ?? false, + isPoisoned: false, + isStructural: false, }); } else if (decl.type === 'pipe') { scope.pipes.push({ @@ -584,10 +642,6 @@ class FakeEnvironment /* implements Environment */ { return ts.createParen(ts.createAsExpression(ts.createNull(), this.referenceType(ref))); } - declareOutputHelper(): ts.Expression { - return ts.createIdentifier('_outputHelper'); - } - reference(ref: Reference>): ts.Expression { return ref.node.name; } @@ -639,4 +693,5 @@ export class NoopOobRecorder implements OutOfBandDiagnosticRecorder { duplicateTemplateVar(): void {} requiresInlineTcb(): void {} requiresInlineTypeConstructors(): void {} + suboptimalTypeInference(): 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 82fc1d0583..9ccb53db84 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 @@ -6,12 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ +import {initMockFileSystem} from '../../file_system/testing'; import {TypeCheckingConfig} from '../api'; import {ALL_ENABLED_CONFIG, tcb, TestDeclaration, TestDirective} from './test_utils'; describe('type check blocks', () => { + beforeEach(() => initMockFileSystem('Native')); + it('should generate a basic block for a binding', () => { expect(tcb('{{hello}} {{world}}')).toContain('"" + (((ctx).hello)) + (((ctx).world));'); }); @@ -60,7 +63,7 @@ describe('type check blocks', () => { selector: '[dir]', inputs: {inputA: 'inputA'}, }]; - expect(tcb(TEMPLATE, DIRECTIVES)).toContain('_t1: DirA = (null!); _t1.inputA = ("value");'); + expect(tcb(TEMPLATE, DIRECTIVES)).toContain('_t1: i0.DirA = null!; _t1.inputA = ("value");'); }); it('should handle multiple bindings to the same property', () => { @@ -133,9 +136,11 @@ describe('type check blocks', () => { }, isGeneric: true, }]; - expect(tcb(TEMPLATE, DIRECTIVES)) - .toContain( - 'var _t1 = Dir.ngTypeCtor({ "fieldA": (((ctx).foo)), "fieldB": (null as any) });'); + const actual = tcb(TEMPLATE, DIRECTIVES); + expect(actual).toContain( + 'const _ctor1: (init: Pick, "fieldA" | "fieldB">) => i0.Dir = null!;'); + expect(actual).toContain( + 'var _t1 = _ctor1({ "fieldA": (((ctx).foo)), "fieldB": null as any });'); }); it('should handle multiple bindings to the same property', () => { @@ -183,11 +188,14 @@ describe('type check blocks', () => { inputs: {input: 'input'}, isGeneric: true, }]; - expect(tcb(TEMPLATE, DIRECTIVES)) - .toContain( - 'var _t2 = Dir.ngTypeCtor({ "input": (null!) }); ' + - 'var _t1 = _t2; ' + - '_t2.input = (_t1);'); + + const actual = tcb(TEMPLATE, DIRECTIVES); + expect(actual).toContain( + 'const _ctor1: (init: Pick, "input">) => i0.Dir = null!;'); + expect(actual).toContain( + 'var _t2 = _ctor1({ "input": (null!) }); ' + + 'var _t1 = _t2; ' + + '_t2.input = (_t1);'); }); it('should generate circular references between two directives correctly', () => { @@ -213,14 +221,16 @@ describe('type check blocks', () => { isGeneric: true, } ]; - expect(tcb(TEMPLATE, DIRECTIVES)) - .toContain( - 'var _t4 = DirA.ngTypeCtor({ "inputA": (null!) }); ' + - 'var _t3 = _t4; ' + - 'var _t2 = DirB.ngTypeCtor({ "inputB": (_t3) }); ' + - 'var _t1 = _t2; ' + - '_t4.inputA = (_t1); ' + - '_t2.inputB = (_t3);'); + const actual = tcb(TEMPLATE, DIRECTIVES); + expect(actual).toContain( + 'const _ctor1: (init: Pick, "inputA">) => i0.DirA = null!; const _ctor2: (init: Pick, "inputB">) => i0.DirB = null!;'); + expect(actual).toContain( + 'var _t4 = _ctor1({ "inputA": (null!) }); ' + + 'var _t3 = _t4; ' + + 'var _t2 = _ctor2({ "inputB": (_t3) }); ' + + 'var _t1 = _t2; ' + + '_t4.inputA = (_t1); ' + + '_t2.inputB = (_t3);'); }); it('should handle empty bindings', () => { @@ -261,7 +271,7 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t1: typeof Dir.ngAcceptInputType_fieldA = (null!); ' + + 'var _t1: typeof i0.Dir.ngAcceptInputType_fieldA = null!; ' + '_t1 = (((ctx).foo));'); }); }); @@ -321,11 +331,11 @@ describe('type check blocks', () => { }, ]; const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('var _t1: HasInput = (null!)'); + expect(block).toContain('var _t1: i0.HasInput = null!'); expect(block).toContain('_t1.input = (((ctx).value));'); - expect(block).toContain('var _t2: HasOutput = (null!)'); + expect(block).toContain('var _t2: i0.HasOutput = null!'); expect(block).toContain('_t2["output"]'); - expect(block).toContain('var _t4: HasReference = (null!)'); + expect(block).toContain('var _t4: i0.HasReference = null!'); expect(block).toContain('var _t3 = _t4;'); expect(block).toContain('(_t3).a'); expect(block).not.toContain('NoBindings'); @@ -355,7 +365,7 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t2: Dir = (null!); ' + + 'var _t2: i0.Dir = null!; ' + 'var _t1 = _t2; ' + '"" + (((_t1).value));'); }); @@ -383,7 +393,7 @@ describe('type check blocks', () => { inputs: {'color': 'color', 'strong': 'strong', 'enabled': 'enabled'}, }]; const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).not.toContain('var _t1: Dir = (null!);'); + expect(block).not.toContain('var _t1: Dir = null!;'); expect(block).not.toContain('"color"'); expect(block).not.toContain('"strong"'); expect(block).not.toContain('"enabled"'); @@ -403,7 +413,7 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t2: Dir = (null!); ' + + 'var _t2: i0.Dir = null!; ' + 'var _t1 = _t2; ' + '_t2.input = (_t1);'); }); @@ -431,9 +441,9 @@ describe('type check blocks', () => { ]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t2: DirB = (null!); ' + + 'var _t2: i0.DirB = null!; ' + 'var _t1 = _t2; ' + - 'var _t3: DirA = (null!); ' + + 'var _t3: i0.DirA = null!; ' + '_t3.inputA = (_t1); ' + 'var _t4 = _t3; ' + '_t2.inputA = (_t4);'); @@ -451,7 +461,7 @@ describe('type check blocks', () => { undeclaredInputFields: ['fieldA'] }]; const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).not.toContain('var _t1: Dir = (null!);'); + expect(block).not.toContain('var _t1: Dir = null!;'); expect(block).toContain('(((ctx).foo)); '); }); @@ -468,8 +478,8 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t1: Dir = (null!); ' + - 'var _t2: typeof _t1["fieldA"] = (null!); ' + + 'var _t1: i0.Dir = null!; ' + + 'var _t2: typeof _t1["fieldA"] = null!; ' + '_t2 = (((ctx).foo)); '); }); @@ -487,7 +497,7 @@ describe('type check blocks', () => { }]; const block = tcb(TEMPLATE, DIRECTIVES); expect(block).toContain( - 'var _t1: Dir = (null!); ' + + 'var _t1: i0.Dir = null!; ' + '_t1["some-input.xs"] = (((ctx).foo)); '); }); @@ -504,7 +514,7 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t1: Dir = (null!); ' + + 'var _t1: i0.Dir = null!; ' + '_t1.field2 = _t1.field1 = (((ctx).foo));'); }); @@ -523,8 +533,8 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t1: typeof Dir.ngAcceptInputType_field1 = (null!); ' + - 'var _t2: Dir = (null!); ' + + 'var _t1: typeof i0.Dir.ngAcceptInputType_field1 = null!; ' + + 'var _t2: i0.Dir = null!; ' + '_t2.field2 = _t1 = (((ctx).foo));'); }); @@ -543,7 +553,7 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t1: Dir = (null!); ' + + 'var _t1: i0.Dir = null!; ' + '_t1.field2 = (((ctx).foo));'); }); @@ -559,9 +569,9 @@ describe('type check blocks', () => { coercedInputFields: ['fieldA'], }]; const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).not.toContain('var _t1: Dir = (null!);'); + expect(block).not.toContain('var _t1: Dir = null!;'); expect(block).toContain( - 'var _t1: typeof Dir.ngAcceptInputType_fieldA = (null!); ' + + 'var _t1: typeof i0.Dir.ngAcceptInputType_fieldA = null!; ' + '_t1 = (((ctx).foo));'); }); @@ -578,9 +588,9 @@ describe('type check blocks', () => { undeclaredInputFields: ['fieldA'], }]; const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).not.toContain('var _t1: Dir = (null!);'); + expect(block).not.toContain('var _t1: Dir = null!;'); expect(block).toContain( - 'var _t1: typeof Dir.ngAcceptInputType_fieldA = (null!); ' + + 'var _t1: typeof i0.Dir.ngAcceptInputType_fieldA = null!; ' + '_t1 = (((ctx).foo));'); }); @@ -619,7 +629,7 @@ describe('type check blocks', () => { }]; const TEMPLATE = `
    {{person.name}}
    `; const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('if (NgIf.ngTemplateGuard_ngIf(_t1, ((ctx).person)))'); + expect(block).toContain('if (i0.NgIf.ngTemplateGuard_ngIf(_t1, ((ctx).person)))'); }); it('should emit binding guards', () => { @@ -666,14 +676,13 @@ describe('type check blocks', () => { const TEMPLATE = `
    `; const block = tcb(TEMPLATE, DIRECTIVES); expect(block).toContain( - '_outputHelper(_t1["outputField"]).subscribe(function ($event): any { (ctx).foo($event); });'); + '_t1["outputField"].subscribe(function ($event): any { (ctx).foo($event); });'); }); it('should emit a listener function with AnimationEvent for animation events', () => { const TEMPLATE = `
    `; const block = tcb(TEMPLATE); - expect(block).toContain( - 'function ($event: animations.AnimationEvent): any { (ctx).foo($event); }'); + expect(block).toContain('function ($event: i1.AnimationEvent): any { (ctx).foo($event); }'); }); it('should emit addEventListener calls for unclaimed outputs', () => { @@ -736,11 +745,13 @@ describe('type check blocks', () => { useContextGenericType: true, strictLiteralTypes: true, enableTemplateTypeChecker: false, + useInlineTypeConstructors: true, + suggestionsForSuboptimalTypeInference: false, }; describe('config.applyTemplateContextGuards', () => { const TEMPLATE = `
    {{ value }}
    `; - const GUARD_APPLIED = 'if (Dir.ngTemplateContextGuard('; + const GUARD_APPLIED = 'if (i0.Dir.ngTemplateContextGuard('; it('should apply template context guards when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); @@ -769,13 +780,13 @@ describe('type check blocks', () => { it('generates a references var when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('var _t1 = (_t2 as any as core.TemplateRef);'); + expect(block).toContain('var _t1 = (_t2 as any as i1.TemplateRef);'); }); it('generates a reference var when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTemplateBodies: false}; const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG); - expect(block).toContain('var _t1 = (_t2 as any as core.TemplateRef);'); + expect(block).toContain('var _t1 = (_t2 as any as i1.TemplateRef);'); }); }); @@ -828,7 +839,7 @@ describe('type check blocks', () => { it('should check types of directive outputs when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); expect(block).toContain( - '_outputHelper(_t1["outputField"]).subscribe(function ($event): any { (ctx).foo($event); });'); + '_t1["outputField"].subscribe(function ($event): any { (ctx).foo($event); });'); expect(block).toContain( '_t2.addEventListener("nonDirOutput", function ($event): any { (ctx).foo($event); });'); }); @@ -839,7 +850,7 @@ describe('type check blocks', () => { expect(block).toContain('function ($event: any): any { (ctx).foo($event); }'); // Note that DOM events are still checked, that is controlled by `checkTypeOfDomEvents` expect(block).toContain( - '_t1.addEventListener("nonDirOutput", function ($event): any { (ctx).foo($event); });'); + 'addEventListener("nonDirOutput", function ($event): any { (ctx).foo($event); });'); }); }); @@ -848,8 +859,7 @@ describe('type check blocks', () => { it('should check types of animation events when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain( - 'function ($event: animations.AnimationEvent): any { (ctx).foo($event); }'); + expect(block).toContain('function ($event: i1.AnimationEvent): any { (ctx).foo($event); }'); }); it('should not check types of animation events when disabled', () => { const DISABLED_CONFIG: @@ -865,7 +875,7 @@ describe('type check blocks', () => { it('should check types of DOM events when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); expect(block).toContain( - '_outputHelper(_t1["outputField"]).subscribe(function ($event): any { (ctx).foo($event); });'); + '_t1["outputField"].subscribe(function ($event): any { (ctx).foo($event); });'); expect(block).toContain( '_t2.addEventListener("nonDirOutput", function ($event): any { (ctx).foo($event); });'); }); @@ -875,7 +885,7 @@ describe('type check blocks', () => { // Note that directive outputs are still checked, that is controlled by // `checkTypeOfOutputEvents` expect(block).toContain( - '_outputHelper(_t1["outputField"]).subscribe(function ($event): any { (ctx).foo($event); });'); + '_t1["outputField"].subscribe(function ($event): any { (ctx).foo($event); });'); expect(block).toContain('function ($event: any): any { (ctx).foo($event); }'); }); }); @@ -893,7 +903,7 @@ describe('type check blocks', () => { TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfDomReferences: false}; const block = tcb(TEMPLATE, [], DISABLED_CONFIG); expect(block).toContain( - 'var _t1 = (_t2 as any); ' + + 'var _t1 = _t2 as any; ' + '"" + (((_t1).value));'); }); }); @@ -919,7 +929,7 @@ describe('type check blocks', () => { it('should trace references to an when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); expect(block).toContain( - 'var _t3 = (_t4 as any as core.TemplateRef); ' + + 'var _t3 = (_t4 as any as i1.TemplateRef); ' + '"" + (((_t3).value2));'); }); @@ -928,7 +938,7 @@ describe('type check blocks', () => { TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfNonDomReferences: false}; const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG); expect(block).toContain( - 'var _t1 = (_t2 as any); ' + + 'var _t1 = _t2 as any; ' + '"" + (((_t1).value));'); }); }); @@ -968,12 +978,14 @@ describe('type check blocks', () => { it('should check types of pipes when enabled', () => { const block = tcb(TEMPLATE, PIPES); - expect(block).toContain('(null as TestPipe).transform(((ctx).a), ((ctx).b), ((ctx).c))'); + expect(block).toContain('var _pipe1: i0.TestPipe = null!;'); + expect(block).toContain('(_pipe1.transform(((ctx).a), ((ctx).b), ((ctx).c)));'); }); it('should not check types of pipes when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfPipes: false}; const block = tcb(TEMPLATE, PIPES, DISABLED_CONFIG); - expect(block).toContain('(null as any).transform(((ctx).a), ((ctx).b), ((ctx).c))'); + expect(block).toContain('var _pipe1: i0.TestPipe = null!;'); + expect(block).toContain('((_pipe1 as any).transform(((ctx).a), ((ctx).b), ((ctx).c)));'); }); }); @@ -982,8 +994,8 @@ describe('type check blocks', () => { it('should use undefined for safe navigation operations when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('((null as any) ? (((ctx).a))!.method() : undefined)'); - expect(block).toContain('((null as any) ? (((ctx).a))!.b : undefined)'); + expect(block).toContain('(null as any ? (((ctx).a))!.method() : undefined)'); + expect(block).toContain('(null as any ? (((ctx).a))!.b : undefined)'); }); it('should use an \'any\' type for safe navigation operations when disabled', () => { const DISABLED_CONFIG: @@ -998,8 +1010,8 @@ describe('type check blocks', () => { const TEMPLATE = `{{a.method()?.b}} {{a()?.method()}}`; it('should check the presence of a property/method on the receiver when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('((null as any) ? ((((ctx).a)).method())!.b : undefined)'); - expect(block).toContain('((null as any) ? ((ctx).a())!.method() : undefined)'); + expect(block).toContain('(null as any ? ((((ctx).a)).method())!.b : undefined)'); + expect(block).toContain('(null as any ? ((ctx).a())!.method() : undefined)'); }); it('should not check the presence of a property/method on the receiver when disabled', () => { const DISABLED_CONFIG: @@ -1015,13 +1027,13 @@ describe('type check blocks', () => { it('should use the generic type of the context when enabled', () => { const block = tcb(TEMPLATE); - expect(block).toContain('function Test_TCB(ctx: Test)'); + expect(block).toContain('function _tcb1(ctx: i0.Test)'); }); it('should use any for the context generic type when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, useContextGenericType: false}; const block = tcb(TEMPLATE, undefined, DISABLED_CONFIG); - expect(block).toContain('function Test_TCB(ctx: Test)'); + expect(block).toContain('function _tcb1(ctx: i0.Test)'); }); }); @@ -1044,7 +1056,7 @@ describe('type check blocks', () => { TypeCheckingConfig = {...BASE_CONFIG, honorAccessModifiersForInputBindings: true}; const block = tcb(TEMPLATE, DIRECTIVES, enableChecks); expect(block).toContain( - 'var _t1: Dir = (null!); ' + + 'var _t1: i0.Dir = null!; ' + '_t1["some-input.xs"] = (((ctx).foo)); '); }); @@ -1062,9 +1074,40 @@ describe('type check blocks', () => { TypeCheckingConfig = {...BASE_CONFIG, honorAccessModifiersForInputBindings: true}; const block = tcb(TEMPLATE, DIRECTIVES, enableChecks); expect(block).toContain( - 'var _t1: Dir = (null!); ' + + 'var _t1: i0.Dir = null!; ' + '_t1.fieldA = (((ctx).foo)); '); }); }); }); + + it('should use `any` type for type constructors with bound generic params ' + + 'when `useInlineTypeConstructors` is `false`', + () => { + const template = ` +
    + `; + const declarations: TestDeclaration[] = [{ + code: ` + interface PrivateInterface{}; + export class Dir {}; + `, + type: 'directive', + name: 'Dir', + selector: '[dir]', + inputs: { + inputA: 'inputA', + inputB: 'inputB', + }, + isGeneric: true + }]; + + const renderedTcb = tcb(template, declarations, {useInlineTypeConstructors: false}); + + expect(renderedTcb).toContain(`var _t1: i0.Dir = null!;`); + expect(renderedTcb).toContain(`_t1.inputA = (((ctx).foo));`); + expect(renderedTcb).toContain(`_t1.inputB = (((ctx).bar));`); + }); }); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__completion_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__completion_spec.ts index ba1036320c..104b3ed279 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__completion_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__completion_spec.ts @@ -71,32 +71,6 @@ runInEachFileSystem(() => { expect(userAtTopLevel.kind).toBe(CompletionKind.Reference); expect(userInNgFor.kind).toBe(CompletionKind.Variable); }); - - it('should invalidate cached completions when overrides change', () => { - // The template starts with a #foo local reference. - const {completions: before, templateTypeChecker, component} = - setupCompletions('
    '); - expect(Array.from(before.templateContext.keys())).toEqual(['foo']); - - // Override the template and change the name of the local reference to #bar. This should - // invalidate any cached completions. - templateTypeChecker.overrideComponentTemplate(component, '
    '); - - // Fresh completions should include the #bar reference instead. - const afterOverride = - templateTypeChecker.getGlobalCompletions(/* root template */ null, component)!; - expect(afterOverride).toBeDefined(); - expect(Array.from(afterOverride.templateContext.keys())).toEqual(['bar']); - - // Reset the template to its original. This should also invalidate any cached completions. - templateTypeChecker.resetOverrides(); - - // Fresh completions should include the original #foo now. - const afterReset = - templateTypeChecker.getGlobalCompletions(/* root template */ null, component)!; - expect(afterReset).toBeDefined(); - expect(Array.from(afterReset.templateContext.keys())).toEqual(['foo']); - }); }); describe('TemplateTypeChecker scopes', () => { diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts index 9343f6e6a5..f08ad4b72e 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts @@ -7,12 +7,13 @@ */ import {ASTWithSource, Binary, BindingPipe, Conditional, Interpolation, PropertyRead, TmplAstBoundAttribute, TmplAstBoundText, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate} from '@angular/compiler'; +import {AST, LiteralArray, LiteralMap} from '@angular/compiler/src/compiler'; import * as ts from 'typescript'; -import {absoluteFrom, getSourceFileOrError} from '../../file_system'; +import {absoluteFrom, AbsoluteFsPath, getSourceFileOrError} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {ClassDeclaration} from '../../reflection'; -import {DirectiveSymbol, DomBindingSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, ReferenceSymbol, Symbol, SymbolKind, TemplateSymbol, TemplateTypeChecker, TypeCheckingConfig, VariableSymbol} from '../api'; +import {DirectiveSymbol, DomBindingSymbol, ElementSymbol, ExpressionSymbol, InputBindingSymbol, OutputBindingSymbol, PipeSymbol, ReferenceSymbol, Symbol, SymbolKind, TemplateSymbol, TemplateTypeChecker, TypeCheckingConfig, VariableSymbol} from '../api'; import {getClass, ngForDeclaration, ngForTypeCheckTarget, setup as baseTestSetup, TypeCheckingTarget} from './test_utils'; @@ -39,71 +40,61 @@ runInEachFileSystem(() => { assertElementSymbol(symbol.host); }); - it('should invalidate symbols when template overrides change', () => { - const fileName = absoluteFrom('/main.ts'); - const templateString = `
    `; - const {templateTypeChecker, program} = setup( - [ - { - fileName, - templates: {'Cmp': templateString}, - source: `export class Cmp {}`, - }, - ], - ); - const sf = getSourceFileOrError(program, fileName); - const cmp = getClass(sf, 'Cmp'); - const {attributes: beforeAttributes} = getAstElements(templateTypeChecker, cmp)[0]; - const beforeSymbol = templateTypeChecker.getSymbolOfNode(beforeAttributes[0], cmp)!; + describe('should get a symbol for text attributes corresponding with a directive input', () => { + let fileName: AbsoluteFsPath; + let targets: TypeCheckingTarget[]; + beforeEach(() => { + fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const templateString = `
    `; + targets = [ + { + fileName, + templates: {'Cmp': templateString} as {[key: string]: string}, + declarations: [{ + name: 'NameDiv', + selector: 'div[name]', + file: dirFile, + type: 'directive' as const, + inputs: {name: 'name'}, + }] + }, + { + fileName: dirFile, + source: `export class NameDiv {name!: string;}`, + templates: {}, + } + ]; + }); - // Replace the
    with a . - templateTypeChecker.overrideComponentTemplate(cmp, ''); + it('checkTypeOfAttributes = true', () => { + const {templateTypeChecker, program} = setup(targets, {checkTypeOfAttributes: true}); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + const {attributes} = getAstElements(templateTypeChecker, cmp)[0]; + const symbol = templateTypeChecker.getSymbolOfNode(attributes[0], cmp)!; + assertInputBindingSymbol(symbol); + expect( + (symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration).name.getText()) + .toEqual('name'); - const {attributes: afterAttributes} = getAstElements(templateTypeChecker, cmp)[0]; - const afterSymbol = templateTypeChecker.getSymbolOfNode(afterAttributes[0], cmp)!; + // Ensure we can go back to the original location using the shim location + const mapping = + templateTypeChecker.getTemplateMappingAtShimLocation(symbol.bindings[0].shimLocation)!; + expect(mapping.span.toString()).toEqual('name'); + }); - // After the override, the symbol cache should have been invalidated. - expect(beforeSymbol).not.toBe(afterSymbol); - }); - - it('should get a symbol for text attributes corresponding with a directive input', () => { - const fileName = absoluteFrom('/main.ts'); - const dirFile = absoluteFrom('/dir.ts'); - const templateString = `
    `; - const {templateTypeChecker, program} = setup( - [ - { - fileName, - templates: {'Cmp': templateString}, - declarations: [{ - name: 'NameDiv', - selector: 'div[name]', - file: dirFile, - type: 'directive', - inputs: {name: 'name'}, - }] - }, - { - fileName: dirFile, - source: `export class NameDiv {name!: string;}`, - templates: {}, - } - ], - ); - const sf = getSourceFileOrError(program, fileName); - const cmp = getClass(sf, 'Cmp'); - const {attributes} = getAstElements(templateTypeChecker, cmp)[0]; - - const symbol = templateTypeChecker.getSymbolOfNode(attributes[0], cmp)!; - assertInputBindingSymbol(symbol); - expect( - (symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration).name.getText()) - .toEqual('name'); - - // Ensure we can go back to the original location using the shim location - const mapping = - templateTypeChecker.getTemplateMappingAtShimLocation(symbol.bindings[0].shimLocation)!; - expect(mapping.span.toString()).toEqual('name'); + it('checkTypeOfAttributes = false', () => { + const {templateTypeChecker, program} = setup(targets, {checkTypeOfAttributes: false}); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + const {attributes} = getAstElements(templateTypeChecker, cmp)[0]; + const symbol = templateTypeChecker.getSymbolOfNode(attributes[0], cmp)!; + assertInputBindingSymbol(symbol); + expect( + (symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration).name.getText()) + .toEqual('name'); + }); }); describe('templates', () => { @@ -539,31 +530,57 @@ runInEachFileSystem(() => { expect(program.getTypeChecker().typeToString(bSymbol.tsType)).toEqual('number'); }); - it('should get symbol for local reference of an Element', () => { - const fileName = absoluteFrom('/main.ts'); - const {templateTypeChecker, program} = setup([ - { - fileName, - templates: { - 'Cmp': ` + describe('local reference of an Element', () => { + it('checkTypeOfDomReferences = true', () => { + const fileName = absoluteFrom('/main.ts'); + const {templateTypeChecker, program} = setup([ + { + fileName, + templates: { + 'Cmp': `
    ` + }, }, - }, - ]); - const sf = getSourceFileOrError(program, fileName); - const cmp = getClass(sf, 'Cmp'); - const nodes = getAstElements(templateTypeChecker, cmp); + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + const nodes = getAstElements(templateTypeChecker, cmp); - const refSymbol = templateTypeChecker.getSymbolOfNode(nodes[0].references[0], cmp)!; - assertReferenceSymbol(refSymbol); - expect((refSymbol.target as TmplAstElement).name).toEqual('input'); - expect((refSymbol.declaration as TmplAstReference).name).toEqual('myRef'); + const refSymbol = templateTypeChecker.getSymbolOfNode(nodes[0].references[0], cmp)!; + assertReferenceSymbol(refSymbol); + expect((refSymbol.target as TmplAstElement).name).toEqual('input'); + expect((refSymbol.declaration as TmplAstReference).name).toEqual('myRef'); - const myRefUsage = templateTypeChecker.getSymbolOfNode(nodes[1].inputs[0].value, cmp)!; - assertReferenceSymbol(myRefUsage); - expect((myRefUsage.target as TmplAstElement).name).toEqual('input'); - expect((myRefUsage.declaration as TmplAstReference).name).toEqual('myRef'); + const myRefUsage = templateTypeChecker.getSymbolOfNode(nodes[1].inputs[0].value, cmp)!; + assertReferenceSymbol(myRefUsage); + expect((myRefUsage.target as TmplAstElement).name).toEqual('input'); + expect((myRefUsage.declaration as TmplAstReference).name).toEqual('myRef'); + }); + + it('checkTypeOfDomReferences = false', () => { + const fileName = absoluteFrom('/main.ts'); + const {templateTypeChecker, program} = setup( + [ + { + fileName, + templates: { + 'Cmp': ` + +
    ` + }, + }, + ], + {checkTypeOfDomReferences: false}); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + const nodes = getAstElements(templateTypeChecker, cmp); + + const refSymbol = templateTypeChecker.getSymbolOfNode(nodes[0].references[0], cmp); + // Our desired behavior here is to honor the user's compiler settings and not produce a + // symbol for the reference when `checkTypeOfDomReferences` is false. + expect(refSymbol).toBeNull(); + }); }); it('should get symbols for references which refer to directives', () => { @@ -650,7 +667,7 @@ runInEachFileSystem(() => { }); it('literal array', () => { - const literalArray = interpolation.expressions[0]; + const literalArray = interpolation.expressions[0] as LiteralArray; const symbol = templateTypeChecker.getSymbolOfNode(literalArray, cmp)!; assertExpressionSymbol(symbol); expect(program.getTypeChecker().symbolToString(symbol.tsSymbol!)).toEqual('Array'); @@ -658,7 +675,7 @@ runInEachFileSystem(() => { }); it('literal map', () => { - const literalMap = interpolation.expressions[1]; + const literalMap = interpolation.expressions[1] as LiteralMap; const symbol = templateTypeChecker.getSymbolOfNode(literalMap, cmp)!; assertExpressionSymbol(symbol); expect(program.getTypeChecker().symbolToString(symbol.tsSymbol!)).toEqual('__object'); @@ -667,34 +684,35 @@ runInEachFileSystem(() => { }); }); - describe('pipes', () => { let templateTypeChecker: TemplateTypeChecker; let cmp: ClassDeclaration; let binding: BindingPipe; let program: ts.Program; - beforeEach(() => { + function setupPipesTest(checkTypeOfPipes = true) { const fileName = absoluteFrom('/main.ts'); const templateString = `
    `; - const testValues = setup([ - { - fileName, - templates: {'Cmp': templateString}, - source: ` + const testValues = setup( + [ + { + fileName, + templates: {'Cmp': templateString}, + source: ` export class Cmp { a: string; b: number; c: boolean } export class TestPipe { transform(value: string, repeat: number, commaSeparate: boolean): string[] { } } `, - declarations: [{ - type: 'pipe', - name: 'TestPipe', - pipeName: 'test', - }], - }, - ]); + declarations: [{ + type: 'pipe', + name: 'TestPipe', + pipeName: 'test', + }], + }, + ], + {checkTypeOfPipes}); program = testValues.program; templateTypeChecker = testValues.templateTypeChecker; const sf = getSourceFileOrError(testValues.program, fileName); @@ -702,38 +720,73 @@ runInEachFileSystem(() => { binding = (getAstElements(templateTypeChecker, cmp)[0].inputs[0].value as ASTWithSource).ast as BindingPipe; - }); + } it('should get symbol for pipe', () => { + setupPipesTest(); const pipeSymbol = templateTypeChecker.getSymbolOfNode(binding, cmp)!; - assertExpressionSymbol(pipeSymbol); + assertPipeSymbol(pipeSymbol); expect(program.getTypeChecker().symbolToString(pipeSymbol.tsSymbol!)) .toEqual('transform'); - expect( - (pipeSymbol.tsSymbol!.declarations[0].parent as ts.ClassDeclaration).name!.getText()) + expect(program.getTypeChecker().symbolToString(pipeSymbol.classSymbol.tsSymbol)) .toEqual('TestPipe'); - expect(program.getTypeChecker().typeToString(pipeSymbol.tsType)) + expect(program.getTypeChecker().typeToString(pipeSymbol.tsType!)) .toEqual('(value: string, repeat: number, commaSeparate: boolean) => string[]'); }); + it('should get symbol for pipe, checkTypeOfPipes: false', () => { + setupPipesTest(false); + const pipeSymbol = templateTypeChecker.getSymbolOfNode(binding, cmp)! as PipeSymbol; + assertPipeSymbol(pipeSymbol); + expect(pipeSymbol.tsSymbol).toBeNull(); + expect(program.getTypeChecker().typeToString(pipeSymbol.tsType!)).toEqual('any'); + expect(program.getTypeChecker().symbolToString(pipeSymbol.classSymbol.tsSymbol)) + .toEqual('TestPipe'); + expect(program.getTypeChecker().typeToString(pipeSymbol.classSymbol.tsType)) + .toEqual('TestPipe'); + }); + it('should get symbols for pipe expression and args', () => { + setupPipesTest(false); const aSymbol = templateTypeChecker.getSymbolOfNode(binding.exp, cmp)!; assertExpressionSymbol(aSymbol); expect(program.getTypeChecker().symbolToString(aSymbol.tsSymbol!)).toEqual('a'); expect(program.getTypeChecker().typeToString(aSymbol.tsType)).toEqual('string'); - const bSymbol = templateTypeChecker.getSymbolOfNode(binding.args[0], cmp)!; + const bSymbol = templateTypeChecker.getSymbolOfNode(binding.args[0] as AST, cmp)!; assertExpressionSymbol(bSymbol); expect(program.getTypeChecker().symbolToString(bSymbol.tsSymbol!)).toEqual('b'); expect(program.getTypeChecker().typeToString(bSymbol.tsType)).toEqual('number'); - const cSymbol = templateTypeChecker.getSymbolOfNode(binding.args[1], cmp)!; + const cSymbol = templateTypeChecker.getSymbolOfNode(binding.args[1] as AST, cmp)!; assertExpressionSymbol(cSymbol); expect(program.getTypeChecker().symbolToString(cSymbol.tsSymbol!)).toEqual('c'); expect(program.getTypeChecker().typeToString(cSymbol.tsType)).toEqual('boolean'); }); - }); + for (const checkTypeOfPipes of [true, false]) { + describe(`checkTypeOfPipes: ${checkTypeOfPipes}`, () => { + // Because the args are property reads, we still need information about them. + it(`should get symbols for pipe expression and args`, () => { + setupPipesTest(checkTypeOfPipes); + const aSymbol = templateTypeChecker.getSymbolOfNode(binding.exp, cmp)!; + assertExpressionSymbol(aSymbol); + expect(program.getTypeChecker().symbolToString(aSymbol.tsSymbol!)).toEqual('a'); + expect(program.getTypeChecker().typeToString(aSymbol.tsType)).toEqual('string'); + + const bSymbol = templateTypeChecker.getSymbolOfNode(binding.args[0] as AST, cmp)!; + assertExpressionSymbol(bSymbol); + expect(program.getTypeChecker().symbolToString(bSymbol.tsSymbol!)).toEqual('b'); + expect(program.getTypeChecker().typeToString(bSymbol.tsType)).toEqual('number'); + + const cSymbol = templateTypeChecker.getSymbolOfNode(binding.args[1] as AST, cmp)!; + assertExpressionSymbol(cSymbol); + expect(program.getTypeChecker().symbolToString(cSymbol.tsSymbol!)).toEqual('c'); + expect(program.getTypeChecker().typeToString(cSymbol.tsType)).toEqual('boolean'); + }); + }); + } + }); it('should get a symbol for PropertyWrite expressions', () => { const fileName = absoluteFrom('/main.ts'); @@ -1056,7 +1109,7 @@ runInEachFileSystem(() => { .toEqual('TestDir'); }); - it('returns the first directive match when two directives have the same input', () => { + it('returns the all inputs when two directives have the same input', () => { const fileName = absoluteFrom('/main.ts'); const dirFile = absoluteFrom('/dir.ts'); const templateString = `
    `; @@ -1098,12 +1151,12 @@ runInEachFileSystem(() => { const inputAbinding = (nodes[0] as TmplAstElement).inputs[0]; const symbol = templateTypeChecker.getSymbolOfNode(inputAbinding, cmp)!; assertInputBindingSymbol(symbol); - expect( - (symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration).name.getText()) - .toEqual('inputA'); - expect((symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) - .parent.name?.text) - .toEqual('TestDir'); + expect(new Set(symbol.bindings.map( + b => (b.tsSymbol!.declarations[0] as ts.PropertyDeclaration).name.getText()))) + .toEqual(new Set(['inputA', 'otherDirInputA'])); + expect(new Set(symbol.bindings.map( + b => (b.tsSymbol!.declarations[0] as ts.PropertyDeclaration).parent.name?.text))) + .toEqual(new Set(['TestDir', 'OtherDir'])); }); }); @@ -1204,40 +1257,31 @@ runInEachFileSystem(() => { .toEqual('TestDir'); }); - it('returns empty list when binding does not match any directive output', () => { - const fileName = absoluteFrom('/main.ts'); - const dirFile = absoluteFrom('/dir.ts'); - const {program, templateTypeChecker} = setup([ - { - fileName, - templates: {'Cmp': `
    `}, - declarations: [ - { - name: 'TestDir', - selector: '[dir]', - file: dirFile, - type: 'directive', - outputs: {outputA: 'outputA'}, - }, - ] - }, - { - fileName: dirFile, - source: `export class TestDir {outputA!: EventEmitter;}`, - templates: {}, - } - ]); - const sf = getSourceFileOrError(program, fileName); - const cmp = getClass(sf, 'Cmp'); + it('returns addEventListener binding to native element when no match to any directive output', + () => { + const fileName = absoluteFrom('/main.ts'); + const {program, templateTypeChecker} = setup([ + { + fileName, + templates: {'Cmp': `
    `}, + }, + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); - const nodes = templateTypeChecker.getTemplate(cmp)!; + const nodes = templateTypeChecker.getTemplate(cmp)!; - const outputABinding = (nodes[0] as TmplAstElement).outputs[0]; - const symbol = templateTypeChecker.getSymbolOfNode(outputABinding, cmp); - expect(symbol).toBeNull(); - }); + const outputABinding = (nodes[0] as TmplAstElement).outputs[0]; + const symbol = templateTypeChecker.getSymbolOfNode(outputABinding, cmp)!; + assertOutputBindingSymbol(symbol); + expect(program.getTypeChecker().symbolToString(symbol.bindings[0].tsSymbol!)) + .toEqual('addEventListener'); - it('returns empty list when checkTypeOfOutputEvents is false', () => { + const eventSymbol = templateTypeChecker.getSymbolOfNode(outputABinding.handler, cmp)!; + assertExpressionSymbol(eventSymbol); + }); + + it('still returns binding when checkTypeOfOutputEvents is false', () => { const fileName = absoluteFrom('/main.ts'); const dirFile = absoluteFrom('/dir.ts'); const {program, templateTypeChecker} = setup( @@ -1268,9 +1312,63 @@ runInEachFileSystem(() => { const nodes = templateTypeChecker.getTemplate(cmp)!; const outputABinding = (nodes[0] as TmplAstElement).outputs[0]; - const symbol = templateTypeChecker.getSymbolOfNode(outputABinding, cmp); - // TODO(atscott): should type checker still generate the subscription in this case? - expect(symbol).toBeNull(); + const symbol = templateTypeChecker.getSymbolOfNode(outputABinding, cmp)!; + assertOutputBindingSymbol(symbol); + expect( + (symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration).name.getText()) + .toEqual('outputA'); + expect((symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) + .parent.name?.text) + .toEqual('TestDir'); + }); + + + it('returns output symbol for two way binding', () => { + const fileName = absoluteFrom('/main.ts'); + const dirFile = absoluteFrom('/dir.ts'); + const {program, templateTypeChecker} = setup([ + { + fileName, + templates: {'Cmp': `
    `}, + source: ` + export class Cmp { + value = ''; + }`, + declarations: [ + { + name: 'TestDir', + selector: '[dir]', + file: dirFile, + type: 'directive', + inputs: {ngModel: 'ngModel'}, + outputs: {ngModelChange: 'ngModelChange'}, + }, + ] + }, + { + fileName: dirFile, + source: ` + export class TestDir { + ngModel!: string; + ngModelChange!: EventEmitter; + }`, + templates: {}, + } + ]); + const sf = getSourceFileOrError(program, fileName); + const cmp = getClass(sf, 'Cmp'); + + const nodes = templateTypeChecker.getTemplate(cmp)!; + + const outputABinding = (nodes[0] as TmplAstElement).outputs[0]; + const symbol = templateTypeChecker.getSymbolOfNode(outputABinding, cmp)!; + assertOutputBindingSymbol(symbol); + expect( + (symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration).name.getText()) + .toEqual('ngModelChange'); + expect((symbol.bindings[0].tsSymbol!.declarations[0] as ts.PropertyDeclaration) + .parent.name?.text) + .toEqual('TestDir'); }); }); @@ -1476,6 +1574,10 @@ function assertExpressionSymbol(tSymbol: Symbol): asserts tSymbol is ExpressionS expect(tSymbol.kind).toEqual(SymbolKind.Expression); } +function assertPipeSymbol(tSymbol: Symbol): asserts tSymbol is PipeSymbol { + expect(tSymbol.kind).toEqual(SymbolKind.Pipe); +} + function assertElementSymbol(tSymbol: Symbol): asserts tSymbol is ElementSymbol { expect(tSymbol.kind).toEqual(SymbolKind.Element); } @@ -1485,6 +1587,8 @@ function assertDomBindingSymbol(tSymbol: Symbol): asserts tSymbol is DomBindingS } export function setup(targets: TypeCheckingTarget[], config?: Partial) { - return baseTestSetup( - targets, {inlining: false, config: {...config, enableTemplateTypeChecker: true}}); + return baseTestSetup(targets, { + inlining: false, + config: {...config, enableTemplateTypeChecker: true, useInlineTypeConstructors: false} + }); } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker_spec.ts index 3a06cc6459..3774e4f9ee 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker_spec.ts @@ -168,184 +168,6 @@ runInEachFileSystem(() => { expect(diags.length).toBe(1); expect(diags[0].code).toBe(ngErrorCode(ErrorCode.INLINE_TCB_REQUIRED)); }); - - it('should produce errors for components that require type constructor inlining', () => { - const fileName = absoluteFrom('/main.ts'); - const dirFile = absoluteFrom('/dir.ts'); - const {program, templateTypeChecker} = setup( - [ - { - fileName, - source: `export class Cmp {}`, - templates: {'Cmp': '
    '}, - declarations: [{ - name: 'TestDir', - selector: '[dir]', - file: dirFile, - type: 'directive', - isGeneric: true, - }] - }, - { - fileName: dirFile, - source: ` - // A non-exported interface used as a type bound for a generic directive causes - // an inline type constructor to be required. - interface NotExported {} - export class TestDir {}`, - templates: {}, - } - ], - {inlining: false}); - const sf = getSourceFileOrError(program, fileName); - const diags = templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram); - expect(diags.length).toBe(1); - expect(diags[0].code).toBe(ngErrorCode(ErrorCode.INLINE_TYPE_CTOR_REQUIRED)); - - // The relatedInformation of the diagnostic should point to the directive which required - // the inline type constructor. - const dirSf = getSourceFileOrError(program, dirFile); - expect(diags[0].relatedInformation).not.toBeUndefined(); - expect(diags[0].relatedInformation!.length).toBe(1); - expect(diags[0].relatedInformation![0].file).not.toBeUndefined(); - expect(absoluteFromSourceFile(diags[0].relatedInformation![0].file!)).toBe(dirSf.fileName); - }); - }); - - describe('template overrides', () => { - it('should override a simple template', () => { - const fileName = absoluteFrom('/main.ts'); - const {program, templateTypeChecker} = setup([{ - fileName, - templates: {'Cmp': '
    {{original}}
    '}, - }]); - - const sf = getSourceFileOrError(program, fileName); - const cmp = getClass(sf, 'Cmp'); - - const tcbReal = templateTypeChecker.getTypeCheckBlock(cmp)!; - expect(tcbReal.getText()).toContain('original'); - - templateTypeChecker.overrideComponentTemplate(cmp, '
    {{override}}
    '); - const tcbOverridden = templateTypeChecker.getTypeCheckBlock(cmp); - expect(tcbOverridden).not.toBeNull(); - expect(tcbOverridden!.getText()).not.toContain('original'); - expect(tcbOverridden!.getText()).toContain('override'); - }); - - it('should clear overrides on request', () => { - const fileName = absoluteFrom('/main.ts'); - const {program, templateTypeChecker} = setup([{ - fileName, - templates: {'Cmp': '
    {{original}}
    '}, - }]); - - const sf = getSourceFileOrError(program, fileName); - const cmp = getClass(sf, 'Cmp'); - - templateTypeChecker.overrideComponentTemplate(cmp, '
    {{override}}
    '); - const tcbOverridden = templateTypeChecker.getTypeCheckBlock(cmp)!; - expect(tcbOverridden.getText()).not.toContain('original'); - expect(tcbOverridden.getText()).toContain('override'); - - templateTypeChecker.resetOverrides(); - - // The template should be back to the original. - const tcbReal = templateTypeChecker.getTypeCheckBlock(cmp)!; - expect(tcbReal.getText()).toContain('original'); - expect(tcbReal.getText()).not.toContain('override'); - }); - - it('should override a template and make use of previously unused directives', () => { - const fileName = absoluteFrom('/main.ts'); - const dirFile = absoluteFrom('/dir.ts'); - const {program, templateTypeChecker} = setup( - [ - { - fileName, - source: `export class Cmp {}`, - templates: {'Cmp': '
    '}, - declarations: [{ - name: 'TestDir', - selector: '[dir]', - file: dirFile, - type: 'directive', - inputs: {'input': 'input'}, - isGeneric: true, - }] - }, - { - fileName: dirFile, - source: `export class TestDir {}`, - templates: {}, - } - ], - {inlining: false}); - const sf = getSourceFileOrError(program, fileName); - const cmp = getClass(sf, 'Cmp'); - - // TestDir is initially unused. Note that this checks the entire text of the ngtypecheck - // file, to ensure it captures not just the TCB function but also any inline type - // constructors. - const tcbReal = templateTypeChecker.getTypeCheckBlock(cmp)!; - expect(tcbReal.getSourceFile().text).not.toContain('TestDir'); - - templateTypeChecker.overrideComponentTemplate(cmp, '
    '); - - const tcbOverridden = templateTypeChecker.getTypeCheckBlock(cmp); - expect(tcbOverridden).not.toBeNull(); - expect(tcbOverridden!.getSourceFile().text).toContain('TestDir'); - }); - - it('should not invalidate other templates when an override is requested', () => { - const file1 = absoluteFrom('/file1.ts'); - const file2 = absoluteFrom('/file2.ts'); - const {program, templateTypeChecker, programStrategy} = setup([ - {fileName: file1, templates: {'Cmp1': '
    '}}, - {fileName: file2, templates: {'Cmp2': ''}} - ]); - - const cmp1 = getClass(getSourceFileOrError(program, file1), 'Cmp1'); - const cmp2 = getClass(getSourceFileOrError(program, file2), 'Cmp2'); - - // To test this scenario, Cmp1's type check block will be captured, then Cmp2's template - // will be overridden. Cmp1's type check block should not change as a result. - const originalTcb = templateTypeChecker.getTypeCheckBlock(cmp1)!; - - templateTypeChecker.overrideComponentTemplate(cmp2, '

    '); - - // Trigger generation of the TCB for Cmp2. - templateTypeChecker.getTypeCheckBlock(cmp2); - - // Verify that Cmp1's TCB has not changed. - const currentTcb = templateTypeChecker.getTypeCheckBlock(cmp1)!; - expect(currentTcb).toBe(originalTcb); - }); - }); - - it('should allow get diagnostics for a single component', () => { - const fileName = absoluteFrom('/main.ts'); - - const {program, templateTypeChecker} = setup([{ - fileName, - templates: { - 'Cmp1': '', - 'Cmp2': '' - }, - }]); - const sf = getSourceFileOrError(program, fileName); - const cmp1 = getClass(sf, 'Cmp1'); - const cmp2 = getClass(sf, 'Cmp2'); - - const diags1 = templateTypeChecker.getDiagnosticsForComponent(cmp1); - expect(diags1.length).toBe(1); - expect(diags1[0].messageText).toContain('invalid-element-a'); - expect(diags1[0].messageText).not.toContain('invalid-element-b'); - - const diags2 = templateTypeChecker.getDiagnosticsForComponent(cmp2); - expect(diags2.length).toBe(1); - expect(diags2[0].messageText).toContain('invalid-element-b'); - expect(diags2[0].messageText).not.toContain('invalid-element-a'); }); describe('getTemplateOfComponent()', () => { @@ -363,24 +185,6 @@ runInEachFileSystem(() => { expect(nodes).not.toBeNull(); expect(nodes[0].sourceSpan.start.file.content).toBe('
    Template
    '); }); - - it('should provide access to an overridden template', () => { - const fileName = absoluteFrom('/main.ts'); - const {program, templateTypeChecker} = setup([{ - fileName, - templates: { - 'Cmp': '
    Template
    ', - }, - }]); - const cmp = getClass(getSourceFileOrError(program, fileName), 'Cmp'); - - templateTypeChecker.overrideComponentTemplate(cmp, '
    Overridden
    '); - templateTypeChecker.getDiagnosticsForComponent(cmp); - - const nodes = templateTypeChecker.getTemplate(cmp)!; - expect(nodes).not.toBeNull(); - expect(nodes[0].sourceSpan.start.file.content).toBe('
    Overridden
    '); - }); }); }); }); 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 f490cda7f5..6cd0410e34 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 @@ -10,6 +10,7 @@ import * as ts from 'typescript'; import {absoluteFrom, AbsoluteFsPath, getFileSystem, getSourceFileOrError, LogicalFileSystem, NgtscCompilerHost} from '../../file_system'; import {runInEachFileSystem, TestFile} from '../../file_system/testing'; import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, Reference, ReferenceEmitter} from '../../imports'; +import {NOOP_PERF_RECORDER} from '../../perf'; import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {getDeclaration, makeProgram} from '../../testing'; import {getRootDirs} from '../../util/src/typescript'; @@ -43,7 +44,7 @@ runInEachFileSystem(() => { const file = new TypeCheckFile( _('/_typecheck_.ts'), ALL_ENABLED_CONFIG, new ReferenceEmitter([]), /* reflector */ null!, host); - const sf = file.render(); + const sf = file.render(false /* removeComments */); expect(sf).toContain('export const IS_A_MODULE = true;'); }); @@ -74,7 +75,7 @@ TestClass.ngTypeCtor({value: 'test'}); ]); const ctx = new TypeCheckContextImpl( ALL_ENABLED_CONFIG, host, new TestMappingStrategy(), emitter, reflectionHost, - new TestTypeCheckingHost(), InliningMode.InlineOps); + new TestTypeCheckingHost(), InliningMode.InlineOps, NOOP_PERF_RECORDER); const TestClass = getDeclaration(program, _('/main.ts'), 'TestClass', isNamedClassDeclaration); const pendingFile = makePendingFile(); @@ -113,7 +114,7 @@ TestClass.ngTypeCtor({value: 'test'}); const pendingFile = makePendingFile(); const ctx = new TypeCheckContextImpl( ALL_ENABLED_CONFIG, host, new TestMappingStrategy(), emitter, reflectionHost, - new TestTypeCheckingHost(), InliningMode.InlineOps); + new TestTypeCheckingHost(), InliningMode.InlineOps, NOOP_PERF_RECORDER); const TestClass = getDeclaration(program, _('/main.ts'), 'TestClass', isNamedClassDeclaration); ctx.addInlineTypeCtor( @@ -158,7 +159,7 @@ TestClass.ngTypeCtor({value: 'test'}); const pendingFile = makePendingFile(); const ctx = new TypeCheckContextImpl( ALL_ENABLED_CONFIG, host, new TestMappingStrategy(), emitter, reflectionHost, - new TestTypeCheckingHost(), InliningMode.InlineOps); + new TestTypeCheckingHost(), InliningMode.InlineOps, NOOP_PERF_RECORDER); const TestClass = getDeclaration(program, _('/main.ts'), 'TestClass', isNamedClassDeclaration); ctx.addInlineTypeCtor( diff --git a/packages/compiler-cli/src/ngtsc/util/src/typescript.ts b/packages/compiler-cli/src/ngtsc/util/src/typescript.ts index f8ca1fb619..63af2e8206 100644 --- a/packages/compiler-cli/src/ngtsc/util/src/typescript.ts +++ b/packages/compiler-cli/src/ngtsc/util/src/typescript.ts @@ -92,7 +92,9 @@ export function isExported(node: DeclarationNode): boolean { topLevel.modifiers.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword); } -export function getRootDirs(host: ts.CompilerHost, options: ts.CompilerOptions): AbsoluteFsPath[] { +export function getRootDirs( + host: Pick, + options: ts.CompilerOptions): AbsoluteFsPath[] { const rootDirs: string[] = []; if (options.rootDirs !== undefined) { rootDirs.push(...options.rootDirs); diff --git a/packages/compiler-cli/src/perform_compile.ts b/packages/compiler-cli/src/perform_compile.ts index cde0281fc2..5220547af9 100644 --- a/packages/compiler-cli/src/perform_compile.ts +++ b/packages/compiler-cli/src/perform_compile.ts @@ -9,7 +9,8 @@ import {isSyntaxError, Position} from '@angular/compiler'; import * as ts from 'typescript'; -import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, relative, resolve} from '../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, ReadonlyFileSystem, relative, resolve} from '../src/ngtsc/file_system'; +import {NgCompilerOptions} from './ngtsc/core/api'; import {replaceTsWithNgInErrors} from './ngtsc/diagnostics'; import * as api from './transformers/api'; @@ -109,10 +110,8 @@ export function formatDiagnostics( } /** Used to read configuration files. */ -// TODO(ayazhafiz): split FileSystem into a ReadonlyFileSystem and make this a -// subset of that. -export type ConfigurationHost = - Pick; +export type ConfigurationHost = Pick< + ReadonlyFileSystem, 'readFile'|'exists'|'lstat'|'resolve'|'join'|'dirname'|'extname'|'pwd'>; export interface ParsedConfiguration { project: string; @@ -134,58 +133,44 @@ export function calcProjectFileAndBasePath( return {projectFile, basePath}; } -export function createNgCompilerOptions( - basePath: string, config: any, tsOptions: ts.CompilerOptions): api.CompilerOptions { - // enableIvy `ngtsc` is an alias for `true`. - const {angularCompilerOptions = {}} = config; - const {enableIvy} = angularCompilerOptions; - angularCompilerOptions.enableIvy = enableIvy !== false && enableIvy !== 'tsc'; - - return {...tsOptions, ...angularCompilerOptions, genDir: basePath, basePath}; -} - export function readConfiguration( - project: string, existingOptions?: ts.CompilerOptions, + project: string, existingOptions?: api.CompilerOptions, host: ConfigurationHost = getFileSystem()): ParsedConfiguration { try { - const {projectFile, basePath} = calcProjectFileAndBasePath(project, host); + const fs = getFileSystem(); - const readExtendedConfigFile = - (configFile: string, existingConfig?: any): {config?: any, error?: ts.Diagnostic} => { - const {config, error} = - ts.readConfigFile(configFile, file => host.readFile(host.resolve(file))); + const readConfigFile = (configFile: string) => + ts.readConfigFile(configFile, file => host.readFile(host.resolve(file))); + const readAngularCompilerOptions = + (configFile: string, parentOptions: NgCompilerOptions = {}): NgCompilerOptions => { + const {config, error} = readConfigFile(configFile); if (error) { - return {error}; + // Errors are handled later on by 'parseJsonConfigFileContent' + return parentOptions; } // we are only interested into merging 'angularCompilerOptions' as // other options like 'compilerOptions' are merged by TS - const baseConfig = existingConfig || config; - if (existingConfig) { - baseConfig.angularCompilerOptions = { - ...config.angularCompilerOptions, - ...baseConfig.angularCompilerOptions - }; - } + const existingNgCompilerOptions = {...config.angularCompilerOptions, ...parentOptions}; - if (config.extends) { - let extendedConfigPath = host.resolve(host.dirname(configFile), config.extends); - extendedConfigPath = host.extname(extendedConfigPath) ? - extendedConfigPath : - absoluteFrom(`${extendedConfigPath}.json`); + if (config.extends && typeof config.extends === 'string') { + const extendedConfigPath = getExtendedConfigPath( + configFile, config.extends, host, fs, + ); - if (host.exists(extendedConfigPath)) { - // Call read config recursively as TypeScript only merges CompilerOptions - return readExtendedConfigFile(extendedConfigPath, baseConfig); + if (extendedConfigPath !== null) { + // Call readAngularCompilerOptions recursively to merge NG Compiler options + return readAngularCompilerOptions(extendedConfigPath, existingNgCompilerOptions); } } - return {config: baseConfig}; + return existingNgCompilerOptions; }; - const {config, error} = readExtendedConfigFile(projectFile); - + const {projectFile, basePath} = calcProjectFileAndBasePath(project, host); + const configFileName = host.resolve(host.pwd(), projectFile); + const {config, error} = readConfigFile(projectFile); if (error) { return { project, @@ -195,19 +180,21 @@ export function readConfiguration( emitFlags: api.EmitFlags.Default }; } - const parseConfigHost = { - useCaseSensitiveFileNames: true, - fileExists: host.exists.bind(host), - readDirectory: ts.sys.readDirectory, - readFile: ts.sys.readFile + const existingCompilerOptions: api.CompilerOptions = { + genDir: basePath, + basePath, + ...readAngularCompilerOptions(configFileName), + ...existingOptions, }; - const configFileName = host.resolve(host.pwd(), projectFile); - const parsed = ts.parseJsonConfigFileContent( - config, parseConfigHost, basePath, existingOptions, configFileName); - const rootNames = parsed.fileNames; - const projectReferences = parsed.projectReferences; - const options = createNgCompilerOptions(basePath, config, parsed.options); + const parseConfigHost = createParseConfigHost(host, fs); + const {options, errors, fileNames: rootNames, projectReferences} = + ts.parseJsonConfigFileContent( + config, parseConfigHost, basePath, existingCompilerOptions, configFileName); + + // Coerce to boolean as `enableIvy` can be `ngtsc|true|false|undefined` here. + options.enableIvy = !!(options.enableIvy ?? true); + let emitFlags = api.EmitFlags.Default; if (!(options.skipMetadataEmit || options.flatModuleOutFile)) { emitFlags |= api.EmitFlags.Metadata; @@ -215,14 +202,7 @@ export function readConfiguration( if (options.skipTemplateCodegen) { emitFlags = emitFlags & ~api.EmitFlags.Codegen; } - return { - project: projectFile, - rootNames, - projectReferences, - options, - errors: parsed.errors, - emitFlags - }; + return {project: projectFile, rootNames, projectReferences, options, errors, emitFlags}; } catch (e) { const errors: ts.Diagnostic[] = [{ category: ts.DiagnosticCategory.Error, @@ -237,6 +217,47 @@ export function readConfiguration( } } +function createParseConfigHost(host: ConfigurationHost, fs = getFileSystem()): ts.ParseConfigHost { + return { + fileExists: host.exists.bind(host), + readDirectory: ts.sys.readDirectory, + readFile: host.readFile.bind(host), + useCaseSensitiveFileNames: fs.isCaseSensitive(), + }; +} + +function getExtendedConfigPath( + configFile: string, extendsValue: string, host: ConfigurationHost, + fs: FileSystem): AbsoluteFsPath|null { + let extendedConfigPath: AbsoluteFsPath|null = null; + + if (extendsValue.startsWith('.') || fs.isRooted(extendsValue)) { + extendedConfigPath = host.resolve(host.dirname(configFile), extendsValue); + extendedConfigPath = host.extname(extendedConfigPath) ? + extendedConfigPath : + absoluteFrom(`${extendedConfigPath}.json`); + } else { + const parseConfigHost = createParseConfigHost(host, fs); + + // Path isn't a rooted or relative path, resolve like a module. + const { + resolvedModule, + } = + ts.nodeModuleNameResolver( + extendsValue, configFile, + {moduleResolution: ts.ModuleResolutionKind.NodeJs, resolveJsonModule: true}, + parseConfigHost); + if (resolvedModule) { + extendedConfigPath = absoluteFrom(resolvedModule.resolvedFileName); + } + } + + if (extendedConfigPath !== null && host.exists(extendedConfigPath)) { + return extendedConfigPath; + } + + return null; +} export interface PerformCompilationResult { diagnostics: Diagnostics; program?: api.Program; diff --git a/packages/compiler-cli/src/transformers/compiler_host.ts b/packages/compiler-cli/src/transformers/compiler_host.ts index 945aeb07b5..185f92aa51 100644 --- a/packages/compiler-cli/src/transformers/compiler_host.ts +++ b/packages/compiler-cli/src/transformers/compiler_host.ts @@ -584,13 +584,15 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos if (this.originalFileExists(packageFile)) { // Once we see a package.json file, assume false until it we find the bundle index. result = false; - const packageContent: any = JSON.parse(assert(this.context.readFile(packageFile))); + const packageContent = + JSON.parse(assert(this.context.readFile(packageFile))) as {typings: string}; if (packageContent.typings) { const typings = path.normalize(path.join(directory, packageContent.typings)); if (DTS.test(typings)) { const metadataFile = typings.replace(DTS, '.metadata.json'); if (this.originalFileExists(metadataFile)) { - const metadata = JSON.parse(assert(this.context.readFile(metadataFile))); + const metadata = JSON.parse(assert(this.context.readFile(metadataFile))) as + {flatModuleIndexRedirect: string, importAs: string}; if (metadata.flatModuleIndexRedirect) { this.flatModuleIndexRedirectNames.add(typings); // Note: don't set result = true, diff --git a/packages/compiler-cli/src/transformers/downlevel_decorators_transform.ts b/packages/compiler-cli/src/transformers/downlevel_decorators_transform.ts index 1e27e784fa..a5533f38a1 100644 --- a/packages/compiler-cli/src/transformers/downlevel_decorators_transform.ts +++ b/packages/compiler-cli/src/transformers/downlevel_decorators_transform.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; import {Decorator, ReflectionHost} from '../ngtsc/reflection'; -import {isAliasImportDeclaration, patchAliasReferenceResolutionOrDie} from './patch_alias_reference_resolution'; +import {isAliasImportDeclaration, loadIsReferencedAliasDeclarationPatch} from './patch_alias_reference_resolution'; /** * Whether a given decorator should be treated as an Angular decorator. @@ -347,7 +347,12 @@ export function getDownlevelDecoratorsTransform( isCore: boolean, isClosureCompilerEnabled: boolean, skipClassDecorators: boolean): ts.TransformerFactory { return (context: ts.TransformationContext) => { - let referencedParameterTypes = new Set(); + // Ensure that referenced type symbols are not elided by TypeScript. Imports for + // such parameter type symbols previously could be type-only, but now might be also + // used in the `ctorParameters` static property as a value. We want to make sure + // that TypeScript does not elide imports for such type references. Read more + // about this in the description for `loadIsReferencedAliasDeclarationPatch`. + const referencedParameterTypes = loadIsReferencedAliasDeclarationPatch(context); /** * Converts an EntityName (from a type annotation) to an expression (accessing a value). @@ -595,12 +600,6 @@ export function getDownlevelDecoratorsTransform( } return (sf: ts.SourceFile) => { - // Ensure that referenced type symbols are not elided by TypeScript. Imports for - // such parameter type symbols previously could be type-only, but now might be also - // used in the `ctorParameters` static property as a value. We want to make sure - // that TypeScript does not elide imports for such type references. Read more - // about this in the description for `patchAliasReferenceResolution`. - patchAliasReferenceResolutionOrDie(context, referencedParameterTypes); // Downlevel decorators and constructor parameter types. We will keep track of all // referenced constructor parameter types so that we can instruct TypeScript to // not elide their imports if they previously were only type-only. diff --git a/packages/compiler-cli/src/transformers/metadata_reader.ts b/packages/compiler-cli/src/transformers/metadata_reader.ts index 422bc2ed4e..3acebdd71c 100644 --- a/packages/compiler-cli/src/transformers/metadata_reader.ts +++ b/packages/compiler-cli/src/transformers/metadata_reader.ts @@ -70,8 +70,9 @@ function readMetadataFile(host: MetadataReaderHost, dtsFilePath: string): Module return undefined; } try { - const metadataOrMetadatas = JSON.parse(host.readFile(metadataPath)); - const metadatas: ModuleMetadata[] = metadataOrMetadatas ? + const metadataOrMetadatas = + JSON.parse(host.readFile(metadataPath)) as ModuleMetadata | ModuleMetadata[] | undefined; + const metadatas = metadataOrMetadatas ? (Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) : []; if (metadatas.length) { diff --git a/packages/compiler-cli/src/transformers/node_emitter.ts b/packages/compiler-cli/src/transformers/node_emitter.ts index 6a27f1798b..2a1cc6fd78 100644 --- a/packages/compiler-cli/src/transformers/node_emitter.ts +++ b/packages/compiler-cli/src/transformers/node_emitter.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinVar, CastExpr, ClassStmt, CommaExpr, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, ExpressionStatement, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LeadingComment, leadingComment, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, LocalizedString, NotExpr, ParseSourceFile, ParseSourceSpan, PartialModule, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, TypeofExpr, UnaryOperator, UnaryOperatorExpr, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler'; +import {AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinVar, CastExpr, ClassStmt, CommaExpr, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, ExpressionStatement, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LeadingComment, leadingComment, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, LocalizedString, NotExpr, ParseSourceFile, ParseSourceSpan, PartialModule, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, TaggedTemplateExpr, ThrowStmt, TryCatchStmt, TypeofExpr, UnaryOperator, UnaryOperatorExpr, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler'; import * as ts from 'typescript'; import {attachComments} from '../ngtsc/translator'; @@ -544,6 +544,10 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { expr.args.map(arg => arg.visitExpression(this, null)))); } + visitTaggedTemplateExpr(expr: TaggedTemplateExpr): RecordedNode { + throw new Error('tagged templates are not supported in pre-ivy mode.'); + } + visitInstantiateExpr(expr: InstantiateExpr): RecordedNode { return this.postProcess( expr, @@ -780,7 +784,6 @@ function modifierFromModifier(modifier: StmtModifier): ts.Modifier { case StmtModifier.Static: return ts.createToken(ts.SyntaxKind.StaticKeyword); } - return error(`unknown statement modifier`); } function translateModifiers(modifiers: StmtModifier[]|null): ts.Modifier[]|undefined { diff --git a/packages/compiler-cli/src/transformers/patch_alias_reference_resolution.ts b/packages/compiler-cli/src/transformers/patch_alias_reference_resolution.ts index f1ccea42d8..4f9932ca78 100644 --- a/packages/compiler-cli/src/transformers/patch_alias_reference_resolution.ts +++ b/packages/compiler-cli/src/transformers/patch_alias_reference_resolution.ts @@ -17,9 +17,12 @@ interface TransformationContextWithResolver extends ts.TransformationContext { getEmitResolver: () => EmitResolver; } +const patchedReferencedAliasesSymbol = Symbol('patchedReferencedAliases'); + /** Describes a subset of the TypeScript internal emit resolver. */ interface EmitResolver { - isReferencedAliasDeclaration?(node: ts.Node, checkChildren?: boolean): void; + isReferencedAliasDeclaration?(node: ts.Node, ...args: unknown[]): void; + [patchedReferencedAliasesSymbol]?: Set; } /** @@ -40,10 +43,10 @@ interface EmitResolver { * This is a trick the CLI used in the past for constructor parameter downleveling in JIT: * https://github.com/angular/angular-cli/blob/b3f84cc5184337666ce61c07b7b9df418030106f/packages/ngtools/webpack/src/transformers/ctor-parameters.ts#L323-L325 * The trick is not ideal though as it preserves the full import (as outlined before), and it - * results in a slow-down due to the type checker being involved multiple times. The CLI - * worked around this import preserving issue by having another complex post-process step that - * detects and elides unused imports. Note that these unused imports could cause unused chunks - * being generated by Webpack if the application or library is not marked as side-effect free. + * results in a slow-down due to the type checker being involved multiple times. The CLI worked + * around this import preserving issue by having another complex post-process step that detects and + * elides unused imports. Note that these unused imports could cause unused chunks being generated + * by Webpack if the application or library is not marked as side-effect free. * * This is not ideal though, as we basically re-implement the complex import usage resolution * from TypeScript. We can do better by letting TypeScript do the import eliding, but providing @@ -60,32 +63,46 @@ interface EmitResolver { * `emitDecoratorMetadata` flag is enabled. TypeScript basically surfaces the same problem and * solves it conceptually the same way, but obviously doesn't need to access an `@internal` API. * + * The set that is returned by this function is meant to be filled with import declaration nodes + * that have been referenced in a value-position by the transform, such the installed patch can + * ensure that those import declarations are not elided. + * * See below. Note that this uses sourcegraph as the TypeScript checker file doesn't display on * Github. * https://sourcegraph.com/github.com/microsoft/TypeScript@3eaa7c65f6f076a08a5f7f1946fd0df7c7430259/-/blob/src/compiler/checker.ts#L31219-31257 */ -export function patchAliasReferenceResolutionOrDie( - context: ts.TransformationContext, referencedAliases: Set): void { +export function loadIsReferencedAliasDeclarationPatch(context: ts.TransformationContext): + Set { // If the `getEmitResolver` method is not available, TS most likely changed the // internal structure of the transformation context. We will abort gracefully. if (!isTransformationContextWithEmitResolver(context)) { throwIncompatibleTransformationContextError(); - return; } const emitResolver = context.getEmitResolver(); - const originalReferenceResolution = emitResolver.isReferencedAliasDeclaration; + + // The emit resolver may have been patched already, in which case we return the set of referenced + // aliases that was created when the patch was first applied. + // See https://github.com/angular/angular/issues/40276. + const existingReferencedAliases = emitResolver[patchedReferencedAliasesSymbol]; + if (existingReferencedAliases !== undefined) { + return existingReferencedAliases; + } + + const originalIsReferencedAliasDeclaration = emitResolver.isReferencedAliasDeclaration; // If the emit resolver does not have a function called `isReferencedAliasDeclaration`, then // we abort gracefully as most likely TS changed the internal structure of the emit resolver. - if (originalReferenceResolution === undefined) { + if (originalIsReferencedAliasDeclaration === undefined) { throwIncompatibleTransformationContextError(); - return; } + + const referencedAliases = new Set(); emitResolver.isReferencedAliasDeclaration = function(node, ...args) { if (isAliasImportDeclaration(node) && referencedAliases.has(node)) { return true; } - return originalReferenceResolution.call(emitResolver, node, ...args); + return originalIsReferencedAliasDeclaration.call(emitResolver, node, ...args); }; + return emitResolver[patchedReferencedAliasesSymbol] = referencedAliases; } /** @@ -110,7 +127,7 @@ function isTransformationContextWithEmitResolver(context: ts.TransformationConte * declaration reference resolution could not be monkey-patched. The error will * also propose potential solutions that can be applied by developers. */ -function throwIncompatibleTransformationContextError() { +function throwIncompatibleTransformationContextError(): never { throw Error( 'Unable to downlevel Angular decorators due to an incompatible TypeScript ' + 'version.\nIf you recently updated TypeScript and this issue surfaces now, consider ' + diff --git a/packages/compiler-cli/src/transformers/program.ts b/packages/compiler-cli/src/transformers/program.ts index f82477b43a..ca50688a50 100644 --- a/packages/compiler-cli/src/transformers/program.ts +++ b/packages/compiler-cli/src/transformers/program.ts @@ -668,10 +668,7 @@ class AngularCompilerProgram implements Program { // - we cache all the files in the hostAdapter // - new new stubs use the exactly same imports/exports as the old once (we assert that in // hostAdapter.updateGeneratedFile). - // TS 4.1+ stores the reuse state in the new program - const checkReuseProgram = - (ts.versionMajorMinor as string) === '4.0' ? tmpProgram : this._tsProgram; - if (tsStructureIsReused(checkReuseProgram) !== StructureIsReused.Completely) { + if (tsStructureIsReused(this._tsProgram) !== StructureIsReused.Completely) { throw new Error(`Internal Error: The structure of the program changed during codegen.`); } } diff --git a/packages/compiler-cli/src/typescript_support.ts b/packages/compiler-cli/src/typescript_support.ts index efdd34c8de..ec058be8d7 100644 --- a/packages/compiler-cli/src/typescript_support.ts +++ b/packages/compiler-cli/src/typescript_support.ts @@ -15,7 +15,7 @@ import {compareVersions} from './diagnostics/typescript_version'; * Note: this check is disabled in g3, search for * `angularCompilerOptions.disableTypeScriptVersionCheck` config param value in g3. */ -const MIN_TS_VERSION = '4.0.0'; +const MIN_TS_VERSION = '4.2.3'; /** * Supremum of supported TypeScript versions @@ -25,7 +25,7 @@ const MIN_TS_VERSION = '4.0.0'; * Note: this check is disabled in g3, search for * `angularCompilerOptions.disableTypeScriptVersionCheck` config param value in g3. */ -const MAX_TS_VERSION = '4.2.0'; +const MAX_TS_VERSION = '4.3.0'; /** * The currently used version of TypeScript, which can be adjusted for testing purposes using diff --git a/packages/compiler-cli/test/BUILD.bazel b/packages/compiler-cli/test/BUILD.bazel index a0a217a5a5..373108771b 100644 --- a/packages/compiler-cli/test/BUILD.bazel +++ b/packages/compiler-cli/test/BUILD.bazel @@ -135,6 +135,7 @@ ts_library( ":test_utils", "//packages/compiler", "//packages/compiler-cli", + "@npm//typescript", ], ) diff --git a/packages/compiler-cli/test/compliance/README.md b/packages/compiler-cli/test/compliance/README.md index d7f25bc316..0f63b497ed 100644 --- a/packages/compiler-cli/test/compliance/README.md +++ b/packages/compiler-cli/test/compliance/README.md @@ -38,7 +38,7 @@ Each test-case can specify: * A `description` of the test. * The `inputFiles` that will be compiled. * Additional `compilerOptions` and `angularCompilerOptions` that are passed to the compiler. -* Whether to exclude this test-case from partial compilation tests (`excludeFromPartialTests`). +* Whether to exclude this test-case from certain tests running under certain compilation modes (`compilationModeFilter`). * A collection of `expectations` definitions that will be checked against the generated files. Note that there is a JSON schema for the `TEST_CASES.json` file stored at `test_cases/test_case_schema.json`. @@ -126,6 +126,28 @@ are intelligently matched to check whether they are equivalent. `__i18nMsg__('message string', [ ['placeholder', 'pair] ], { meta: 'properties'})`. * Attribute markers - for example: `__AttributeMarker.Bindings__`. +### Source-map checks + +To check a mapping, add a `// SOURCE:` comment to the end of a line in an expectation file: + +``` + // SOURCE: "" +``` + +The generated code, stripped of the `// SOURCE: ` comment, will still be checked as normal by the +`expectEmit()` helper. But, prior to that, the source-map segments are checked to ensure that there +is a mapping from `` to `` found in the file at ``. + +Note: + +* The source-url should be absolute, with the directory containing the TEST_CASES.json file assumed + to be `/`. +* Whitespace is important and will be included when comparing the segments. +* There is a single space character between each part of the line. +* Newlines within a mapping must be escaped since the mapping and comment must all appear on a + single line of this file. + + ## Running tests The simplest way to run all the compliance tests is: @@ -163,6 +185,11 @@ bazel run //packages/compiler-cli/test/compliance/test_cases: where to replace `` with the path (relative to `test_cases`) of the directory that contains the `GOLDEN_PARTIAL.js` to update. +To update all golden partial files, the following command can be run: + +```sh +node packages/compiler-cli/test/compliance/update_all_goldens.js +``` ## Debugging test-cases diff --git a/packages/compiler-cli/test/compliance/linked/BUILD.bazel b/packages/compiler-cli/test/compliance/linked/BUILD.bazel index 304127218b..7f2f0b16fe 100644 --- a/packages/compiler-cli/test/compliance/linked/BUILD.bazel +++ b/packages/compiler-cli/test/compliance/linked/BUILD.bazel @@ -5,8 +5,11 @@ ts_library( testonly = True, srcs = ["linked_compile_spec.ts"], deps = [ + "//packages/compiler-cli/linker", "//packages/compiler-cli/linker/babel", "//packages/compiler-cli/src/ngtsc/file_system", + "//packages/compiler-cli/src/ngtsc/logging", + "//packages/compiler-cli/src/ngtsc/sourcemaps", "//packages/compiler-cli/test/compliance/test_helpers", "@npm//@types/babel__core", ], diff --git a/packages/compiler-cli/test/compliance/linked/linked_compile_spec.ts b/packages/compiler-cli/test/compliance/linked/linked_compile_spec.ts index 1dd362fb9c..503d3fc8ba 100644 --- a/packages/compiler-cli/test/compliance/linked/linked_compile_spec.ts +++ b/packages/compiler-cli/test/compliance/linked/linked_compile_spec.ts @@ -7,42 +7,71 @@ */ import {PluginObj, transformSync} from '@babel/core'; +import {needsLinking} from '../../../linker'; import {createEs2015LinkerPlugin} from '../../../linker/babel'; import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system'; +import {ConsoleLogger, LogLevel} from '../../../src/ngtsc/logging'; +import {MapAndPath, RawSourceMap, SourceFileLoader} from '../../../src/ngtsc/sourcemaps'; import {CompileResult, getBuildOutputDirectory} from '../test_helpers/compile_test'; import {ComplianceTest} from '../test_helpers/get_compliance_tests'; import {parseGoldenPartial} from '../test_helpers/golden_partials'; import {runTests} from '../test_helpers/test_runner'; -runTests('partial compile + link', linkPartials); +runTests('linked compile', linkPartials); /** * Link all the partials specified in the given `test`. * - * @param fs The mock file-system to use for linking the partials. + * @param fileSystem The mock file-system to use for linking the partials. * @param test The compliance test whose partials will be linked. */ -function linkPartials(fs: FileSystem, test: ComplianceTest): CompileResult { - const builtDirectory = getBuildOutputDirectory(fs); +function linkPartials(fileSystem: FileSystem, test: ComplianceTest): CompileResult { + const logger = new ConsoleLogger(LogLevel.debug); + const loader = new SourceFileLoader(fileSystem, logger, {}); + const builtDirectory = getBuildOutputDirectory(fileSystem); const linkerPlugin = createEs2015LinkerPlugin({ + fileSystem, + logger, // By default we don't render legacy message ids in compliance tests. enableI18nLegacyMessageIdFormat: false, + sourceMapping: test.compilerOptions?.sourceMap === true, ...test.angularCompilerOptions }); - const goldenPartialPath = fs.resolve('/GOLDEN_PARTIAL.js'); - if (!fs.exists(goldenPartialPath)) { + const goldenPartialPath = fileSystem.resolve('/GOLDEN_PARTIAL.js'); + if (!fileSystem.exists(goldenPartialPath)) { throw new Error( 'Golden partial does not exist for this test\n' + 'Try generating it by running:\n' + `bazel run //packages/compiler-cli/test/compliance/test_cases:${ test.relativePath}.golden.update`); } - const partialFile = fs.readFile(goldenPartialPath); - const partials = parseGoldenPartial(partialFile).filter(f => f.path.endsWith('.js')); - for (const partial of partials) { - const linkedSource = - applyLinker({fileName: partial.path, source: partial.content}, linkerPlugin); - safeWrite(fs, fs.resolve(builtDirectory, partial.path), linkedSource); + const partialFile = fileSystem.readFile(goldenPartialPath); + const partialFiles = parseGoldenPartial(partialFile); + + partialFiles.forEach( + f => safeWrite(fileSystem, fileSystem.resolve(builtDirectory, f.path), f.content)); + + for (const expectation of test.expectations) { + for (const {generated} of expectation.files) { + const fileName = fileSystem.resolve(builtDirectory, generated); + if (!fileSystem.exists(fileName)) { + continue; + } + const source = fileSystem.readFile(fileName); + const sourceMapPath = fileSystem.resolve(fileName + '.map'); + const sourceMap = fileSystem.exists(sourceMapPath) ? + JSON.parse(fileSystem.readFile(sourceMapPath)) as RawSourceMap : + undefined; + const {linkedSource, linkedSourceMap} = + applyLinker(builtDirectory, fileName, source, sourceMap, linkerPlugin); + + if (linkedSourceMap !== undefined) { + const mapAndPath: MapAndPath = {map: linkedSourceMap, mapPath: sourceMapPath}; + const sourceFile = loader.loadSourceFile(fileName, linkedSource, mapAndPath); + safeWrite(fileSystem, sourceMapPath, JSON.stringify(sourceFile.renderFlattenedSourceMap())); + } + safeWrite(fileSystem, fileName, linkedSource); + } } return {emittedFiles: [], errors: []}; } @@ -52,16 +81,20 @@ function linkPartials(fs: FileSystem, test: ComplianceTest): CompileResult { * * It will ignore files that do not have a `.js` extension. * - * @param file The file name and its source to be transformed using the linker. + * @param file The absolute file path and its source to be transformed using the linker. * @param linkerPlugin The linker plugin to apply. * @returns The file's source content, which has been transformed using the linker if necessary. */ -function applyLinker(file: {fileName: string; source: string}, linkerPlugin: PluginObj): string { - if (!file.fileName.endsWith('.js')) { - return file.source; +function applyLinker( + cwd: string, filename: string, source: string, sourceMap: RawSourceMap|undefined, + linkerPlugin: PluginObj): {linkedSource: string, linkedSourceMap: RawSourceMap|undefined} { + if (!filename.endsWith('.js') || !needsLinking(filename, source)) { + return {linkedSource: source, linkedSourceMap: sourceMap}; } - const result = transformSync(file.source, { - filename: file.fileName, + const result = transformSync(source, { + cwd, + filename, + sourceMaps: !!sourceMap, plugins: [linkerPlugin], parserOpts: {sourceType: 'unambiguous'}, }); @@ -71,7 +104,7 @@ function applyLinker(file: {fileName: string; source: string}, linkerPlugin: Plu if (result.code == null) { throw fail('Babel transform result does not have any code'); } - return result.code; + return {linkedSource: result.code, linkedSourceMap: result.map || undefined}; } /** diff --git a/packages/compiler-cli/test/compliance/partial/partial_compliance_goldens.bzl b/packages/compiler-cli/test/compliance/partial/partial_compliance_goldens.bzl index ea296400cd..78ba8b0d71 100644 --- a/packages/compiler-cli/test/compliance/partial/partial_compliance_goldens.bzl +++ b/packages/compiler-cli/test/compliance/partial/partial_compliance_goldens.bzl @@ -21,6 +21,9 @@ def partial_compliance_golden(filePath): entry_point = "//packages/compiler-cli/test/compliance/partial:cli.ts", templated_args = [ filePath, + # TODO(josephperrott): update dependency usages to no longer need bazel patch module resolver + # See: https://github.com/bazelbuild/rules_nodejs/wiki#--bazel_patch_module_resolver-now-defaults-to-false-2324 + "--bazel_patch_module_resolver", ], ) @@ -31,6 +34,9 @@ def partial_compliance_golden(filePath): visibility = [":__pkg__"], entry_point = "//packages/compiler-cli/test/compliance/partial:cli.ts", templated_args = [ + # TODO(josephperrott): update dependency usages to no longer need bazel patch module resolver + # See: https://github.com/bazelbuild/rules_nodejs/wiki#--bazel_patch_module_resolver-now-defaults-to-false-2324 + "--bazel_patch_module_resolver", "--node_options=--inspect-brk", filePath, ], diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/GOLDEN_PARTIAL.js index dfa610334a..4b01fe1d0e 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/GOLDEN_PARTIAL.js @@ -5,9 +5,9 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '

    test

    ', isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: '

    test

    ', isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -16,10 +16,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -29,12 +29,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -42,11 +43,27 @@ export declare class MyModule { ****************************************************************************************************/ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; +export class MathCmp { +} +MathCmp.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MathCmp, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MathCmp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MathCmp, selector: "math", ngImport: i0, template: '', isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MathCmp, [{ + type: Component, + args: [{ selector: 'math', template: '' }] + }], null, null); })(); +export class InfinityCmp { +} +InfinityCmp.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: InfinityCmp, deps: [], target: i0.ɵɵFactoryTarget.Component }); +InfinityCmp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: InfinityCmp, selector: "infinity", ngImport: i0, template: '', isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(InfinityCmp, [{ + type: Component, + args: [{ selector: 'infinity', template: '' }] + }], null, null); })(); export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '

    test

    ', isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: '

    test

    ', isInline: true, components: [{ type: MathCmp, selector: "math" }, { type: InfinityCmp, selector: "infinity" }] }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -55,25 +72,34 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent, MathCmp, InfinityCmp] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, - args: [{ declarations: [MyComponent] }] + args: [{ declarations: [MyComponent, MathCmp, InfinityCmp] }] }], null, null); })(); /**************************************************************************************************** * PARTIAL FILE: mathml.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; +export declare class MathCmp { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} +export declare class InfinityCmp { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -83,9 +109,9 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '
    Hello World!
    ', isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: '
    Hello World!
    ', isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -94,10 +120,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -107,12 +133,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -122,9 +149,9 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '
    Hello World!
    ', isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: '
    Hello World!
    ', isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -133,10 +160,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -146,12 +173,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -161,9 +189,9 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: 'in a container', isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: 'in a container', isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -172,10 +200,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -185,12 +213,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -200,18 +229,18 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '', isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: '', isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', template: '' }] }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -221,12 +250,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -239,18 +269,18 @@ export class MyComponent { this.id = 'one'; } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '
    ', isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: '
    ', isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', template: '
    ' }] }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -261,32 +291,63 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { import * as i0 from "@angular/core"; export declare class MyComponent { id: string; - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** * PARTIAL FILE: property_pure_functions.js ****************************************************************************************************/ -import { Component, NgModule } from '@angular/core'; +import { Component, Directive, Input, NgModule, Pipe } from '@angular/core'; import * as i0 from "@angular/core"; +export class DivDir { +} +DivDir.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: DivDir, deps: [], target: i0.ɵɵFactoryTarget.Directive }); +DivDir.ɵdir = i0.ɵɵngDeclareDirective({ version: "0.0.0-PLACEHOLDER", type: DivDir, selector: "div", inputs: { ternary: "ternary", pipe: "pipe", and: "and", or: "or" }, ngImport: i0 }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DivDir, [{ + type: Directive, + args: [{ selector: 'div' }] + }], null, { ternary: [{ + type: Input + }], pipe: [{ + type: Input + }], and: [{ + type: Input + }], or: [{ + type: Input + }] }); })(); +export class PipePipe { + transform(v, a, a2) { } +} +PipePipe.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: PipePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); +PipePipe.ɵpipe = i0.ɵɵngDeclarePipe({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: PipePipe, name: "pipe" }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PipePipe, [{ + type: Pipe, + args: [{ name: 'pipe' }] + }], null, null); })(); export class MyComponent { constructor() { this.id = 'one'; + this.cond = ''; + this.value = ''; + this.a = ''; + this.b = ''; + this.c = ''; } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: `
    `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + >
    `, isInline: true, directives: [{ type: DivDir, selector: "div", inputs: ["ternary", "pipe", "and", "or"] }], pipes: { "pipe": PipePipe } }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -300,26 +361,45 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent, DivDir, PipePipe] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, - args: [{ declarations: [MyComponent] }] + args: [{ declarations: [MyComponent, DivDir, PipePipe] }] }], null, null); })(); /**************************************************************************************************** * PARTIAL FILE: property_pure_functions.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; +export declare class DivDir { + ternary: any; + pipe: any; + and: any; + or: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} +export declare class PipePipe { + transform(v: any, a: any, a2: any): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵpipe: i0.ɵɵPipeDeclaration; +} export declare class MyComponent { id: string; - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + cond: string; + value: string; + a: string; + b: string; + c: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -332,9 +412,9 @@ export class MyComponent { return 'expanded'; } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", inputs: { expandedHeight: "expandedHeight", collapsedHeight: "collapsedHeight", expandedWidth: "expandedWidth", collapsedWidth: "collapsedWidth" }, host: { properties: { "@expansionHeight": "{\n value: getExpandedState(),\n params: {\n collapsedHeight: collapsedHeight,\n expandedHeight: expandedHeight\n }\n }", "@expansionWidth": "{\n value: getExpandedState(),\n params: {\n collapsedWidth: collapsedWidth,\n expandedWidth: expandedWidth\n }\n }" } }, ngImport: i0, template: { source: '...', isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", inputs: { expandedHeight: "expandedHeight", collapsedHeight: "collapsedHeight", expandedWidth: "expandedWidth", collapsedWidth: "collapsedWidth" }, host: { properties: { "@expansionHeight": "{\n value: getExpandedState(),\n params: {\n collapsedHeight: collapsedHeight,\n expandedHeight: expandedHeight\n }\n }", "@expansionWidth": "{\n value: getExpandedState(),\n params: {\n collapsedWidth: collapsedWidth,\n expandedWidth: expandedWidth\n }\n }" } }, ngImport: i0, template: '...', isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -367,10 +447,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }] }); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -385,12 +465,13 @@ export declare class MyComponent { expandedWidth: string; collapsedWidth: string; getExpandedState(): string; - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -404,9 +485,9 @@ export class MyComponent { this.color = 'red'; } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '
    ', isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: '
    ', isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -415,10 +496,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -430,12 +511,13 @@ import * as i0 from "@angular/core"; export declare class MyComponent { error: boolean; color: string; - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -445,12 +527,12 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -462,10 +544,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -475,11 +557,74 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: security_sensitive_constant_attributes.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` + + + + + + + + + + + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + + + + + + + + + + + ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: security_sensitive_constant_attributes.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} +export declare class MyModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/TEST_CASES.json index b5932cf87d..e8fdcfccbd 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/TEST_CASES.json @@ -226,6 +226,17 @@ "failureMessage": "Incorrect generated template." } ] + }, + { + "description": "should specify security-sensitive constant attributes as template literals", + "inputFiles": [ + "security_sensitive_constant_attributes.ts" + ], + "expectations": [ + { + "failureMessage": "Incorrect generated template." + } + ] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/class_style_bindings_template.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/class_style_bindings_template.js index 4c74990900..9bf4459bcc 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/class_style_bindings_template.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/class_style_bindings_template.js @@ -1,4 +1,4 @@ -MyComponent.ɵcmp = i0.ɵɵdefineComponent({type:MyComponent,selectors:[["my-component"]], +MyComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({type:MyComponent,selectors:[["my-component"]], decls: 1, vars: 4, template: function MyComponent_Template(rf,ctx){ diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/deduplicate_attributes.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/deduplicate_attributes.ts index 66d891dd9a..c4df9fcd41 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/deduplicate_attributes.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/deduplicate_attributes.ts @@ -12,4 +12,4 @@ export class MyComponent { @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/mathml.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/mathml.ts index 57c7da1aed..289ff32ec6 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/mathml.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/mathml.ts @@ -1,5 +1,13 @@ import {Component, NgModule} from '@angular/core'; +@Component({selector: 'math', template: ''}) +export class MathCmp { +} + +@Component({selector: 'infinity', template: ''}) +export class InfinityCmp { +} + @Component({ selector: 'my-component', template: '

    test

    ' @@ -7,6 +15,6 @@ import {Component, NgModule} from '@angular/core'; export class MyComponent { } -@NgModule({declarations: [MyComponent]}) +@NgModule({declarations: [MyComponent, MathCmp, InfinityCmp]}) export class MyModule { } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/property_pure_functions.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/property_pure_functions.ts index 674a467ef3..8ddbdca0de 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/property_pure_functions.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/property_pure_functions.ts @@ -1,4 +1,17 @@ -import {Component, NgModule} from '@angular/core'; +import {Component, Directive, Input, NgModule, Pipe} from '@angular/core'; + +@Directive({selector: 'div'}) +export class DivDir { + @Input() ternary!: any; + @Input() pipe!: any; + @Input() and!: any; + @Input() or!: any; +} + +@Pipe({name: 'pipe'}) +export class PipePipe { + transform(v: any, a: any, a2: any) {} +} @Component({ selector: 'my-component', @@ -11,8 +24,13 @@ import {Component, NgModule} from '@angular/core'; }) export class MyComponent { id = 'one'; + cond = ''; + value = ''; + a = ''; + b = ''; + c = ''; } -@NgModule({declarations: [MyComponent]}) +@NgModule({declarations: [MyComponent, DivDir, PipePipe]}) export class MyModule { } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/interpolations/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/interpolations/GOLDEN_PARTIAL.js index 16282198a6..5cb9b35824 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/interpolations/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/interpolations/GOLDEN_PARTIAL.js @@ -8,9 +8,9 @@ export class MyApp { this.list = []; } } -MyApp.ɵfac = function MyApp_Factory(t) { return new (t || MyApp)(); }; -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyApp, selector: "my-app", ngImport: i0, template: { source: ' {{list[0]}} {{list[1]}} {{list[2]}} {{list[3]}} {{list[4]}} {{list[5]}} {{list[6]}} {{list[7]}} {{list[8]}} ', isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyApp, [{ +MyApp.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "my-app", ngImport: i0, template: ' {{list[0]}} {{list[1]}} {{list[2]}} {{list[3]}} {{list[4]}} {{list[5]}} {{list[6]}} {{list[7]}} {{list[8]}} ', isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{ type: Component, args: [{ selector: 'my-app', @@ -19,10 +19,10 @@ MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyApp, selector: "my }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyApp] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyApp] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyApp] }] }], null, null); })(); @@ -33,11 +33,12 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { import * as i0 from "@angular/core"; export declare class MyApp { list: any[]; - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/GOLDEN_PARTIAL.js deleted file mode 100644 index 1821e346a0..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/GOLDEN_PARTIAL.js +++ /dev/null @@ -1,48 +0,0 @@ -/**************************************************************************************************** - * PARTIAL FILE: test.js - ****************************************************************************************************/ -import { Component, Directive, NgModule } from '@angular/core'; -import * as i0 from "@angular/core"; -export class I18nDirective { -} -I18nDirective.ɵfac = function I18nDirective_Factory(t) { return new (t || I18nDirective)(); }; -I18nDirective.ɵdir = i0.ɵɵngDeclareDirective({ version: 1, type: I18nDirective, selector: "[i18n]", ngImport: i0 }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(I18nDirective, [{ - type: Directive, - args: [{ selector: '[i18n]' }] - }], null, null); })(); -export class MyComponent { -} -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '
    ', isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ - type: Component, - args: [{ selector: 'my-component', template: '
    ' }] - }], null, null); })(); -export class MyModule { -} -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [I18nDirective, MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ - type: NgModule, - args: [{ declarations: [I18nDirective, MyComponent] }] - }], null, null); })(); - -/**************************************************************************************************** - * PARTIAL FILE: test.d.ts - ****************************************************************************************************/ -import * as i0 from "@angular/core"; -export declare class I18nDirective { - static ɵfac: i0.ɵɵFactoryDef; - static ɵdir: i0.ɵɵDirectiveDefWithMeta; -} -export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; -} -export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; -} - diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/TEST_CASES.json deleted file mode 100644 index 6ccf180b44..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/TEST_CASES.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "$schema": "../../../test_case_schema.json", - "cases": [ - { - "description": "should not match directives on i18n attribute", - "expectations": [ - { - "failureMessage": "Incorrect ChildComponent.ɵcmp", - "files": [ - { - "expected": "component.js", - "generated": "test.js" - } - ] - }, - { - "failureMessage": "Incorrect ChildComponent.ɵfac", - "files": [ - { - "expected": "factory.js", - "generated": "test.js" - } - ] - } - ] - } - ] -} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/component.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/component.js deleted file mode 100644 index 42bf7ae2fa..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/component.js +++ /dev/null @@ -1,12 +0,0 @@ -MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ - type: MyComponent, - selectors: [["my-component"]], - decls: 1, - vars: 0, - template: function MyComponent_Template(rf, ctx) { - if (rf & 1) { - $r3$.ɵɵelement(0, "div"); - } - }, - encapsulation: 2 -}); \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/factory.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/factory.js deleted file mode 100644 index 4348030bd5..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/factory.js +++ /dev/null @@ -1,3 +0,0 @@ -MyComponent.ɵfac = function MyComponent_Factory(t) { - return new (t || MyComponent)(); -}; \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/test.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/test.ts deleted file mode 100644 index 875b9e7c48..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {Component, Directive, NgModule} from '@angular/core'; - -@Directive({selector: '[i18n]'}) -export class I18nDirective { -} - -@Component({selector: 'my-component', template: '
    '}) -export class MyComponent { -} - -@NgModule({declarations: [I18nDirective, MyComponent]}) -export class MyModule { -} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/GOLDEN_PARTIAL.js index 1813909de0..a46b59d691 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/GOLDEN_PARTIAL.js @@ -5,8 +5,8 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Content A
    Content B
    Content C
    @@ -15,8 +15,8 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s
    Content F
    Content G
    Content H
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -34,10 +34,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -47,12 +47,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -62,11 +63,11 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -77,10 +78,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -90,12 +91,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -104,12 +106,15 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.visible = false; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` Test -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -120,10 +125,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -133,12 +138,14 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + visible: boolean; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -147,12 +154,15 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.name = ''; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -163,10 +173,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -176,26 +186,40 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + name: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** * PARTIAL FILE: ng-template_interpolation_structural.js ****************************************************************************************************/ -import { Component, NgModule } from '@angular/core'; +import { Component, NgModule, Pipe } from '@angular/core'; import * as i0 from "@angular/core"; -export class MyComponent { +export class UppercasePipe { + transform(v) { } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +UppercasePipe.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); +UppercasePipe.ɵpipe = i0.ɵɵngDeclarePipe({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, name: "uppercase" }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UppercasePipe, [{ + type: Pipe, + args: [{ name: 'uppercase' }] + }], null, null); })(); +export class MyComponent { + constructor() { + this.name = ''; + } +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -206,25 +230,32 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [UppercasePipe, MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, - args: [{ declarations: [MyComponent] }] + args: [{ declarations: [UppercasePipe, MyComponent] }] }], null, null); })(); /**************************************************************************************************** * PARTIAL FILE: ng-template_interpolation_structural.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; +export declare class UppercasePipe { + transform(v: any): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵpipe: i0.ɵɵPipeDeclaration; +} export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + name: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -234,11 +265,11 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -249,10 +280,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -262,12 +293,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -276,15 +308,19 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.title = ''; + this.label = ''; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -298,10 +334,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -311,12 +347,15 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + title: string; + label: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -326,11 +365,11 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -341,10 +380,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -354,23 +393,95 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: static_attributes_structural.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { + constructor() { + this.exp = true; + } +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` +
    + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
    + ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: static_attributes_structural.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + exp: boolean; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} +export declare class MyModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** * PARTIAL FILE: interpolation_basic.js ****************************************************************************************************/ -import { Component, NgModule } from '@angular/core'; +import { Component, Directive, Input, NgModule, Pipe } from '@angular/core'; import * as i0 from "@angular/core"; +export class UppercasePipe { + transform(v) { } +} +UppercasePipe.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); +UppercasePipe.ɵpipe = i0.ɵɵngDeclarePipe({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, name: "uppercase" }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UppercasePipe, [{ + type: Pipe, + args: [{ name: 'uppercase' }] + }], null, null); })(); +export class DivDir { +} +DivDir.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: DivDir, deps: [], target: i0.ɵɵFactoryTarget.Directive }); +DivDir.ɵdir = i0.ɵɵngDeclareDirective({ version: "0.0.0-PLACEHOLDER", type: DivDir, selector: "div", inputs: { al: ["aria-label", "al"], arl: ["aria-roledescription", "arl"] }, ngImport: i0 }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DivDir, [{ + type: Directive, + args: [{ selector: 'div' }] + }], null, { al: [{ + type: Input, + args: ['aria-label'] + }], arl: [{ + type: Input, + args: ['aria-roledescription'] + }] }); })(); export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true, directives: [{ type: DivDir, selector: "div", inputs: ["aria-label", "aria-roledescription"] }], pipes: { "uppercase": UppercasePipe } }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -400,39 +511,66 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [UppercasePipe, MyComponent, DivDir] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, - args: [{ declarations: [MyComponent] }] + args: [{ declarations: [UppercasePipe, MyComponent, DivDir] }] }], null, null); })(); /**************************************************************************************************** * PARTIAL FILE: interpolation_basic.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; +export declare class UppercasePipe { + transform(v: any): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵpipe: i0.ɵɵPipeDeclaration; +} +export declare class DivDir { + al: any; + arl: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + valueA: any; + valueB: any; + valueC: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** * PARTIAL FILE: interpolation_custom_config.js ****************************************************************************************************/ -import { Component, NgModule } from '@angular/core'; +import { Component, NgModule, Pipe } from '@angular/core'; import * as i0 from "@angular/core"; -export class MyComponent { +export class UppercasePipe { + transform(v) { } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +UppercasePipe.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); +UppercasePipe.ɵpipe = i0.ɵɵngDeclarePipe({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, name: "uppercase" }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UppercasePipe, [{ + type: Pipe, + args: [{ name: 'uppercase' }] + }], null, null); })(); +export class MyComponent { + constructor() { + this.valueA = ''; + } +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    - `, isInline: true }, interpolation: ["{%", "%}"] }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true, pipes: { "uppercase": UppercasePipe }, interpolation: ["{%", "%}"] }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -444,41 +582,60 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [UppercasePipe, MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, - args: [{ declarations: [MyComponent] }] + args: [{ declarations: [UppercasePipe, MyComponent] }] }], null, null); })(); /**************************************************************************************************** * PARTIAL FILE: interpolation_custom_config.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; +export declare class UppercasePipe { + transform(v: any): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵpipe: i0.ɵɵPipeDeclaration; +} export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + valueA: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** * PARTIAL FILE: interpolation_nested_context.js ****************************************************************************************************/ -import { Component, NgModule } from '@angular/core'; +import { Component, NgModule, Pipe } from '@angular/core'; import * as i0 from "@angular/core"; -export class MyComponent { +export class UppercasePipe { + transform(v) { } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +UppercasePipe.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); +UppercasePipe.ɵpipe = i0.ɵɵngDeclarePipe({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, name: "uppercase" }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UppercasePipe, [{ + type: Pipe, + args: [{ name: 'uppercase' }] + }], null, null); })(); +export class MyComponent { + constructor() { + this.outer = ''; + } +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true, pipes: { "uppercase": UppercasePipe } }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -491,25 +648,32 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [UppercasePipe, MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, - args: [{ declarations: [MyComponent] }] + args: [{ declarations: [UppercasePipe, MyComponent] }] }], null, null); })(); /**************************************************************************************************** * PARTIAL FILE: interpolation_nested_context.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; +export declare class UppercasePipe { + transform(v: any): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵpipe: i0.ɵɵPipeDeclaration; +} export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + outer: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -519,11 +683,11 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -534,10 +698,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -547,12 +711,14 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + valueA: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -562,11 +728,11 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -577,10 +743,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -590,12 +756,14 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + valueA: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -605,11 +773,11 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Some content
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -620,10 +788,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -633,12 +801,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -648,13 +817,13 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Some content
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -667,10 +836,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -680,11 +849,12 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/TEST_CASES.json index 94f8aa18ec..6d84fd7bcc 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/TEST_CASES.json @@ -113,6 +113,20 @@ } ] }, + { + "description": "should translate static attributes when used on an element with structural directive", + "inputFiles": [ + "static_attributes_structural.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, { "description": "should support interpolation", "inputFiles": [ diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/bound_attributes.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/bound_attributes.ts index b0e1b2b7d9..e6a92afbbd 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/bound_attributes.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/bound_attributes.ts @@ -10,8 +10,10 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + title = ''; + label = ''; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_basic.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_basic.ts index 799f34b30a..85e93b5c87 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_basic.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_basic.ts @@ -1,4 +1,15 @@ -import {Component, NgModule} from '@angular/core'; +import {Component, Directive, Input, NgModule, Pipe} from '@angular/core'; + +@Pipe({name: 'uppercase'}) +export class UppercasePipe { + transform(v: any) {} +} + +@Directive({selector: 'div'}) +export class DivDir { + @Input('aria-label') al!: any; + @Input('aria-roledescription') arl!: any; +} @Component({ selector: 'my-component', @@ -15,8 +26,11 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + valueA: any; + valueB: any; + valueC: any; } -@NgModule({declarations: [MyComponent]}) +@NgModule({declarations: [UppercasePipe, MyComponent, DivDir]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.ts index 7a87fab3a8..1fb6aaf5ef 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.ts @@ -7,8 +7,9 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + valueA!: any; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_custom_config.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_custom_config.ts index 7e94667410..1a7f852efb 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_custom_config.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_custom_config.ts @@ -1,4 +1,9 @@ -import {Component, NgModule} from '@angular/core'; +import {Component, NgModule, Pipe} from '@angular/core'; + +@Pipe({name: 'uppercase'}) +export class UppercasePipe { + transform(v: any) {} +} @Component({ selector: 'my-component', @@ -8,8 +13,9 @@ import {Component, NgModule} from '@angular/core'; interpolation: ['{%', '%}'], }) export class MyComponent { + valueA = ''; } -@NgModule({declarations: [MyComponent]}) +@NgModule({declarations: [UppercasePipe, MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_nested_context.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_nested_context.ts index 5614807fe7..21f34052cd 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_nested_context.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_nested_context.ts @@ -1,4 +1,9 @@ -import {Component, NgModule} from '@angular/core'; +import {Component, NgModule, Pipe} from '@angular/core'; + +@Pipe({name: 'uppercase'}) +export class UppercasePipe { + transform(v: any) {} +} @Component({ selector: 'my-component', @@ -9,8 +14,9 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + outer = ''; } -@NgModule({declarations: [MyComponent]}) +@NgModule({declarations: [UppercasePipe, MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation.ts index 0a136319d0..650a8b330c 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation.ts @@ -7,8 +7,9 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + name = ''; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation_structural.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation_structural.ts index d61223b76a..aae20cd029 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation_structural.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation_structural.ts @@ -1,4 +1,9 @@ -import {Component, NgModule} from '@angular/core'; +import {Component, NgModule, Pipe} from '@angular/core'; + +@Pipe({name: 'uppercase'}) +export class UppercasePipe { + transform(v: any) {} +} @Component({ selector: 'my-component', @@ -7,8 +12,9 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + name = ''; } -@NgModule({declarations: [MyComponent]}) +@NgModule({declarations: [UppercasePipe, MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_structural.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_structural.ts index 9c2a5b92a4..dd36c8cd83 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_structural.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_structural.ts @@ -7,8 +7,9 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + visible = false; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/GOLDEN_PARTIAL.js deleted file mode 100644 index 8b13789179..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/GOLDEN_PARTIAL.js +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/TEST_CASES.json deleted file mode 100644 index 14825ea3e3..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/TEST_CASES.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "$schema": "../../test_case_schema.json", - "cases": [ - { - "description": "should throw on nested i18n sections", - "inputFiles": [ - "nested_i18n_msg.ts" - ], - "excludeFromPartialTests": true, - "expectations": [ - { - "expectedErrors": [ - { - "message": "Cannot mark an element as translatable inside of a translatable section\\. Please remove the nested i18n marker\\.", - "location": "nested_i18n_msg\\.ts \\(7,5\\)" - } - ] - } - ] - }, - { - "description": "should throw on nested i18n sections with tags in between", - "inputFiles": [ - "nested_i18n_msg_with_tags.ts" - ], - "excludeFromPartialTests": true, - "expectations": [ - { - "expectedErrors": [ - { - "message": "Cannot mark an element as translatable inside of a translatable section\\. Please remove the nested i18n marker\\.", - "location": "nested_i18n_msg_with_tags\\.ts \\(8,7\\)" - } - ] - } - ] - }, - { - "description": "should throw on nested i18n sections represented with s", - "inputFiles": [ - "nested_i18n_msg_with_ng-containers.ts" - ], - "excludeFromPartialTests": true, - "expectations": [ - { - "expectedErrors": [ - { - "message": "Cannot mark an element as translatable inside of a translatable section\\. Please remove the nested i18n marker\\.", - "location": "nested_i18n_msg_with_ng-containers\\.ts \\(8,7\\)" - } - ] - } - ] - } - ] -} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg.ts deleted file mode 100644 index 533f1f9060..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {Component, NgModule} from '@angular/core'; - -@Component({ - selector: 'my-component', - template: ` -
    -
    Some content
    -
    -`, -}) -export class MyComponent { -} - -@NgModule({declarations: [MyComponent]}) -export class MyModule { -} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg_with_ng-containers.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg_with_ng-containers.ts deleted file mode 100644 index e5690686f2..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg_with_ng-containers.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {Component, NgModule} from '@angular/core'; - -@Component({ - selector: 'my-component', - template: ` - -
    - Some content -
    -
    -`, -}) -export class MyComponent { -} - -@NgModule({declarations: [MyComponent]}) -export class MyModule { -} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg_with_tags.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg_with_tags.ts deleted file mode 100644 index e8942340c2..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg_with_tags.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {Component, NgModule} from '@angular/core'; - -@Component({ - selector: 'my-component', - template: ` -
    -
    -
    Some content
    -
    -
    -`, -}) -export class MyComponent { -} - -@NgModule({declarations: [MyComponent]}) -export class MyModule { -} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/GOLDEN_PARTIAL.js index a441458f15..c8a081ce74 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/GOLDEN_PARTIAL.js @@ -6,12 +6,12 @@ import * as i0 from "@angular/core"; var MyComponent = /** @class */ (function () { function MyComponent() { } - MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; - MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '
    Content A
    ', isInline: true } }); + MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: '
    Content A
    ', isInline: true }); return MyComponent; }()); export { MyComponent }; -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -21,13 +21,13 @@ export { MyComponent }; var MyModule = /** @class */ (function () { function MyModule() { } - MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); - MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); + MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); + MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); return MyModule; }()); export { MyModule }; -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -37,11 +37,12 @@ export { MyModule }; ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/TEST_CASES.json index 16ed8ed14e..b55b561088 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/TEST_CASES.json @@ -6,7 +6,9 @@ "compilerOptions": { "target": "ES5" }, - "excludeFromPartialTests": true, + "compilationModeFilter": [ + "full compile" + ], "expectations": [ { "extraChecks": [ diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/GOLDEN_PARTIAL.js index a4aa0761fa..55ddfad2f2 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/GOLDEN_PARTIAL.js @@ -4,12 +4,15 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.gender = 'male'; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {gender, select, male {male} female {female} other {other}}
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -20,10 +23,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -33,12 +36,14 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + gender: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -47,12 +52,15 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.gender = 'male'; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {gender, select, single {'single quotes'} double {"double quotes"} other {other}}
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -63,10 +71,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -76,12 +84,14 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + gender: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -90,12 +100,15 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.age = 20; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` {age, select, 10 {ten} 20 {twenty} other {other}} -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -106,10 +119,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -119,12 +132,14 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + age: number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -133,9 +148,12 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.gender = 'female'; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {gender, select, male {male} female {female} other {other}}
    {age, select, 10 {ten} 20 {twenty} other {other}} @@ -143,8 +161,8 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s
    You have {count, select, 0 {no emails} 1 {one email} other {{{count}} emails}}.
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -161,10 +179,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -174,12 +192,14 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + gender: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -188,12 +208,16 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.age = 1; + this.other = 'bla'; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {age, select, 10 {ten} 20 {twenty} other {{% other %}}}
    -`, isInline: true }, interpolation: ["{%", "%}"] }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true, interpolation: ["{%", "%}"] }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -205,10 +229,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -218,12 +242,15 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + age: number; + other: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -232,16 +259,19 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.gender = 'female'; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {gender, select, male {male - male} female {female female} other {
    other
    }} Other content
    Another content
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -256,10 +286,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -269,12 +299,14 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + gender: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -283,12 +315,18 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.gender = 'female'; + this.ageA = 1; + this.ageB = 2; + this.ageC = 3; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {gender, select, male {male of age: {{ ageA + ageB + ageC }}} female {female} other {other}}
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -299,10 +337,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -312,12 +350,17 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + gender: string; + ageA: number; + ageB: number; + ageC: number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -326,15 +369,19 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.gender = 'male'; + this.age = 1; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {gender, select, male {male} female {female} other {other}} {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -348,10 +395,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -361,12 +408,15 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + gender: string; + age: number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -375,9 +425,12 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.gender = 'male'; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {gender, select, male {male} female {female} other {other}}
    @@ -387,8 +440,8 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s {gender, select, male {male} female {female} other {other}}
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -407,10 +460,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -420,12 +473,14 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + gender: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -434,9 +489,13 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.age = 1; + this.gender = 'male'; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {gender, select, male {male of age: {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}} @@ -444,8 +503,8 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s other {other} }
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -462,10 +521,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -475,12 +534,15 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + age: number; + gender: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -489,9 +551,13 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.count = 0; + this.name = 'Andrew'; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {count, plural, =0 {zero} =2 {{{count}} {name, select, @@ -500,8 +566,8 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s other {animals}} !} other {other - {{count}}} }
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -519,10 +585,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -532,12 +598,15 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + count: number; + name: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -546,17 +615,20 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.gender = 'female'; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {gender, select, male {male} female {female} other {other}} {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -572,10 +644,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -585,12 +657,14 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + gender: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -599,17 +673,22 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.gender = 'male'; + this.weight = 1; + this.height = 1; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {gender, select, male {male {{ weight }}} female {female {{ height }}} other {other}} {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other: {{ otherAge }}}}
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -625,10 +704,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -638,12 +717,16 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + gender: string; + weight: number; + height: number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -652,9 +735,15 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.gender = 'male'; + this.weight = 1; + this.height = 1; + this.age = 1; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    { gender, select, @@ -662,8 +751,8 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s female {female {{ height // i18n(ph="PH_B") }}} other {other {{ age // i18n(ph="PH WITH SPACES") }}} }
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -680,10 +769,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -693,12 +782,17 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + gender: string; + weight: number; + height: number; + age: number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -707,12 +801,15 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.count = 1; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {count, select, 1 {one} other {more than one}}
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -723,10 +820,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -736,12 +833,14 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + count: number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -750,15 +849,18 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.count = 0; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {count, select , 1 {one} other {more than one}} {count, plural , =1 {one} other {more than one}}
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -772,10 +874,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -785,11 +887,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + count: number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/bare_icu.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/bare_icu.ts index 10ed95ab1b..484995c49d 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/bare_icu.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/bare_icu.ts @@ -13,8 +13,9 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + gender = 'female'; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/custom_interpolation.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/custom_interpolation.ts index 9c01465bf6..f4f7fbe9af 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/custom_interpolation.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/custom_interpolation.ts @@ -8,8 +8,10 @@ import {Component, NgModule} from '@angular/core'; interpolation: ['{%', '%}'], }) export class MyComponent { + age = 1; + other = 'bla'; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/different_contexts.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/different_contexts.ts index 1f82608e06..7272417cd6 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/different_contexts.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/different_contexts.ts @@ -12,8 +12,9 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + gender = 'female'; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/escape_quotes.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/escape_quotes.ts index 65817503b2..512a895a44 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/escape_quotes.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/escape_quotes.ts @@ -7,8 +7,9 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + gender = 'male'; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/expressions.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/expressions.ts index ded54469e7..3910c06fbf 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/expressions.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/expressions.ts @@ -7,8 +7,12 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + gender = 'female'; + ageA = 1; + ageB = 2; + ageC = 3; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.js index e606a62e26..e2f0887ebf 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.js @@ -2,7 +2,7 @@ decls: 5, vars: 1, consts: function() { __i18nIcuMsg__('{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}}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`], ['START_BOLD_TEXT', ''], ['CLOSE_BOLD_TEXT', ''], ['START_ITALIC_TEXT', ''], ['CLOSE_ITALIC_TEXT', ''], ['START_TAG_DIV', '
    '], ['CLOSE_TAG_DIV', '
    '],]) - __i18nMsg__(' {$icu} {$startBoldText}Other content{$closeBoldText}{$startTagDiv}{$startItalicText}Another content{$closeItalicText}{$closeTagDiv}', [['startBoldText', String.raw`\uFFFD#2\uFFFD`], ['closeBoldText', String.raw`\uFFFD/#2\uFFFD`], ['startTagDiv', String.raw`\uFFFD#3\uFFFD`], ['startItalicText', String.raw`\uFFFD#4\uFFFD`], ['closeItalicText', String.raw`\uFFFD/#4\uFFFD`], ['closeTagDiv', String.raw`\uFFFD/#3\uFFFD`], ['icu', '$I18N_0$']], {}) + __i18nMsg__(' {$icu} {$startBoldText}Other content{$closeBoldText}{$startTagDiv}{$startItalicText}Another content{$closeItalicText}{$closeTagDiv}', [['startBoldText', String.raw`\uFFFD#2\uFFFD`], ['closeBoldText', String.raw`\uFFFD/#2\uFFFD`], ['startTagDiv', String.raw`\uFFFD#3\uFFFD`], ['startItalicText', String.raw`\uFFFD#4\uFFFD`], ['closeItalicText', String.raw`\uFFFD/#4\uFFFD`], ['closeTagDiv', String.raw`\uFFFD/#3\uFFFD`], ['icu', '$I18N_1$']], {}) return [ $i18n_1$, [__AttributeMarker.Classes__, "other"] diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.ts index 2739dfb294..c11acbcec1 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.ts @@ -11,8 +11,9 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + gender = 'female'; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_only.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_only.ts index f243e3d692..49e6e8b097 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_only.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_only.ts @@ -7,8 +7,9 @@ import {Component, NgModule} from '@angular/core'; `, }) export class MyComponent { + age = 20; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_with_interpolations.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_with_interpolations.ts index 07a10973bb..6ed46a9f08 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_with_interpolations.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_with_interpolations.ts @@ -12,8 +12,11 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + gender = 'male'; + weight = 1; + height = 1; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/keyword_spaces.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/keyword_spaces.ts index e7c711f477..2f7480bd35 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/keyword_spaces.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/keyword_spaces.ts @@ -10,8 +10,9 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + count = 0; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/metadata.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/metadata.ts index 5380a92f97..a3817ea362 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/metadata.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/metadata.ts @@ -7,8 +7,9 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + count = 1; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/multiple_icus.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/multiple_icus.ts index bfe6a15822..c07686aa56 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/multiple_icus.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/multiple_icus.ts @@ -10,8 +10,10 @@ import {Component, NgModule} from '@angular/core'; `, }) export class MyComponent { + gender = 'male'; + age = 1; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/named_interpolations.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/named_interpolations.ts index aa311c3354..ecacfe3220 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/named_interpolations.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/named_interpolations.ts @@ -13,8 +13,12 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + gender = 'male'; + weight = 1; + height = 1; + age = 1; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icu_in_other_block.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icu_in_other_block.ts index bbcdde3755..ef65ff7792 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icu_in_other_block.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icu_in_other_block.ts @@ -14,8 +14,10 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + count = 0; + name = 'Andrew'; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icus.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icus.ts index 0b267229ff..1737f28090 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icus.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icus.ts @@ -13,8 +13,10 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + age = 1; + gender = 'male'; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/shared_placeholder.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/shared_placeholder.ts index 83649faea6..a4a25f92cf 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/shared_placeholder.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/shared_placeholder.ts @@ -15,8 +15,9 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + gender = 'male'; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/single_icu.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/single_icu.ts index 55f82b75c3..3263fd2350 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/single_icu.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/single_icu.ts @@ -7,8 +7,9 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + gender = 'male'; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/GOLDEN_PARTIAL.js index 3cc02fb8f7..98e60766ea 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/GOLDEN_PARTIAL.js @@ -1,12 +1,12 @@ /**************************************************************************************************** - * PARTIAL FILE: inline_template_non_legacy.js + * PARTIAL FILE: inline_template_non_legacy_normalized.js ****************************************************************************************************/ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Some Message @@ -16,8 +16,8 @@ Some Message =0 { zero } -}
    `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +}
    `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -38,36 +38,38 @@ Some Message }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); /**************************************************************************************************** - * PARTIAL FILE: inline_template_non_legacy.d.ts + * PARTIAL FILE: inline_template_non_legacy_normalized.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + value: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** - * PARTIAL FILE: inline_template_non_legacy.js + * PARTIAL FILE: inline_template_non_legacy_non_normalized.js ****************************************************************************************************/ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Some Message @@ -77,8 +79,8 @@ Some Message =0 { zero } -}
    `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +}`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -99,37 +101,39 @@ Some Message }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); /**************************************************************************************************** - * PARTIAL FILE: inline_template_non_legacy.d.ts + * PARTIAL FILE: inline_template_non_legacy_non_normalized.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + value: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** - * PARTIAL FILE: external_template_non_legacy.js + * PARTIAL FILE: external_template_non_legacy_normalized.js ****************************************************************************************************/ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: "\n
    \r\nSome Message\r\n{\r\n value,\r\n select,\r\n =0 {\r\n zero\r\n }\r\n}
    ", isInline: false } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: "\n
    \r\n Some Message\r\n {\r\n value,\r\n select,\r\n =0 {\r\n zero\r\n }\r\n }
    " }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -140,37 +144,39 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); /**************************************************************************************************** - * PARTIAL FILE: external_template_non_legacy.d.ts + * PARTIAL FILE: external_template_non_legacy_normalized.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + value: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** - * PARTIAL FILE: external_template_non_legacy.js + * PARTIAL FILE: external_template_non_legacy_non_normalized.js ****************************************************************************************************/ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: "\n
    \r\nSome Message\r\n{\r\n value,\r\n select,\r\n =0 {\r\n zero\r\n }\r\n}
    ", isInline: false } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: "\n
    \r\n Some Message\r\n {\r\n value,\r\n select,\r\n =0 {\r\n zero\r\n }\r\n }
    " }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -181,36 +187,38 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); /**************************************************************************************************** - * PARTIAL FILE: external_template_non_legacy.d.ts + * PARTIAL FILE: external_template_non_legacy_non_normalized.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + value: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** - * PARTIAL FILE: inline_template_legacy.js + * PARTIAL FILE: inline_template_legacy_normalized.js ****************************************************************************************************/ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Some Message @@ -220,8 +228,8 @@ Some Message =0 { zero } -}
    `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +}`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -242,36 +250,38 @@ Some Message }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); /**************************************************************************************************** - * PARTIAL FILE: inline_template_legacy.d.ts + * PARTIAL FILE: inline_template_legacy_normalized.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + value: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** - * PARTIAL FILE: inline_template_legacy.js + * PARTIAL FILE: inline_template_legacy_non_normalized.js ****************************************************************************************************/ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Some Message @@ -281,8 +291,8 @@ Some Message =0 { zero } -}
    `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +}`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -303,25 +313,27 @@ Some Message }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); /**************************************************************************************************** - * PARTIAL FILE: inline_template_legacy.d.ts + * PARTIAL FILE: inline_template_legacy_non_normalized.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + value: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -331,9 +343,9 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: "\n
    \r\nSome Message\r\n{\r\n value,\r\n select,\r\n =0 {\r\n zero\r\n }\r\n}
    ", isInline: false } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: "\n
    \r\n Some Message\r\n {\r\n value,\r\n select,\r\n =0 {\r\n zero\r\n }\r\n }
    " }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -344,10 +356,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -357,24 +369,26 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + value: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** - * PARTIAL FILE: external_template_legacy.js + * PARTIAL FILE: external_template_legacy_non_normalized.js ****************************************************************************************************/ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: "\n
    \r\nSome Message\r\n{\r\n value,\r\n select,\r\n =0 {\r\n zero\r\n }\r\n}
    ", isInline: false } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: "\n
    \r\n Some Message\r\n {\r\n value,\r\n select,\r\n =0 {\r\n zero\r\n }\r\n }
    " }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -385,24 +399,26 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); /**************************************************************************************************** - * PARTIAL FILE: external_template_legacy.d.ts + * PARTIAL FILE: external_template_legacy_non_normalized.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + value: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/TEST_CASES.json index 9677da1a75..bd6e3d7763 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/TEST_CASES.json @@ -2,9 +2,9 @@ "$schema": "../../test_case_schema.json", "cases": [ { - "description": "should normalize non-legacy message line endings in inline templates where i18nNormalizeLineEndingsInICUs is true", + "description": "should normalize line breaks for non-legacy messages in 'inline templates' where i18nNormalizeLineEndingsInICUs is true", "inputFiles": [ - "inline_template_non_legacy.ts" + "inline_template_non_legacy_normalized.ts" ], "angularCompilerOptions": { "i18nNormalizeLineEndingsInICUs": true, @@ -14,8 +14,8 @@ { "files": [ { - "generated": "inline_template_non_legacy.js", - "expected": "non_legacy.js" + "generated": "inline_template_non_legacy_normalized.js", + "expected": "inline_template_non_legacy.js" } ], "extraChecks": [ @@ -26,9 +26,9 @@ ] }, { - "description": "should normalize non-legacy message line endings in inline templates where i18nNormalizeLineEndingsInICUs is false", + "description": "should normalize line breaks for non-legacy messages in 'inline templates' where i18nNormalizeLineEndingsInICUs is false", "inputFiles": [ - "inline_template_non_legacy.ts" + "inline_template_non_legacy_non_normalized.ts" ], "angularCompilerOptions": { "i18nNormalizeLineEndingsInICUs": false, @@ -38,8 +38,8 @@ { "files": [ { - "generated": "inline_template_non_legacy.js", - "expected": "non_legacy.js" + "generated": "inline_template_non_legacy_non_normalized.js", + "expected": "inline_template_non_legacy.js" } ], "extraChecks": [ @@ -50,9 +50,9 @@ ] }, { - "description": "should normalize non-legacy message line endings in external templates where i18nNormalizeLineEndingsInICUs is true", + "description": "should normalize line breaks for non-legacy messages in 'external templates' where i18nNormalizeLineEndingsInICUs is true", "inputFiles": [ - "external_template_non_legacy.ts" + "external_template_non_legacy_normalized.ts" ], "angularCompilerOptions": { "i18nNormalizeLineEndingsInICUs": true, @@ -62,8 +62,8 @@ { "files": [ { - "generated": "external_template_non_legacy.js", - "expected": "non_legacy.js" + "generated": "external_template_non_legacy_normalized.js", + "expected": "external_template_non_legacy.js" } ], "extraChecks": [ @@ -74,9 +74,9 @@ ] }, { - "description": "should normalize non-legacy line endings in external templates where i18nNormalizeLineEndingsInICUs is false", + "description": "should normalize line breaks for non-legacy messages in 'external templates' where i18nNormalizeLineEndingsInICUs is false", "inputFiles": [ - "external_template_non_legacy.ts" + "external_template_non_legacy_non_normalized.ts" ], "angularCompilerOptions": { "i18nNormalizeLineEndingsInICUs": false, @@ -86,8 +86,8 @@ { "files": [ { - "generated": "external_template_non_legacy.js", - "expected": "non_legacy.js" + "generated": "external_template_non_legacy_non_normalized.js", + "expected": "external_template_non_legacy.js" } ], "extraChecks": [ @@ -100,7 +100,7 @@ { "description": "should compute normalized legacy ids for messages in inline templates where i18nNormalizeLineEndingsInICUs is true", "inputFiles": [ - "inline_template_legacy.ts" + "inline_template_legacy_normalized.ts" ], "angularCompilerOptions": { "i18nNormalizeLineEndingsInICUs": true, @@ -110,8 +110,8 @@ { "files": [ { - "generated": "inline_template_legacy.js", - "expected": "legacy_normalized.js" + "generated": "inline_template_legacy_normalized.js", + "expected": "inline_template_legacy.js" } ], "extraChecks": [ @@ -124,7 +124,7 @@ { "description": "should compute normalized legacy ids for messages in inline templates where i18nNormalizeLineEndingsInICUs is false", "inputFiles": [ - "inline_template_legacy.ts" + "inline_template_legacy_non_normalized.ts" ], "angularCompilerOptions": { "i18nNormalizeLineEndingsInICUs": false, @@ -134,8 +134,8 @@ { "files": [ { - "generated": "inline_template_legacy.js", - "expected": "legacy_normalized.js" + "generated": "inline_template_legacy_non_normalized.js", + "expected": "inline_template_legacy.js" } ], "extraChecks": [ @@ -156,12 +156,6 @@ }, "expectations": [ { - "files": [ - { - "generated": "external_template_legacy_normalized.js", - "expected": "legacy_normalized.js" - } - ], "extraChecks": [ "verifyPlaceholdersIntegrity", "verifyUniqueConsts" @@ -172,7 +166,7 @@ { "description": "should compute non-normalized legacy ids for messages in external templates where i18nNormalizeLineEndingsInICUs is false", "inputFiles": [ - "external_template_legacy.ts" + "external_template_legacy_non_normalized.ts" ], "angularCompilerOptions": { "i18nNormalizeLineEndingsInICUs": false, @@ -180,12 +174,6 @@ }, "expectations": [ { - "files": [ - { - "generated": "external_template_legacy.js", - "expected": "legacy_nonnormalized.js" - } - ], "extraChecks": [ "verifyPlaceholdersIntegrity", "verifyUniqueConsts" diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_legacy.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_legacy.ts deleted file mode 100644 index 7e3a6f14d1..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_legacy.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {Component, NgModule} from '@angular/core'; - -@Component({ - selector: 'my-component', - // NOTE: The template has escaped `\r\n` line-endings markers that will be converted to real - // `\r\n` line-ending chars when loaded from the test file-system. - templateUrl: 'template.html' -}) -export class MyComponent { -} - -@NgModule({declarations: [MyComponent]}) -export class MyModule { -} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_legacy_normalized.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_legacy_normalized.ts index 7e3a6f14d1..26ea41f171 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_legacy_normalized.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_legacy_normalized.ts @@ -7,8 +7,9 @@ import {Component, NgModule} from '@angular/core'; templateUrl: 'template.html' }) export class MyComponent { + value!: any; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_non_legacy.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_non_legacy.ts deleted file mode 100644 index 7e3a6f14d1..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_non_legacy.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {Component, NgModule} from '@angular/core'; - -@Component({ - selector: 'my-component', - // NOTE: The template has escaped `\r\n` line-endings markers that will be converted to real - // `\r\n` line-ending chars when loaded from the test file-system. - templateUrl: 'template.html' -}) -export class MyComponent { -} - -@NgModule({declarations: [MyComponent]}) -export class MyModule { -} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/inline_template_legacy.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/inline_template_legacy.ts deleted file mode 100644 index 4cce3c69a6..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/inline_template_legacy.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {Component, NgModule} from '@angular/core'; - -@Component({ - selector: 'my-component', - // NOTE: This template has escaped `\r\n` line-endings markers that will be converted to real - // `\r\n` line-ending chars when loaded from the test file-system. - template: ` -
    \r\n -Some Message\r\n -{\r\n - value,\r\n - select,\r\n - =0 {\r\n - zero\r\n - }\r\n -}
    ` -}) -export class MyComponent { -} - -@NgModule({declarations: [MyComponent]}) -export class MyModule { -} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/inline_template_non_legacy.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/inline_template_non_legacy.ts deleted file mode 100644 index 4cce3c69a6..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/inline_template_non_legacy.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {Component, NgModule} from '@angular/core'; - -@Component({ - selector: 'my-component', - // NOTE: This template has escaped `\r\n` line-endings markers that will be converted to real - // `\r\n` line-ending chars when loaded from the test file-system. - template: ` -
    \r\n -Some Message\r\n -{\r\n - value,\r\n - select,\r\n - =0 {\r\n - zero\r\n - }\r\n -}
    ` -}) -export class MyComponent { -} - -@NgModule({declarations: [MyComponent]}) -export class MyModule { -} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/legacy_nonnormalized.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/legacy_nonnormalized.js deleted file mode 100644 index 6706670177..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/legacy_nonnormalized.js +++ /dev/null @@ -1,10 +0,0 @@ -// NOTE: The ids generated by the compiler are different if the template is external and we are not explicitly normalizing the line endings. -$I18N_0$ = $localize `:␟4f9ce2c66b187afd9898b25f6336d1eb2be8b5dc␟7326958852138509669:abc -def`; -… -$I18N_4$ = $localize `:␟70a685282be2d956e4db234fa3d985970672faa0␟4863953183043480207:{VAR_SELECT, select, =0 {zero - }}` -… -$I18N_3$ = $localize `:␟6a55b51b9bcf8f84b1b868c585ae09949668a72b␟2773178924738647105: -Some Message -${$I18N_4$}:ICU:`; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/legacy_normalized.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/legacy_normalized.js deleted file mode 100644 index 7ad84559ff..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/legacy_normalized.js +++ /dev/null @@ -1,10 +0,0 @@ -// NOTE: The ids generated by the compiler are different if the template is external and we are not explicitly normalizing the line endings. -$I18N_0$ = $localize `:␟4f9ce2c66b187afd9898b25f6336d1eb2be8b5dc␟7326958852138509669:abc -def`; -… -$I18N_4$ = $localize `:␟b5fe162f4e47ab5b3e534491d30b715e0dff0f52␟4863953183043480207:{VAR_SELECT, select, =0 {zero - }}` -… -$I18N_3$ = $localize `:␟e31c7bc4db2f2e56dc40f005958055a02fd43a2e␟2773178924738647105: -Some Message -${$I18N_4$}:ICU:`; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/non_legacy.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/non_legacy.js deleted file mode 100644 index 438d8776ee..0000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/non_legacy.js +++ /dev/null @@ -1,9 +0,0 @@ -$I18N_0$ = $localize `abc -def`; -… -$I18N_4$ = $localize `{VAR_SELECT, select, =0 {zero - }}` -… -$I18N_3$ = $localize ` -Some Message -${$I18N_4$}:ICU:`; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/template.html b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/template.html index 81f2da3e5c..db08b6bb46 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/template.html +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/template.html @@ -1,11 +1,14 @@ - +
    \r\n -Some Message\r\n -{\r\n - value,\r\n - select,\r\n - =0 {\r\n - zero\r\n - }\r\n -}
    \ No newline at end of file + Some Message\r\n + {\r\n + value,\r\n + select,\r\n + =0 {\r\n + zero\r\n + }\r\n + } \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/GOLDEN_PARTIAL.js index d00c67d51c..494b6e15a8 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/GOLDEN_PARTIAL.js @@ -5,11 +5,11 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Some Message
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -20,10 +20,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -33,12 +33,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -48,11 +49,11 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Some Message
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -63,10 +64,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -76,11 +77,12 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/GOLDEN_PARTIAL.js index 93fd6a839b..efe4774881 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/GOLDEN_PARTIAL.js @@ -5,8 +5,8 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` @@ -14,8 +14,8 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -32,10 +32,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -45,12 +45,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -60,8 +61,8 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` @@ -69,8 +70,8 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -87,10 +88,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -100,11 +101,12 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/GOLDEN_PARTIAL.js index 2e4706b644..1e708bd57f 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/GOLDEN_PARTIAL.js @@ -5,15 +5,15 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -28,10 +28,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -41,12 +41,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -56,11 +57,11 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Some text
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -71,10 +72,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -84,12 +85,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -99,11 +101,11 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Some text 'with single quotes', "with double quotes", \`with backticks\` and without quotes.
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -114,10 +116,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -127,12 +129,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -141,10 +144,13 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.count = 1; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '
    `{{ count }}`
    ', isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: '
    `{{ count }}`
    ', isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -153,10 +159,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -166,12 +172,14 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + count: number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -181,15 +189,15 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    My i18n block #1
    My non-i18n block #1
    My i18n block #2
    My non-i18n block #2
    My i18n block #3
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -204,10 +212,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -217,12 +225,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -231,15 +240,19 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.valueA = ''; + this.valueB = ''; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Named interpolation: {{ valueA // i18n(ph="PH_A") }} Named interpolation with spaces: {{ valueB // i18n(ph="PH B") }}
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -253,10 +266,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -266,12 +279,15 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + valueA: string; + valueB: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -280,12 +296,15 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.valueA = ''; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {% valueA %}
    - `, isInline: true }, interpolation: ["{%", "%}"] }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true, interpolation: ["{%", "%}"] }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -297,10 +316,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -310,30 +329,41 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + valueA: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** * PARTIAL FILE: interpolation_complex_expressions.js ****************************************************************************************************/ -import { Component, NgModule } from '@angular/core'; +import { Component, NgModule, Pipe } from '@angular/core'; import * as i0 from "@angular/core"; +export class AsyncPipe { + transform(v) { } +} +AsyncPipe.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AsyncPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); +AsyncPipe.ɵpipe = i0.ɵɵngDeclarePipe({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AsyncPipe, name: "async" }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AsyncPipe, [{ + type: Pipe, + args: [{ name: 'async' }] + }], null, null); })(); export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {{ valueA | async }} {{ valueA?.a?.b }} {{ valueA.getRawValue()?.getTitle() }}
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true, pipes: { "async": AsyncPipe } }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -348,41 +378,64 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent, AsyncPipe] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, - args: [{ declarations: [MyComponent] }] + args: [{ declarations: [MyComponent, AsyncPipe] }] }], null, null); })(); /**************************************************************************************************** * PARTIAL FILE: interpolation_complex_expressions.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; +export declare class AsyncPipe { + transform(v: any): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵpipe: i0.ɵɵPipeDeclaration; +} export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + valueA: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** * PARTIAL FILE: bindings_in_content.js ****************************************************************************************************/ -import { Component, NgModule } from '@angular/core'; +import { Component, NgModule, Pipe } from '@angular/core'; import * as i0 from "@angular/core"; -export class MyComponent { +export class UppercasePipe { + transform(v) { } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +UppercasePipe.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); +UppercasePipe.ɵpipe = i0.ɵɵngDeclarePipe({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, name: "uppercase" }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UppercasePipe, [{ + type: Pipe, + args: [{ name: 'uppercase' }] + }], null, null); })(); +export class MyComponent { + constructor() { + this.one = 1; + this.two = 2; + this.three = 3; + this.four = 4; + this.five = 5; + } +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    My i18n block #{{ one }}
    My i18n block #{{ two | uppercase }}
    My i18n block #{{ three + four + five }}
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true, pipes: { "uppercase": UppercasePipe } }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -395,36 +448,61 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent, UppercasePipe] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, - args: [{ declarations: [MyComponent] }] + args: [{ declarations: [MyComponent, UppercasePipe] }] }], null, null); })(); /**************************************************************************************************** * PARTIAL FILE: bindings_in_content.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; +export declare class UppercasePipe { + transform(v: any): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵpipe: i0.ɵɵPipeDeclaration; +} export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + one: number; + two: number; + three: number; + four: number; + five: number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** * PARTIAL FILE: nested_elements.js ****************************************************************************************************/ -import { Component, NgModule } from '@angular/core'; +import { Component, NgModule, Pipe } from '@angular/core'; import * as i0 from "@angular/core"; -export class MyComponent { +export class UppercasePipe { + transform(v) { } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +UppercasePipe.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); +UppercasePipe.ɵpipe = i0.ɵɵngDeclarePipe({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, name: "uppercase" }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UppercasePipe, [{ + type: Pipe, + args: [{ name: 'uppercase' }] + }], null, null); })(); +export class MyComponent { + constructor() { + this.one = 1; + this.two = 2; + this.nestedInBlockTwo = ''; + } +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    My i18n block #{{ one }} Plain text in nested element @@ -439,8 +517,8 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true, pipes: { "uppercase": UppercasePipe } }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -464,36 +542,54 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent, UppercasePipe] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, - args: [{ declarations: [MyComponent] }] + args: [{ declarations: [MyComponent, UppercasePipe] }] }], null, null); })(); /**************************************************************************************************** * PARTIAL FILE: nested_elements.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; +export declare class UppercasePipe { + transform(v: any): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵpipe: i0.ɵɵPipeDeclaration; +} export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + one: number; + two: number; + nestedInBlockTwo: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** * PARTIAL FILE: nested_elements_with_i18n_attributes.js ****************************************************************************************************/ -import { Component, NgModule } from '@angular/core'; +import { Component, NgModule, Pipe } from '@angular/core'; import * as i0 from "@angular/core"; +export class UppercasePipe { + transform(v) { } +} +UppercasePipe.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); +UppercasePipe.ɵpipe = i0.ɵɵngDeclarePipe({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, name: "uppercase" }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UppercasePipe, [{ + type: Pipe, + args: [{ name: 'uppercase' }] + }], null, null); })(); export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    My i18n block #1 with value: {{ valueA }} @@ -506,8 +602,8 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s Plain text in nested element (block #2)
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true, pipes: { "uppercase": UppercasePipe } }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -529,25 +625,36 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [UppercasePipe, MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, - args: [{ declarations: [MyComponent] }] + args: [{ declarations: [UppercasePipe, MyComponent] }] }], null, null); })(); /**************************************************************************************************** * PARTIAL FILE: nested_elements_with_i18n_attributes.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; +export declare class UppercasePipe { + transform(v: any): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵpipe: i0.ɵɵPipeDeclaration; +} export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + valueA: any; + valueB: any; + valueC: any; + valueD: any; + valueE: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -557,8 +664,8 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Some content
    @@ -570,8 +677,8 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -592,10 +699,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -605,12 +712,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -620,13 +728,13 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -639,10 +747,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -652,12 +760,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -667,8 +776,8 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Some content
    @@ -690,8 +799,8 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -722,10 +831,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -735,12 +844,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -750,11 +860,11 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Some other content {{ valueA }}
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -765,10 +875,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -778,12 +888,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -792,12 +903,13 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + onClick() { } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Hello
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -808,10 +920,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -821,11 +933,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + onClick(): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/backtick_quotes.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/backtick_quotes.ts index 6337d35ab5..3fc6e6d180 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/backtick_quotes.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/backtick_quotes.ts @@ -5,8 +5,9 @@ import {Component, NgModule} from '@angular/core'; template: '
    `{{ count }}`
    ', }) export class MyComponent { + count = 1; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/bindings_in_content.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/bindings_in_content.ts index 4a4ea77d5d..bc7a051a1f 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/bindings_in_content.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/bindings_in_content.ts @@ -1,4 +1,9 @@ -import {Component, NgModule} from '@angular/core'; +import {Component, NgModule, Pipe} from '@angular/core'; + +@Pipe({name: 'uppercase'}) +export class UppercasePipe { + transform(v: any) {} +} @Component({ selector: 'my-component', @@ -9,8 +14,13 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + one = 1; + two = 2; + three = 3; + four = 4; + five = 5; } -@NgModule({declarations: [MyComponent]}) +@NgModule({declarations: [MyComponent, UppercasePipe]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/event_listeners.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/event_listeners.ts index 987a192757..6c90d941f4 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/event_listeners.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/event_listeners.ts @@ -7,8 +7,9 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + onClick() {} } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.ts index 394b39565b..dff55cca7b 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.ts @@ -1,4 +1,9 @@ -import {Component, NgModule} from '@angular/core'; +import {Component, NgModule, Pipe} from '@angular/core'; + +@Pipe({name: 'async'}) +export class AsyncPipe { + transform(v: any) {} +} @Component({ selector: 'my-component', @@ -11,8 +16,9 @@ import {Component, NgModule} from '@angular/core'; ` }) export class MyComponent { + valueA!: any; } -@NgModule({declarations: [MyComponent]}) +@NgModule({declarations: [MyComponent, AsyncPipe]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_custom_config.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_custom_config.ts index efbd1e39ed..f224406bb3 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_custom_config.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_custom_config.ts @@ -8,8 +8,9 @@ import {Component, NgModule} from '@angular/core'; interpolation: ['{%', '%}'], }) export class MyComponent { + valueA = ''; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/named_interpolations.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/named_interpolations.ts index 5ddfce4ae9..8568305b39 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/named_interpolations.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/named_interpolations.ts @@ -10,8 +10,10 @@ import {Component, NgModule} from '@angular/core'; `, }) export class MyComponent { + valueA = ''; + valueB = ''; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements.ts index 9a3d88576a..e8ceca53ed 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements.ts @@ -1,4 +1,9 @@ -import {Component, NgModule} from '@angular/core'; +import {Component, NgModule, Pipe} from '@angular/core'; + +@Pipe({name: 'uppercase'}) +export class UppercasePipe { + transform(v: any) {} +} @Component({ selector: 'my-component', @@ -20,8 +25,11 @@ import {Component, NgModule} from '@angular/core'; `, }) export class MyComponent { + one = 1; + two = 2; + nestedInBlockTwo = ''; } -@NgModule({declarations: [MyComponent]}) +@NgModule({declarations: [MyComponent, UppercasePipe]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements_with_i18n_attributes.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements_with_i18n_attributes.ts index ba16f92f74..dba8e5c2e6 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements_with_i18n_attributes.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements_with_i18n_attributes.ts @@ -1,4 +1,9 @@ -import {Component, NgModule} from '@angular/core'; +import {Component, NgModule, Pipe} from '@angular/core'; + +@Pipe({name: 'uppercase'}) +export class UppercasePipe { + transform(v: any) {} +} @Component({ selector: 'my-component', @@ -18,8 +23,13 @@ import {Component, NgModule} from '@angular/core'; `, }) export class MyComponent { + valueA!: any; + valueB!: any; + valueC!: any; + valueD!: any; + valueE!: any; } -@NgModule({declarations: [MyComponent]}) +@NgModule({declarations: [UppercasePipe, MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/GOLDEN_PARTIAL.js index 04b038c45b..127bd0cb05 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/GOLDEN_PARTIAL.js @@ -1,15 +1,27 @@ /**************************************************************************************************** * PARTIAL FILE: single_ng-container.js ****************************************************************************************************/ -import { Component, NgModule } from '@angular/core'; +import { Component, NgModule, Pipe } from '@angular/core'; import * as i0 from "@angular/core"; -export class MyComponent { +export class UppercasePipe { + transform(v) { } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +UppercasePipe.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); +UppercasePipe.ɵpipe = i0.ɵɵngDeclarePipe({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, name: "uppercase" }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UppercasePipe, [{ + type: Pipe, + args: [{ name: 'uppercase' }] + }], null, null); })(); +export class MyComponent { + constructor() { + this.valueA = ''; + } +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` Some content: {{ valueA | uppercase }} -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true, pipes: { "uppercase": UppercasePipe } }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -20,25 +32,32 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent, UppercasePipe] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, - args: [{ declarations: [MyComponent] }] + args: [{ declarations: [MyComponent, UppercasePipe] }] }], null, null); })(); /**************************************************************************************************** * PARTIAL FILE: single_ng-container.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; +export declare class UppercasePipe { + transform(v: any): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵpipe: i0.ɵɵPipeDeclaration; +} export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + valueA: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -48,11 +67,11 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` Some content: {{ valueA | uppercase }} -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -63,10 +82,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -76,29 +95,43 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** * PARTIAL FILE: child_elements.js ****************************************************************************************************/ -import { Component, NgModule } from '@angular/core'; +import { Component, NgModule, Pipe } from '@angular/core'; import * as i0 from "@angular/core"; -export class MyComponent { +export class UppercasePipe { + transform(v) { } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +UppercasePipe.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); +UppercasePipe.ɵpipe = i0.ɵɵngDeclarePipe({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: UppercasePipe, name: "uppercase" }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UppercasePipe, [{ + type: Pipe, + args: [{ name: 'uppercase' }] + }], null, null); })(); +export class MyComponent { + constructor() { + this.valueA = ''; + this.valueB = ''; + } +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Template content: {{ valueA | uppercase }} Container content: {{ valueB | uppercase }}
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true, pipes: { "uppercase": UppercasePipe } }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -112,25 +145,33 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent, UppercasePipe] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, - args: [{ declarations: [MyComponent] }] + args: [{ declarations: [MyComponent, UppercasePipe] }] }], null, null); })(); /**************************************************************************************************** * PARTIAL FILE: child_elements.d.ts ****************************************************************************************************/ import * as i0 from "@angular/core"; +export declare class UppercasePipe { + transform(v: any): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵpipe: i0.ɵɵPipeDeclaration; +} export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + valueA: string; + valueB: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -139,13 +180,16 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.age = 0; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` {gender, select, male {male} female {female} other {other}} {age, select, 10 {ten} 20 {twenty} other {other}} -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -157,10 +201,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -170,12 +214,14 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + age: number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -185,8 +231,8 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Template A: {{ valueA | uppercase }} @@ -198,8 +244,8 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -220,10 +266,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -233,12 +279,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -247,13 +294,16 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.gender = 'female'; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` {gender, select, male {male} female {female} other {other}} {age, select, 10 {ten} 20 {twenty} other {other}} -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -265,10 +315,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -278,12 +328,14 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + gender: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -293,16 +345,16 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` is my logo #1 is my logo #2 -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -318,10 +370,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -331,12 +383,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -346,12 +399,12 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Test
    Test
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -363,10 +416,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -376,12 +429,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -391,13 +445,13 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Hello there
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -410,10 +464,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -423,12 +477,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -438,13 +493,13 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Hello there !
    -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -457,10 +512,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -470,12 +525,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -485,12 +541,12 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` Content A Content B -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -502,10 +558,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -515,11 +571,12 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/bare_icus.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/bare_icus.ts index 5ede4fd032..87fdb79f26 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/bare_icus.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/bare_icus.ts @@ -8,8 +8,9 @@ import {Component, NgModule} from '@angular/core'; `, }) export class MyComponent { + age = 0; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/child_elements.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/child_elements.ts index 363f9ab63b..551f9cad62 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/child_elements.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/child_elements.ts @@ -1,4 +1,9 @@ -import {Component, NgModule} from '@angular/core'; +import {Component, NgModule, Pipe} from '@angular/core'; + +@Pipe({name: 'uppercase'}) +export class UppercasePipe { + transform(v: any) {} +} @Component({ selector: 'my-component', @@ -10,8 +15,10 @@ import {Component, NgModule} from '@angular/core'; `, }) export class MyComponent { + valueA = ''; + valueB = ''; } -@NgModule({declarations: [MyComponent]}) +@NgModule({declarations: [MyComponent, UppercasePipe]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/icus.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/icus.ts index 3eb4e1de4e..5d59b79ef6 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/icus.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/icus.ts @@ -8,8 +8,9 @@ import {Component, NgModule} from '@angular/core'; `, }) export class MyComponent { + gender = 'female'; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-container.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-container.ts index 21575e742d..08c1d64bff 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-container.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-container.ts @@ -1,4 +1,9 @@ -import {Component, NgModule} from '@angular/core'; +import {Component, NgModule, Pipe} from '@angular/core'; + +@Pipe({name: 'uppercase'}) +export class UppercasePipe { + transform(v: any) {} +} @Component({ selector: 'my-component', @@ -7,8 +12,9 @@ import {Component, NgModule} from '@angular/core'; `, }) export class MyComponent { + valueA = ''; } -@NgModule({declarations: [MyComponent]}) +@NgModule({declarations: [MyComponent, UppercasePipe]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/GOLDEN_PARTIAL.js index d5c39853fb..91fa8db4bb 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/GOLDEN_PARTIAL.js @@ -5,11 +5,11 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    My i18n block #1
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -20,10 +20,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -33,12 +33,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -47,12 +48,15 @@ export declare class MyModule { import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { + constructor() { + this.age = 1; + } } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    {age, select, 10 {ten} 20 {twenty} other {other}}
    - `, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + `, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -63,10 +67,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -76,12 +80,14 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + age: number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -91,12 +97,12 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` My i18n block #1 My i18n block #2 -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -108,10 +114,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -121,12 +127,13 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } /**************************************************************************************************** @@ -136,12 +143,12 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` Text #1 Text #2 -`, isInline: true } }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -153,10 +160,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -166,11 +173,12 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/icu_only.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/icu_only.ts index d536823bcf..d5e95f2dbe 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/icu_only.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/icu_only.ts @@ -7,8 +7,9 @@ import {Component, NgModule} from '@angular/core'; `, }) export class MyComponent { + age = 1; } @NgModule({declarations: [MyComponent]}) export class MyModule { -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/GOLDEN_PARTIAL.js index 02ed4e72c5..ffef7dece5 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/GOLDEN_PARTIAL.js @@ -5,14 +5,14 @@ import { Component, NgModule } from '@angular/core'; import * as i0 from "@angular/core"; export class MyComponent { } -MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
    Some text Text inside span
    -`, isInline: true }, preserveWhitespaces: true }); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ +`, isInline: true, preserveWhitespaces: true }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ selector: 'my-component', @@ -27,10 +27,10 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, s }], null, null); })(); export class MyModule { } -MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); -MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); -(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); -/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ +MyModule.ɵfac = i0.ɵɵngDeclareFactory({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); +MyModule.ɵmod = i0.ɵɵngDeclareNgModule({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent] }); +MyModule.ɵinj = i0.ɵɵngDeclareInjector({ version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule }); +(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{ type: NgModule, args: [{ declarations: [MyComponent] }] }], null, null); })(); @@ -40,11 +40,12 @@ MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { ****************************************************************************************************/ import * as i0 from "@angular/core"; export declare class MyComponent { - static ɵfac: i0.ɵɵFactoryDef; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; } export declare class MyModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; - static ɵinj: i0.ɵɵInjectorDef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; } diff --git a/packages/compiler-cli/test/compliance/test_cases/test_case_schema.json b/packages/compiler-cli/test/compliance/test_cases/test_case_schema.json index 925811f117..f6f30e1ffe 100644 --- a/packages/compiler-cli/test/compliance/test_cases/test_case_schema.json +++ b/packages/compiler-cli/test/compliance/test_cases/test_case_schema.json @@ -20,10 +20,20 @@ "description": "This will be used as the message in an `it()` clause.", "type": "string" }, - "excludeFromPartialTests": { - "title": "If set to true then do not check this test-case expectations in partial tests.", - "type": "boolean", - "default": false + "compilationModeFilter": { + "title": "An array of compilation modes under which this test-case should be run.", + "type": "array", + "items": { + "type": "string", + "enum": [ + "full compile", + "linked compile" + ] + }, + "default": [ + "full compile", + "linked compile" + ] }, "inputFiles": { "title": "A collection of source files to compile", @@ -73,67 +83,67 @@ "title": "A path (relative to the test case) where an file containing expected output can be found. (The generated path is inferred from this.)" } ] - }, - "expectedErrors": { - "title": "A collection of expected error messages for this test-case.", - "type": "array", - "items": { - "title": "An array of strings that should appear in the error message.", - "type": "object", - "properties": { - "message": { - "title": "A regular expression that should match the error message", - "type": "string" - }, - "location": { - "title": "An optional regular expression that should match the location of the error", - "type": "string" - } + } + }, + "expectedErrors": { + "title": "A collection of expected error messages for this test-case.", + "type": "array", + "items": { + "title": "An array of strings that should appear in the error message.", + "type": "object", + "properties": { + "message": { + "title": "A regular expression that should match the error message", + "type": "string" }, - "required": [ - "message" - ] - } - }, - "extraChecks": { - "title": "Additional checks to run against the generated code.", - "type": "array", - "items": { - "anyOf": [ - { - "title": "The name of a function to run as an additional check on the source.", - "type": "string" - }, - { - "title": "An array whose first element is the name of a function to run as an additional check, and subsequent elements are args for this function", - "type": "array" - } - ] - } + "location": { + "title": "An optional regular expression that should match the location of the error", + "type": "string" + } + }, + "required": [ + "message" + ] + } + }, + "extraChecks": { + "title": "Additional checks to run against the generated code.", + "type": "array", + "items": { + "anyOf": [ + { + "title": "The name of a function to run as an additional check on the source.", + "type": "string" + }, + { + "title": "An array whose first element is the name of a function to run as an additional check, and subsequent elements are args for this function", + "type": "array" + } + ] } } } - }, - "compilerOptions": { - "title": "Additional options to pass to the TypeScript compiler", - "type": "object" - }, - "angularCompilerOptions": { - "title": "Additional options to pass to the Angular compiler", - "type": "object" - }, - "focusTest": { - "title": "Set to true to focus on this test - equivalent to jasmine's `fit()` function", - "type": "boolean" - }, - "excludeTest": { - "title": "Set to true to exclude this test - equivalent to jasmine's `xit()` function", - "type": "boolean" } + }, + "compilerOptions": { + "title": "Additional options to pass to the TypeScript compiler", + "type": "object" + }, + "angularCompilerOptions": { + "title": "Additional options to pass to the Angular compiler", + "type": "object" + }, + "focusTest": { + "title": "Set to true to focus on this test - equivalent to jasmine's `fit()` function", + "type": "boolean" + }, + "excludeTest": { + "title": "Set to true to exclude this test - equivalent to jasmine's `xit()` function", + "type": "boolean" } - }, - "minItems": 1 - } + } + }, + "minItems": 1 } } } diff --git a/packages/compiler-cli/test/compliance/test_helpers/BUILD.bazel b/packages/compiler-cli/test/compliance/test_helpers/BUILD.bazel index 2f6e61ad85..f7db924eca 100644 --- a/packages/compiler-cli/test/compliance/test_helpers/BUILD.bazel +++ b/packages/compiler-cli/test/compliance/test_helpers/BUILD.bazel @@ -15,6 +15,8 @@ ts_library( "//packages/compiler-cli", "//packages/compiler-cli/src/ngtsc/file_system", "//packages/compiler-cli/src/ngtsc/file_system/testing", + "//packages/compiler-cli/src/ngtsc/logging", + "//packages/compiler-cli/src/ngtsc/sourcemaps", "//packages/compiler-cli/src/ngtsc/testing", "@npm//typescript", ], diff --git a/packages/compiler-cli/test/compliance/test_helpers/check_errors.ts b/packages/compiler-cli/test/compliance/test_helpers/check_errors.ts index 169c038db9..5eccb24c44 100644 --- a/packages/compiler-cli/test/compliance/test_helpers/check_errors.ts +++ b/packages/compiler-cli/test/compliance/test_helpers/check_errors.ts @@ -22,3 +22,11 @@ export function checkErrors( } } } + +export function checkNoUnexpectedErrors(testPath: string, actualErrors: string[]): void { + if (actualErrors.length > 0) { + throw new Error( + `Unexpected errors occurred for test case at "${testPath}"\n` + + `Errors: ${inspect(actualErrors)}.`); + } +} diff --git a/packages/compiler-cli/test/compliance/test_helpers/check_expectations.ts b/packages/compiler-cli/test/compliance/test_helpers/check_expectations.ts index a39d1d9976..40bf25cd60 100644 --- a/packages/compiler-cli/test/compliance/test_helpers/check_expectations.ts +++ b/packages/compiler-cli/test/compliance/test_helpers/check_expectations.ts @@ -5,18 +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 {FileSystem} from '../../../src/ngtsc/file_system'; +import {ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {getBuildOutputDirectory, getRootDirectory} from './compile_test'; +import {verifyUniqueFactory} from './di_checks'; import {expectEmit} from './expect_emit'; import {replaceMacros} from './expected_file_macros'; +import {verifyUniqueFunctions} from './function_checks'; import {ExpectedFile, ExtraCheck} from './get_compliance_tests'; import {verifyPlaceholdersIntegrity, verifyUniqueConsts} from './i18n_checks'; +import {checkMappings} from './sourcemap_helpers'; type ExtraCheckFunction = (generated: string, ...extraArgs: any[]) => boolean; const EXTRA_CHECK_FUNCTIONS: Record = { verifyPlaceholdersIntegrity, verifyUniqueConsts, + verifyUniqueFactory, + verifyUniqueFunctions, }; /** @@ -28,7 +33,7 @@ const EXTRA_CHECK_FUNCTIONS: Record = { * @param expectedFiles The list of expected-generated pairs to compare. */ export function checkExpectations( - fs: FileSystem, testPath: string, failureMessage: string, expectedFiles: ExpectedFile[], + fs: ReadonlyFileSystem, testPath: string, failureMessage: string, expectedFiles: ExpectedFile[], extraChecks: ExtraCheck[]): void { const builtDirectory = getBuildOutputDirectory(fs); for (const expectedFile of expectedFiles) { @@ -44,14 +49,16 @@ export function checkExpectations( `The generated file at ${generatedPath} does not exist.\n` + 'Perhaps there is no matching input source file in the TEST_CASES.json file for this test case.\n' + 'Or maybe you need to regenerate the GOLDEN_PARTIAL.js file by running:\n\n' + - ` bazel run //packages/compiler-cli/test/compliance/test_cases:${ + ` yarn bazel run //packages/compiler-cli/test/compliance/test_cases:${ testPath}.golden.update`); // Clear the stack so that we get a nice error message error.stack = ''; throw error; } - const expected = replaceMacros(fs.readFile(expectedPath)); const generated = fs.readFile(generatedPath); + let expected = fs.readFile(expectedPath); + expected = replaceMacros(expected); + expected = checkMappings(fs, generated, generatedPath, expected, expectedPath); expectEmit( generated, expected, @@ -79,6 +86,10 @@ function runExtraChecks( `Unknown extra-check function: "${fnName}" in ${testPath}.\n` + `Possible choices are: ${Object.keys(EXTRA_CHECK_FUNCTIONS).map(f => `\n - ${f}`)}.`); } - expect(fn(generated, ...args)).toBe(true); + if (!fn(generated, ...args)) { + throw new Error( + `Extra check ${fnName}(${args.map(arg => JSON.stringify(arg)).join(',')}) in ${ + testPath} failed for generated code:\n\n${generated}`); + } } } diff --git a/packages/compiler-cli/test/compliance/test_helpers/compile_test.ts b/packages/compiler-cli/test/compliance/test_helpers/compile_test.ts index 2716dbcf99..70eda234f5 100644 --- a/packages/compiler-cli/test/compliance/test_helpers/compile_test.ts +++ b/packages/compiler-cli/test/compliance/test_helpers/compile_test.ts @@ -7,9 +7,9 @@ */ import * as ts from 'typescript'; -import {AbsoluteFsPath, FileSystem, NgtscCompilerHost} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, FileSystem, PathManipulation, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {initMockFileSystem} from '../../../src/ngtsc/file_system/testing'; -import {loadStandardTestFiles, loadTestDirectory} from '../../../src/ngtsc/testing'; +import {loadStandardTestFiles, loadTestDirectory, NgtscTestCompilerHost} from '../../../src/ngtsc/testing'; import {Diagnostics, performCompilation} from '../../../src/perform_compile'; import {CompilerOptions} from '../../../src/transformers/api'; @@ -52,7 +52,7 @@ export function compileTest( const outDir = getBuildOutputDirectory(fs); const options = getOptions(rootDir, outDir, compilerOptions, angularCompilerOptions); const rootNames = files.map(f => fs.resolve(f)); - const host = new NgtscCompilerHost(fs, options); + const host = new NgtscTestCompilerHost(fs, options); const {diagnostics, emitResult} = performCompilation({rootNames, host, options}); const emittedFiles = emitResult ? emitResult.emittedFiles!.map(p => fs.resolve(rootDir, p)) : []; const errors = parseDiagnostics(diagnostics); @@ -65,7 +65,7 @@ export function compileTest( * * @param fs the mock file-system where the compilation is happening. */ -export function getRootDirectory(fs: FileSystem): AbsoluteFsPath { +export function getRootDirectory(fs: PathManipulation): AbsoluteFsPath { return fs.resolve('/'); } @@ -75,7 +75,7 @@ export function getRootDirectory(fs: FileSystem): AbsoluteFsPath { * * @param fs the mock file-system where the compilation is happening. */ -export function getBuildOutputDirectory(fs: FileSystem): AbsoluteFsPath { +export function getBuildOutputDirectory(fs: PathManipulation): AbsoluteFsPath { return fs.resolve('/built'); } @@ -118,7 +118,6 @@ function getOptions( typeRoots: ['node_modules/@types'], ...convertedCompilerOptions.options, enableIvy: true, - ivyTemplateTypeCheck: false, enableI18nLegacyMessageIdFormat: false, ...angularCompilerOptions, }; @@ -128,13 +127,19 @@ function getOptions( * Replace escaped line-ending markers (\r\n) with real line-ending characters. * * This allows us to simulate, more reliably, files that have `\r\n` line-endings. - * (See `line_ending_normalization` test cases.) + * (See `test_cases/r3_view_compiler_i18n/line_ending_normalization/template.html`.) */ -function monkeyPatchReadFile(fs: FileSystem): void { +function monkeyPatchReadFile(fs: ReadonlyFileSystem): void { const originalReadFile = fs.readFile; fs.readFile = (path: AbsoluteFsPath): string => { const file = originalReadFile.call(fs, path); - return file.replace(/\\r\\n\r?\n/g, '\r\n'); + return file + // First convert actual `\r\n` sequences to `\n` + .replace(/\r\n/g, '\n') + // unescape `\r\n` at the end of a line + .replace(/\\r\\n\n/g, '\r\n') + // unescape `\\r\\n`, at the end of a line, to `\r\n` + .replace(/\\\\r\\\\n(\r?\n)/g, '\\r\\n$1'); }; } diff --git a/packages/compiler-cli/test/compliance/test_helpers/expected_file_macros.ts b/packages/compiler-cli/test/compliance/test_helpers/expected_file_macros.ts index 73a7c17ed4..fdfcfb19fa 100644 --- a/packages/compiler-cli/test/compliance/test_helpers/expected_file_macros.ts +++ b/packages/compiler-cli/test/compliance/test_helpers/expected_file_macros.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 {AttributeMarker} from '@angular/compiler/src/core'; -import {i18nIcuMsg, i18nMsg, i18nMsgWithPostprocess, Placeholder} from './i18n_helpers'; +import {AttributeMarker, SelectorFlags} from '@angular/compiler/src/core'; +import {QueryFlags} from '@angular/compiler/src/render3/view/compiler'; +import {i18nIcuMsg, i18nMsg, i18nMsgWithPostprocess, Placeholder, resetMessageIndex} from './i18n_helpers'; const EXPECTED_FILE_MACROS: [RegExp, (...args: string[]) => string][] = [ [ @@ -32,6 +33,12 @@ const EXPECTED_FILE_MACROS: [RegExp, (...args: string[]) => string][] = [ /__AttributeMarker\.([^_]+)__/g, (_match, member) => getAttributeMarker(member), ], + + // E.g. `__SelectorFlags.ELEMENT__` + flagUnion(/__SelectorFlags\.([^_]+)__/, (_match, member) => getSelectorFlag(member)), + + // E.g. `__QueryFlags.ELEMENT__` + flagUnion(/__QueryFlags\.([^_]+)__/, (_match, member) => getQueryFlag(member)), ]; /** @@ -40,6 +47,8 @@ const EXPECTED_FILE_MACROS: [RegExp, (...args: string[]) => string][] = [ * @param expectedContent The content to process. */ export function replaceMacros(expectedContent: string): string { + resetMessageIndex(); + for (const [regex, replacer] of EXPECTED_FILE_MACROS) { expectedContent = expectedContent.replace(regex, replacer); } @@ -89,6 +98,36 @@ function getAttributeMarker(member: string): string { return `${marker}`; } +const SelectorFlagsMap: Record = { + NOT: SelectorFlags.NOT, + ATTRIBUTE: SelectorFlags.ATTRIBUTE, + ELEMENT: SelectorFlags.ELEMENT, + CLASS: SelectorFlags.CLASS, +}; + +function getSelectorFlag(member: string): number { + const marker = SelectorFlagsMap[member]; + if (typeof marker !== 'number') { + throw new Error('Unknown SelectorFlag: ' + member); + } + return marker; +} + +const QueryFlagsMap: Record = { + none: QueryFlags.none, + descendants: QueryFlags.descendants, + isStatic: QueryFlags.isStatic, + emitDistinctChangesOnly: QueryFlags.emitDistinctChangesOnly, +}; + +function getQueryFlag(member: string): number { + const marker = QueryFlagsMap[member]; + if (typeof marker !== 'number') { + throw new Error('Unknown SelectorFlag: ' + member); + } + return marker; +} + function stringParam() { return /'([^']*?[^\\])'/; } @@ -105,3 +144,24 @@ function macroFn(fnName: RegExp, ...args: RegExp[]): RegExp { ws + fnName.source + '\\(' + args.map(r => `${ws}${r.source}${ws}`).join(',') + '\\)' + ws, 'g'); } + +/** + * Creates a macro to replace a union of flags with its numeric constant value. + * + * @param pattern The regex to match a single occurrence of the flag. + * @param getFlagValue A function to extract the numeric flag value from the pattern. + */ +function flagUnion(pattern: RegExp, getFlagValue: (...match: string[]) => number): + typeof EXPECTED_FILE_MACROS[number] { + return [ + // Match at least one occurrence of the pattern, optionally followed by more occurrences + // separated by a pipe. + new RegExp(pattern.source + '(?:\s*\\\|\s*' + pattern.source + ')*', 'g'), + (match: string) => { + // Replace all matches with the union of the individually matched flags. + return String(match.split('|') + .map(flag => getFlagValue(...flag.trim().match(pattern)!)) + .reduce((accumulator, flagValue) => accumulator | flagValue, 0)); + }, + ]; +} diff --git a/packages/compiler-cli/test/compliance/test_helpers/get_compliance_tests.ts b/packages/compiler-cli/test/compliance/test_helpers/get_compliance_tests.ts index 065415311f..e474794706 100644 --- a/packages/compiler-cli/test/compliance/test_helpers/get_compliance_tests.ts +++ b/packages/compiler-cli/test/compliance/test_helpers/get_compliance_tests.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, NodeJSFileSystem, PathSegment} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, NodeJSFileSystem, PathSegment, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; const fs = new NodeJSFileSystem(); const basePath = fs.resolve(__dirname, '../test_cases'); @@ -35,13 +35,16 @@ export function* getComplianceTests(testConfigPath: string): Generator; + + + +/** + * Interface espressing the type for the json object found at ../test_cases/test_case_schema.json. + */ +export interface TestCaseJson { + description: string; + compilationModeFilter?: ('fulll compile'|'linked compile')[]; + inputFiles?: string[]; + expectations?: { + failureMessage?: string; + files?: ExpectedFile[] | string; + expectedErrors?: {message: string, location?: string}; + extraChecks?: (string | string[])[]; + }; + compilerOptions?: ConfigOptions; + angularCompilerOptions?: ConfigOptions; + focusTest?: boolean; + excludeTest?: boolean; +} diff --git a/packages/compiler-cli/test/compliance/test_helpers/golden_partials.ts b/packages/compiler-cli/test/compliance/test_helpers/golden_partials.ts index 579d9cdb06..680d7cdf77 100644 --- a/packages/compiler-cli/test/compliance/test_helpers/golden_partials.ts +++ b/packages/compiler-cli/test/compliance/test_helpers/golden_partials.ts @@ -11,7 +11,7 @@ const headerStart = ' * PARTIAL FILE: '; const headerEnd = - '\n ****************************************************************************************************/'; + '\n ****************************************************************************************************/\n'; /** * Render the partially compiled files into a single golden partial output string. @@ -19,7 +19,7 @@ const headerEnd = * @param files The partially compiled files to be rendered. */ export function renderGoldenPartial(files: PartiallyCompiledFile[]): string { - return files.map(file => `${headerStart + file.path + headerEnd}\n${file.content}`).join('\n'); + return files.map(file => `${headerStart + file.path + headerEnd}${file.content}`).join('\n'); } /** @@ -35,7 +35,9 @@ export function parseGoldenPartial(partialContent: string): PartiallyCompiledFil const partials = partialContent.split(headerStart); for (const partial of partials) { const [path, content] = partial.split(headerEnd); - files.push({path, content}); + if (path) { + files.push({path, content}); + } } return files; } diff --git a/packages/compiler-cli/test/compliance/test_helpers/i18n_helpers.ts b/packages/compiler-cli/test/compliance/test_helpers/i18n_helpers.ts index c9fd5491a0..b2da418764 100644 --- a/packages/compiler-cli/test/compliance/test_helpers/i18n_helpers.ts +++ b/packages/compiler-cli/test/compliance/test_helpers/i18n_helpers.ts @@ -12,6 +12,10 @@ */ let msgIndex = 0; +export function resetMessageIndex(): void { + msgIndex = 0; +} + /** * Generate a string that represents expected i18n block content for a simple message. */ diff --git a/packages/compiler-cli/test/compliance/test_helpers/test_runner.ts b/packages/compiler-cli/test/compliance/test_helpers/test_runner.ts index 59b48d3506..d0de7943cd 100644 --- a/packages/compiler-cli/test/compliance/test_helpers/test_runner.ts +++ b/packages/compiler-cli/test/compliance/test_helpers/test_runner.ts @@ -6,10 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ import {FileSystem} from '../../../src/ngtsc/file_system'; -import {checkExpectations} from '../test_helpers/check_expectations'; -import {CompileResult, initMockTestFileSystem} from '../test_helpers/compile_test'; -import {ComplianceTest, getAllComplianceTests} from '../test_helpers/get_compliance_tests'; -import {checkErrors} from './check_errors'; + +import {checkErrors, checkNoUnexpectedErrors} from './check_errors'; +import {checkExpectations} from './check_expectations'; +import {CompileResult, initMockTestFileSystem} from './compile_test'; +import {CompilationMode, ComplianceTest, getAllComplianceTests} from './get_compliance_tests'; /** * Set up jasmine specs for each of the compliance tests. @@ -18,23 +19,20 @@ import {checkErrors} from './check_errors'; * @param compileFn The function that will do the compilation of the source files */ export function runTests( - type: 'partial compile + link'|'full compile', - compileFn: (fs: FileSystem, test: ComplianceTest) => CompileResult) { - const isPartial = type === 'partial compile + link'; - + type: CompilationMode, compileFn: (fs: FileSystem, test: ComplianceTest) => CompileResult) { describe(`compliance tests (${type})`, () => { for (const test of getAllComplianceTests()) { - if (isPartial && test.excludeFromPartialTests) { + if (!test.compilationModeFilter.includes(type)) { continue; } describe(`[${test.relativePath}]`, () => { const itFn = test.focusTest ? fit : test.excludeTest ? xit : it; itFn(test.description, () => { - if (isPartial && test.compilerOptions?.target === 'ES5') { + if (type === 'linked compile' && test.compilerOptions?.target === 'ES5') { throw new Error( `The "${type}" scenario does not support ES5 output.\n` + - `Did you mean to set \`"excludeFromPartialTests": true\` in "${ + `Did you mean to set \`"compilationModeFilter": ["full compile"]\` in "${ test.relativePath}"?`); } @@ -46,6 +44,7 @@ export function runTests( test.relativePath, expectation.failureMessage, expectation.expectedErrors, errors); } else { + checkNoUnexpectedErrors(test.relativePath, errors); checkExpectations( fs, test.relativePath, expectation.failureMessage, expectation.files, expectation.extraChecks); diff --git a/packages/compiler-cli/test/compliance_old/prelink/BUILD.bazel b/packages/compiler-cli/test/compliance_old/prelink/BUILD.bazel index f0d5f7bb8d..3953c78dc9 100644 --- a/packages/compiler-cli/test/compliance_old/prelink/BUILD.bazel +++ b/packages/compiler-cli/test/compliance_old/prelink/BUILD.bazel @@ -6,7 +6,10 @@ ts_library( srcs = ["bootstrap.ts"], deps = [ "//packages:types", + "//packages/compiler-cli/linker", "//packages/compiler-cli/linker/babel", + "//packages/compiler-cli/src/ngtsc/file_system/testing", + "//packages/compiler-cli/src/ngtsc/logging/testing", "//packages/compiler-cli/test/compliance_old/mock_compile", "@npm//@babel/core", "@npm//@babel/generator", diff --git a/packages/compiler-cli/test/compliance_old/prelink/bootstrap.ts b/packages/compiler-cli/test/compliance_old/prelink/bootstrap.ts index 1682850877..3830156273 100644 --- a/packages/compiler-cli/test/compliance_old/prelink/bootstrap.ts +++ b/packages/compiler-cli/test/compliance_old/prelink/bootstrap.ts @@ -8,7 +8,10 @@ import {PluginObj, transformSync} from '@babel/core'; import * as ts from 'typescript'; +import {needsLinking} from '../../../linker'; import {createEs2015LinkerPlugin} from '../../../linker/babel'; +import {MockFileSystemNative} from '../../../src/ngtsc/file_system/testing'; +import {MockLogger} from '../../../src/ngtsc/logging/testing'; import {compileFiles, CompileFn, setCompileFn} from '../mock_compile'; /** @@ -27,14 +30,20 @@ const linkedCompile: CompileFn = (data, angularFiles, options) => { } const compiledFiles = compileFiles(data, angularFiles, {...options, compilationMode: 'partial'}); - + const fileSystem = new MockFileSystemNative(); + const logger = new MockLogger(); const linkerPlugin = createEs2015LinkerPlugin({ + fileSystem, + logger, // enableI18nLegacyMessageIdFormat defaults to false in `compileFiles`. enableI18nLegacyMessageIdFormat: false, ...options, }); - const source = compiledFiles.map(file => applyLinker(file, linkerPlugin)).join('\n'); + const source = + compiledFiles + .map(file => applyLinker({path: file.fileName, source: file.source}, linkerPlugin)) + .join('\n'); return {source}; }; @@ -42,16 +51,16 @@ const linkedCompile: CompileFn = (data, angularFiles, options) => { /** * Runs the provided code through the Babel linker plugin, if the file has the .js extension. * - * @param file The file name and its source to be transformed using the linker. + * @param file The absolute file path and its source to be transformed using the linker. * @param linkerPlugin The linker plugin to apply. * @returns The file's source content, which has been transformed using the linker if necessary. */ -function applyLinker(file: {fileName: string; source: string}, linkerPlugin: PluginObj): string { - if (!file.fileName.endsWith('.js')) { +function applyLinker(file: {path: string; source: string}, linkerPlugin: PluginObj): string { + if (!file.path.endsWith('.js') || !needsLinking(file.path, file.source)) { return file.source; } const result = transformSync(file.source, { - filename: file.fileName, + filename: file.path, plugins: [linkerPlugin], parserOpts: {sourceType: 'unambiguous'}, }); diff --git a/packages/compiler-cli/test/compliance_old/r3_compiler_compliance_spec.ts b/packages/compiler-cli/test/compliance_old/r3_compiler_compliance_spec.ts index b83844c31a..8b99bf37ad 100644 --- a/packages/compiler-cli/test/compliance_old/r3_compiler_compliance_spec.ts +++ b/packages/compiler-cli/test/compliance_old/r3_compiler_compliance_spec.ts @@ -477,7 +477,7 @@ describe('compiler compliance', () => { const factory = 'MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }'; const template = ` - MyComponent.ɵcmp = i0.ɵɵdefineComponent({type:MyComponent,selectors:[["my-component"]], + MyComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({type:MyComponent,selectors:[["my-component"]], decls: 1, vars: 4, template: function MyComponent_Template(rf,ctx){ @@ -564,7 +564,7 @@ describe('compiler compliance', () => { // ChildComponent definition should be: const ChildComponentDefinition = ` - ChildComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + ChildComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: ChildComponent, selectors: [["child"]], decls: 1, @@ -582,7 +582,7 @@ describe('compiler compliance', () => { // SomeDirective definition should be: const SomeDirectiveDefinition = ` - SomeDirective.ɵdir = $r3$.ɵɵdefineDirective({ + SomeDirective.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ type: SomeDirective, selectors: [["", "some-directive", ""]] }); @@ -594,7 +594,7 @@ describe('compiler compliance', () => { // MyComponent definition should be: const MyComponentDefinition = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 2, @@ -645,7 +645,7 @@ describe('compiler compliance', () => { // SomeDirective definition should be: const SomeDirectiveDefinition = ` - SomeDirective.ɵdir = $r3$.ɵɵdefineDirective({ + SomeDirective.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ type: SomeDirective, selectors: [["div", "some-directive", "", 8, "foo", 3, "title", "", 9, "baz"]] }); @@ -656,7 +656,7 @@ describe('compiler compliance', () => { // OtherDirective definition should be: const OtherDirectiveDefinition = ` - OtherDirective.ɵdir = $r3$.ɵɵdefineDirective({ + OtherDirective.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ type: OtherDirective, selectors: [["", 5, "span", "title", "", 9, "baz"]] }); @@ -691,7 +691,7 @@ describe('compiler compliance', () => { // SomeDirective definition should be: const SomeDirectiveDefinition = ` - SomeComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + SomeComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: SomeComponent, selectors: [["", "id", "my-app"]], … @@ -721,7 +721,7 @@ describe('compiler compliance', () => { // EmptyOutletComponent definition should be: const EmptyOutletComponentDefinition = ` … - EmptyOutletComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + EmptyOutletComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: EmptyOutletComponent, selectors: [["ng-component"]], decls: 1, @@ -768,7 +768,7 @@ describe('compiler compliance', () => { const MyComponentDefinition = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 0, @@ -816,7 +816,7 @@ describe('compiler compliance', () => { }; const IfDirectiveDefinition = ` - IfDirective.ɵdir = $r3$.ɵɵdefineDirective({ + IfDirective.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ type: IfDirective, selectors: [["", "if", ""]] });`; @@ -838,7 +838,7 @@ describe('compiler compliance', () => { } } … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 3, @@ -904,7 +904,7 @@ describe('compiler compliance', () => { const MyAppDeclaration = ` const $e0_ff$ = function ($v$) { return ["Nancy", $v$]; }; … - MyApp.ɵcmp = $r3$.ɵɵdefineComponent({ + MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyApp, selectors: [["my-app"]], decls: 1, @@ -985,7 +985,7 @@ describe('compiler compliance', () => { return ["start-", $v0$, $v1$, $v2$, $v3$, $v4$, "-middle-", $v5$, $v6$, $v7$, $v8$, "-end"]; } … - MyApp.ɵcmp = $r3$.ɵɵdefineComponent({ + MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyApp, selectors: [["my-app"]], decls: 1, @@ -1047,7 +1047,7 @@ describe('compiler compliance', () => { const MyAppDefinition = ` const $e0_ff$ = function ($v$) { return {"duration": 500, animation: $v$}; }; … - MyApp.ɵcmp = $r3$.ɵɵdefineComponent({ + MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyApp, selectors: [["my-app"]], decls: 1, @@ -1114,7 +1114,7 @@ describe('compiler compliance', () => { const $e0_ff_1$ = function ($v1$, $v2$) { return [$v1$, $v2$]; }; const $e0_ff_2$ = function ($v1$, $v2$) { return {animation: $v1$, actions: $v2$}; }; … - MyApp.ɵcmp = $r3$.ɵɵdefineComponent({ + MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyApp, selectors: [["my-app"]], decls: 1, @@ -1173,7 +1173,7 @@ describe('compiler compliance', () => { }; const SimpleComponentDefinition = ` - SimpleComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + SimpleComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: SimpleComponent, selectors: [["simple"]], ngContentSelectors: $c0$, @@ -1193,7 +1193,7 @@ describe('compiler compliance', () => { const ComplexComponentDefinition = ` const $c1$ = [[["span", "title", "tofirst"]], [["span", "title", "tosecond"]]]; … - ComplexComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + ComplexComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: ComplexComponent, selectors: [["complex"]], ngContentSelectors: $c2$, @@ -1249,7 +1249,7 @@ describe('compiler compliance', () => { const $c0$ = ["*", [["", "spacer", ""]], "*"]; const $c1$ = ["*", "[spacer]", "*"]; … - Cmp.ɵcmp = $r3$.ɵɵdefineComponent({ + Cmp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: Cmp, selectors: [["ng-component"]], ngContentSelectors: $c1$, @@ -1434,7 +1434,7 @@ describe('compiler compliance', () => { const $_c0$ = [[["", "title", ""]]]; const $_c1$ = ["[title]"]; … - MyApp.ɵcmp = $r3$.ɵɵdefineComponent({ + MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyApp, selectors: [["my-app"]], decls: 2, @@ -1486,7 +1486,7 @@ describe('compiler compliance', () => { const $_c0$ = [[["", "title", ""]]]; const $_c1$ = ["[title]"]; … - MyApp.ɵcmp = $r3$.ɵɵdefineComponent({ + MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyApp, selectors: [["my-app"]], decls: 2, @@ -1526,7 +1526,7 @@ describe('compiler compliance', () => { }; const SimpleComponentDefinition = ` - MyApp.ɵcmp = i0.ɵɵdefineComponent({ + MyApp.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MyApp, selectors: [ ["my-app"] @@ -1567,7 +1567,7 @@ describe('compiler compliance', () => { }; const SimpleComponentDefinition = ` - SimpleComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + SimpleComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: SimpleComponent, selectors: [["simple"]], ngContentSelectors: $c0$, @@ -1631,13 +1631,13 @@ describe('compiler compliance', () => { const ViewQueryComponentDefinition = ` … - ViewQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + ViewQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: ViewQueryComponent, selectors: [["view-query-component"]], viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery(SomeDirective, true); - $r3$.ɵɵviewQuery(SomeDirective, true); + $r3$.ɵɵviewQuery(SomeDirective, 5); + $r3$.ɵɵviewQuery(SomeDirective, 5); } if (rf & 2) { let $tmp$; @@ -1691,12 +1691,12 @@ describe('compiler compliance', () => { const $e0_attrs$ = ["myRef"]; const $e1_attrs$ = ["myRef1", "myRef2", "myRef3"]; … - ViewQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + ViewQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery($e0_attrs$, true); - $r3$.ɵɵviewQuery($e1_attrs$, true); + $r3$.ɵɵviewQuery($e0_attrs$, 5); + $r3$.ɵɵviewQuery($e1_attrs$, 5); } if (rf & 2) { let $tmp$; @@ -1741,13 +1741,13 @@ describe('compiler compliance', () => { const ViewQueryComponentDefinition = ` const $refs$ = ["foo"]; … - ViewQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + ViewQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: ViewQueryComponent, selectors: [["view-query-component"]], viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵstaticViewQuery(SomeDirective, true); - $r3$.ɵɵviewQuery($refs$, true); + $r3$.ɵɵviewQuery(SomeDirective, 7); + $r3$.ɵɵviewQuery($refs$, 5); } if (rf & 2) { let $tmp$; @@ -1806,14 +1806,14 @@ describe('compiler compliance', () => { const $e0_attrs$ = ["myRef"]; const $e1_attrs$ = ["myRef1", "myRef2", "myRef3"]; … - ViewQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + ViewQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery($e0_attrs$, true, TemplateRef); - $r3$.ɵɵviewQuery(SomeDirective, true, ElementRef); - $r3$.ɵɵviewQuery($e1_attrs$, true, ElementRef); - $r3$.ɵɵviewQuery(SomeDirective, true, TemplateRef); + $r3$.ɵɵviewQuery($e0_attrs$, 5, TemplateRef); + $r3$.ɵɵviewQuery(SomeDirective, 5, ElementRef); + $r3$.ɵɵviewQuery($e1_attrs$, 5, ElementRef); + $r3$.ɵɵviewQuery(SomeDirective, 5, TemplateRef); } if (rf & 2) { let $tmp$; @@ -1868,13 +1868,13 @@ describe('compiler compliance', () => { }; const ContentQueryComponentDefinition = ` - ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + ContentQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: ContentQueryComponent, selectors: [["content-query-component"]], contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, true); - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, false); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 5); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 4); } if (rf & 2) { let $tmp$; @@ -1929,12 +1929,12 @@ describe('compiler compliance', () => { const $e0_attrs$ = ["myRef"]; const $e1_attrs$ = ["myRef1", "myRef2", "myRef3"]; … - ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + ContentQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, true); - $r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, false); + $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 5); + $r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 4); } if (rf & 2) { let $tmp$; @@ -1987,13 +1987,13 @@ describe('compiler compliance', () => { }; const ContentQueryComponentDefinition = ` - ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + ContentQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: ContentQueryComponent, selectors: [["content-query-component"]], contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵstaticContentQuery(dirIndex, SomeDirective, true); - $r3$.ɵɵcontentQuery(dirIndex, $ref0$, true); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 7); + $r3$.ɵɵcontentQuery(dirIndex, $ref0$, 5); } if (rf & 2) { let $tmp$; @@ -2053,14 +2053,14 @@ describe('compiler compliance', () => { const $e0_attrs$ = ["myRef"]; const $e1_attrs$ = ["myRef1", "myRef2", "myRef3"]; … - ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + ContentQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, true, TemplateRef); - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, true, ElementRef); - $r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, false, ElementRef); - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, false, TemplateRef); + $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 5, TemplateRef); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 5, ElementRef); + $r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 4, ElementRef); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 4, TemplateRef); } if (rf & 2) { let $tmp$; @@ -2121,7 +2121,7 @@ describe('compiler compliance', () => { }; const MyPipeDefinition = ` - MyPipe.ɵpipe = $r3$.ɵɵdefinePipe({ + MyPipe.ɵpipe = /*@__PURE__*/ $r3$.ɵɵdefinePipe({ name: "myPipe", type: MyPipe, pure: false @@ -2133,7 +2133,7 @@ describe('compiler compliance', () => { `; const MyPurePipeDefinition = ` - MyPurePipe.ɵpipe = $r3$.ɵɵdefinePipe({ + MyPurePipe.ɵpipe = /*@__PURE__*/ $r3$.ɵɵdefinePipe({ name: "myPurePipe", type: MyPurePipe, pure: true @@ -2148,7 +2148,7 @@ describe('compiler compliance', () => { return [$a0$, 1, 2, 3, 4, 5]; }; // ... - MyApp.ɵcmp = $r3$.ɵɵdefineComponent({ + MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyApp, selectors: [["my-app"]], decls: 7, @@ -2215,7 +2215,7 @@ describe('compiler compliance', () => { const MyAppDefinition = ` // ... - MyApp.ɵcmp = $r3$.ɵɵdefineComponent({ + MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyApp, selectors: [["my-app"]], decls: 6, @@ -2286,7 +2286,7 @@ describe('compiler compliance', () => { }; const MyPipeDefinition = ` - MyPipe.ɵpipe = $r3$.ɵɵdefinePipe({ + MyPipe.ɵpipe = /*@__PURE__*/ $r3$.ɵɵdefinePipe({ name: "myPipe", type: MyPipe, pure: true @@ -2294,18 +2294,18 @@ describe('compiler compliance', () => { `; const MyPipeFactory = ` - MyPipe.ɵfac = function MyPipe_Factory(t) { return new (t || MyPipe)($r3$.ɵɵinjectPipeChangeDetectorRef()); }; + MyPipe.ɵfac = function MyPipe_Factory(t) { return new (t || MyPipe)($i0$.ɵɵdirectiveInject($i0$.ChangeDetectorRef, 16)); }; `; const MyOtherPipeDefinition = ` - MyOtherPipe.ɵpipe = $r3$.ɵɵdefinePipe({ + MyOtherPipe.ɵpipe = /*@__PURE__*/ $r3$.ɵɵdefinePipe({ name: "myOtherPipe", type: MyOtherPipe, pure: true });`; const MyOtherPipeFactory = ` - MyOtherPipe.ɵfac = function MyOtherPipe_Factory(t) { return new (t || MyOtherPipe)($r3$.ɵɵinjectPipeChangeDetectorRef(8)); }; + MyOtherPipe.ɵfac = function MyOtherPipe_Factory(t) { return new (t || MyOtherPipe)($i0$.ɵɵdirectiveInject($i0$.ChangeDetectorRef, 24)); }; `; const result = compile(files, angularFiles); @@ -2335,7 +2335,7 @@ describe('compiler compliance', () => { const MyComponentDefinition = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 3, @@ -2428,7 +2428,7 @@ describe('compiler compliance', () => { } } … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 6, @@ -2572,7 +2572,7 @@ describe('compiler compliance', () => { it('should gen hooks with a few simple components', () => { const LifecycleCompDefinition = ` - LifecycleComp.ɵcmp = $r3$.ɵɵdefineComponent({ + LifecycleComp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: LifecycleComp, selectors: [["lifecycle-comp"]], inputs: {nameMin: ["name", "nameMin"]}, @@ -2584,7 +2584,7 @@ describe('compiler compliance', () => { });`; const SimpleLayoutDefinition = ` - SimpleLayout.ɵcmp = $r3$.ɵɵdefineComponent({ + SimpleLayout.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: SimpleLayout, selectors: [["simple-layout"]], decls: 2, @@ -2693,7 +2693,7 @@ describe('compiler compliance', () => { // TODO(benlesh): Enforce this when the directives are specified const ForDirectiveDefinition = ` - ForOfDirective.ɵdir = $r3$.ɵɵdefineDirective({ + ForOfDirective.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ type: ForOfDirective, selectors: [["", "forOf", ""]], features: [$r3$.ɵɵNgOnChangesFeature], @@ -2715,7 +2715,7 @@ describe('compiler compliance', () => { } } … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 2, @@ -2773,7 +2773,7 @@ describe('compiler compliance', () => { // TODO(chuckj): Enforce this when the directives are specified const ForDirectiveDefinition = ` - ForOfDirective.ɵdir = $r3$.ɵɵdefineDirective({ + ForOfDirective.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ type: ForOfDirective, selectors: [["", "forOf", ""]], features: [$r3$.ɵɵNgOnChangesFeature], @@ -2801,7 +2801,7 @@ describe('compiler compliance', () => { } } … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 2, @@ -2905,7 +2905,7 @@ describe('compiler compliance', () => { } … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 2, @@ -3023,7 +3023,7 @@ describe('compiler compliance', () => { // SomeDirective definition should be: const SomeDirectiveDefinition = ` - SomeDirective.ɵdir = $r3$.ɵɵdefineDirective({ + SomeDirective.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ type: SomeDirective, selectors: [["", "some-directive", ""]], exportAs: ["someDir", "otherDir"] @@ -3071,7 +3071,7 @@ describe('compiler compliance', () => { }; const expectedOutput = ` // ... - AbstractDirective.ɵdir = $r3$.ɵɵdefineDirective({ + AbstractDirective.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ type: AbstractDirective }); // ... @@ -3099,7 +3099,7 @@ describe('compiler compliance', () => { const $c0$ = function () { return {}; }; const $c1$ = function () { return { a: 1, b: 2 }; }; … - MyApp.ɵcmp = $r3$.ɵɵdefineComponent({ + MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyApp, selectors: [["ng-component"]], decls: 1, @@ -3140,7 +3140,7 @@ describe('compiler compliance', () => { const $c0$ = function () { return []; }; const $c1$ = function () { return [0, 1, 2]; }; … - MyApp.ɵcmp = $r3$.ɵɵdefineComponent({ + MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyApp, selectors: [["ng-component"]], decls: 1, @@ -3187,7 +3187,7 @@ describe('compiler compliance', () => { const $c1$ = function () { return {}; }; const $c2$ = function (a0) { return { foo: a0 }; }; … - MyApp.ɵcmp = $r3$.ɵɵdefineComponent({ + MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyApp, selectors: [["ng-component"]], decls: 2, @@ -3237,7 +3237,7 @@ describe('compiler compliance', () => { const $c1$ = function () { return []; }; const $c2$ = function (a0) { return { foo: a0 }; }; … - MyApp.ɵcmp = $r3$.ɵɵdefineComponent({ + MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyApp, selectors: [["ng-component"]], decls: 2, @@ -3290,7 +3290,7 @@ describe('compiler compliance', () => { const $c0$ = function () { return { foo: null }; }; const $c1$ = function (a0) { return { foo: a0 }; }; … - MyApp.ɵcmp = $r3$.ɵɵdefineComponent({ + MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyApp, selectors: [["ng-component"]], decls: 2, @@ -3340,14 +3340,14 @@ describe('compiler compliance', () => { // The setClassMetadata call should look like this. const setClassMetadata = ` - … - (function() { - i0.ɵsetClassMetadata(Comp, [{ - type: Component, - args: [{ - template: '', - providers: [{ provide: token, useExisting: Comp }] - `; + (function() { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(Comp, [{ + type: Component, + args: [{ + template: '', + providers: [{provide: token, useExisting: Comp}], + }] + }], null, null); })(); + `; const result = compile(files, angularFiles, {target: ts.ScriptTarget.ES5}); expectEmit(result.source, setClassMetadata, 'Incorrect setClassMetadata call'); diff --git a/packages/compiler-cli/test/compliance_old/r3_view_compiler_binding_spec.ts b/packages/compiler-cli/test/compliance_old/r3_view_compiler_binding_spec.ts index 213718ceb6..206f4ff1b4 100644 --- a/packages/compiler-cli/test/compliance_old/r3_view_compiler_binding_spec.ts +++ b/packages/compiler-cli/test/compliance_old/r3_view_compiler_binding_spec.ts @@ -705,7 +705,7 @@ describe('compiler compliance: bindings', () => { }; const HostBindingDirDeclaration = ` - HostBindingDir.ɵdir = $r3$.ɵɵdefineDirective({ + HostBindingDir.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ type: HostBindingDir, selectors: [["", "hostBindingDir", ""]], hostVars: 1, @@ -744,7 +744,7 @@ describe('compiler compliance: bindings', () => { }; const HostBindingDirDeclaration = ` - HostBindingDir.ɵdir = $r3$.ɵɵdefineDirective({ + HostBindingDir.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ type: HostBindingDir, selectors: [["", "hostBindingDir", ""]], hostVars: 1, @@ -789,7 +789,7 @@ describe('compiler compliance: bindings', () => { const HostBindingCompDeclaration = ` const $ff$ = function ($v$) { return ["red", $v$]; }; … - HostBindingComp.ɵcmp = $r3$.ɵɵdefineComponent({ + HostBindingComp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: HostBindingComp, selectors: [["host-binding-comp"]], hostVars: 3, @@ -834,7 +834,7 @@ describe('compiler compliance: bindings', () => { }; const HostAttributeDirDeclaration = ` - HostAttributeDir.ɵdir = $r3$.ɵɵdefineDirective({ + HostAttributeDir.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ type: HostAttributeDir, selectors: [["", "hostAttributeDir", ""]], hostVars: 1, @@ -874,7 +874,7 @@ describe('compiler compliance: bindings', () => { }; const HostAttributeDirDeclaration = ` - HostAttributeDir.ɵdir = $r3$.ɵɵdefineDirective({ + HostAttributeDir.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ type: HostAttributeDir, selectors: [["", "hostAttributeDir", ""]], hostAttrs: ["aria-label", "label"] @@ -924,13 +924,13 @@ describe('compiler compliance: bindings', () => { }; const CompAndDirDeclaration = ` - HostAttributeComp.ɵcmp = $r3$.ɵɵdefineComponent({ + HostAttributeComp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: HostAttributeComp, selectors: [["my-host-attribute-component"]], hostAttrs: ["title", "hello there from component", ${ AttributeMarker.Styles}, "opacity", "1"], … - HostAttributeDir.ɵdir = $r3$.ɵɵdefineDirective({ + HostAttributeDir.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ type: HostAttributeDir, selectors: [["", "hostAttributeDir", ""]], hostAttrs: ["title", "hello there from directive", ${ diff --git a/packages/compiler-cli/test/compliance_old/r3_view_compiler_di_spec.ts b/packages/compiler-cli/test/compliance_old/r3_view_compiler_di_spec.ts index 3c299e8f2c..91820d3953 100644 --- a/packages/compiler-cli/test/compliance_old/r3_view_compiler_di_spec.ts +++ b/packages/compiler-cli/test/compliance_old/r3_view_compiler_di_spec.ts @@ -25,6 +25,10 @@ describe('compiler compliance: dependency injection', () => { @Injectable() export class MyService {} + function dynamicAttrName() { + return 'the-attr'; + } + @Component({ selector: 'my-component', template: \`\` @@ -32,6 +36,7 @@ describe('compiler compliance: dependency injection', () => { export class MyComponent { constructor( @Attribute('name') name:string, + @Attribute(dynamicAttrName()) other: string, s1: MyService, @Host() s2: MyService, @Self() s4: MyService, @@ -51,6 +56,7 @@ describe('compiler compliance: dependency injection', () => { MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)( $r3$.ɵɵinjectAttribute('name'), + $r3$.ɵɵinjectAttribute(dynamicAttrName()), $r3$.ɵɵdirectiveInject(MyService), $r3$.ɵɵdirectiveInject(MyService, 1), $r3$.ɵɵdirectiveInject(MyService, 2), @@ -88,7 +94,7 @@ describe('compiler compliance: dependency injection', () => { }`; const def = ` - MyService.ɵprov = $r3$.ɵɵdefineInjectable({ + MyService.ɵprov = /*@__PURE__*/ $r3$.ɵɵdefineInjectable({ token: MyService, factory: MyService.ɵfac }); @@ -123,7 +129,7 @@ describe('compiler compliance: dependency injection', () => { }`; const def = ` - MyService.ɵprov = $r3$.ɵɵdefineInjectable({ + MyService.ɵprov = /*@__PURE__*/ $r3$.ɵɵdefineInjectable({ token: MyService, factory: MyService.ɵfac }); @@ -176,7 +182,7 @@ describe('compiler compliance: dependency injection', () => { }; const def = ` - MyService.ɵprov = $r3$.ɵɵdefineInjectable({ + MyService.ɵprov = /*@__PURE__*/ $r3$.ɵɵdefineInjectable({ token: MyService, factory: function() { return alternateFactory(); @@ -209,7 +215,7 @@ describe('compiler compliance: dependency injection', () => { }; const def = ` - MyService.ɵprov = $r3$.ɵɵdefineInjectable({ + MyService.ɵprov = /*@__PURE__*/ $r3$.ɵɵdefineInjectable({ token: MyService, factory: function MyService_Factory(t) { let r = null; @@ -247,7 +253,7 @@ describe('compiler compliance: dependency injection', () => { }; const factory = ` - MyService.ɵprov = $r3$.ɵɵdefineInjectable({ + MyService.ɵprov = /*@__PURE__*/ $r3$.ɵɵdefineInjectable({ token: MyService, factory: function(t) { return MyAlternateService.ɵfac(t); @@ -282,7 +288,7 @@ describe('compiler compliance: dependency injection', () => { }; const factory = ` - MyService.ɵprov = $r3$.ɵɵdefineInjectable({ + MyService.ɵprov = /*@__PURE__*/ $r3$.ɵɵdefineInjectable({ token: MyService, factory: function MyService_Factory(t) { let r = null; @@ -318,7 +324,7 @@ describe('compiler compliance: dependency injection', () => { }; const factory = ` - SomeProvider.ɵprov = $r3$.ɵɵdefineInjectable({ + SomeProvider.ɵprov = /*@__PURE__*/ $r3$.ɵɵdefineInjectable({ token: SomeProvider, factory: function(t) { return SomeProviderImpl.ɵfac(t); @@ -372,16 +378,16 @@ describe('compiler compliance: dependency injection', () => { // The prov definition must be last so MyPipe.fac is defined const MyPipeDefs = ` - MyPipe.ɵfac = function MyPipe_Factory(t) { return new (t || MyPipe)(i0.ɵɵdirectiveInject(Service)); }; - MyPipe.ɵpipe = i0.ɵɵdefinePipe({ name: "myPipe", type: MyPipe, pure: true }); - MyPipe.ɵprov = i0.ɵɵdefineInjectable({ token: MyPipe, factory: MyPipe.ɵfac }); + MyPipe.ɵfac = function MyPipe_Factory(t) { return new (t || MyPipe)(i0.ɵɵdirectiveInject(Service, 16)); }; + MyPipe.ɵpipe = /*@__PURE__*/ i0.ɵɵdefinePipe({ name: "myPipe", type: MyPipe, pure: true }); + MyPipe.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: MyPipe, factory: MyPipe.ɵfac }); `; // The prov definition must be last so MyOtherPipe.fac is defined const MyOtherPipeDefs = ` - MyOtherPipe.ɵfac = function MyOtherPipe_Factory(t) { return new (t || MyOtherPipe)($r3$.ɵɵdirectiveInject(Service)); }; - MyOtherPipe.ɵpipe = i0.ɵɵdefinePipe({ name: "myOtherPipe", type: MyOtherPipe, pure: true }); - MyOtherPipe.ɵprov = i0.ɵɵdefineInjectable({ token: MyOtherPipe, factory: MyOtherPipe.ɵfac }); + MyOtherPipe.ɵfac = function MyOtherPipe_Factory(t) { return new (t || MyOtherPipe)($r3$.ɵɵdirectiveInject(Service, 16)); }; + MyOtherPipe.ɵpipe = /*@__PURE__*/ i0.ɵɵdefinePipe({ name: "myOtherPipe", type: MyOtherPipe, pure: true }); + MyOtherPipe.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: MyOtherPipe, factory: MyOtherPipe.ɵfac }); `; expectEmit(source, MyPipeDefs, 'Invalid pipe factory function'); diff --git a/packages/compiler-cli/test/compliance_old/r3_view_compiler_directives_spec.ts b/packages/compiler-cli/test/compliance_old/r3_view_compiler_directives_spec.ts index 01b73050f2..e5019a9aed 100644 --- a/packages/compiler-cli/test/compliance_old/r3_view_compiler_directives_spec.ts +++ b/packages/compiler-cli/test/compliance_old/r3_view_compiler_directives_spec.ts @@ -36,7 +36,7 @@ describe('compiler compliance: directives', () => { // MyComponent definition should be: const MyComponentDefinition = ` - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 1, @@ -86,7 +86,7 @@ describe('compiler compliance: directives', () => { // MyComponent definition should be: const MyComponentDefinition = ` - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 1, @@ -111,7 +111,7 @@ describe('compiler compliance: directives', () => { expectEmit(source, MyComponentFactory, 'Incorrect ChildComponent.ɵfac'); }); - it('should match directives on element bindings', () => { + it('should match directives on property bindings', () => { const files = { app: { 'spec.ts': ` @@ -135,7 +135,7 @@ describe('compiler compliance: directives', () => { // MyComponent definition should be: const MyComponentDefinition = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … consts: [[${AttributeMarker.Bindings}, "someDirective"]], template: function MyComponent_Template(rf, ctx) { @@ -193,7 +193,7 @@ describe('compiler compliance: directives', () => { } } … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … consts: [["directiveA", ""]], template: function MyComponent_Template(rf, ctx) { @@ -248,7 +248,7 @@ describe('compiler compliance: directives', () => { } } … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … consts: [["directiveA", "", ${AttributeMarker.Template}, "ngIf"], ["directiveA", ""]], template: function MyComponent_Template(rf, ctx) { @@ -293,7 +293,7 @@ describe('compiler compliance: directives', () => { // MyComponent definition should be: const MyComponentDefinition = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … consts: [[${AttributeMarker.Bindings}, "someDirective"]], template: function MyComponent_Template(rf, ctx) { @@ -339,7 +339,7 @@ describe('compiler compliance: directives', () => { // MyComponent definition should be: const MyComponentDefinition = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … consts: [[${AttributeMarker.Template}, "someDirective"]], template: function MyComponent_Template(rf, ctx) { @@ -385,7 +385,7 @@ describe('compiler compliance: directives', () => { // MyComponent definition should be: const MyComponentDefinition = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … consts: [[${AttributeMarker.Bindings}, "someDirective"]], template: function MyComponent_Template(rf, ctx) { diff --git a/packages/compiler-cli/test/compliance_old/r3_view_compiler_i18n_spec.ts b/packages/compiler-cli/test/compliance_old/r3_view_compiler_i18n_spec.ts index e87b65c0a5..f0cf9ac3a5 100644 --- a/packages/compiler-cli/test/compliance_old/r3_view_compiler_i18n_spec.ts +++ b/packages/compiler-cli/test/compliance_old/r3_view_compiler_i18n_spec.ts @@ -3325,61 +3325,6 @@ $` + String.raw`{$I18N_4$}:ICU:\`; }); }); - describe('errors', () => { - const verifyNestedSectionsError = (errorThrown: any, expectedErrorText: string) => { - expect(errorThrown.ngParseErrors.length).toBe(1); - const msg = errorThrown.ngParseErrors[0].toString(); - expect(msg).toContain( - 'Cannot mark an element as translatable inside of a translatable section. Please remove the nested i18n marker.'); - expect(msg).toContain(expectedErrorText); - expect(msg).toMatch(/app\/spec\.ts\@\d+\:\d+/); - }; - - it('should throw on nested i18n sections', () => { - const files = getAppFilesWithTemplate(` -
    -
    Some content
    -
    - `); - try { - compile(files, angularFiles); - } catch (error) { - verifyNestedSectionsError(error, '[ERROR ->]
    Some content
    '); - } - }); - - it('should throw on nested i18n sections with tags in between', () => { - const files = getAppFilesWithTemplate(` -
    -
    -
    Some content
    -
    -
    - `); - try { - compile(files, angularFiles); - } catch (error) { - verifyNestedSectionsError(error, '[ERROR ->]
    Some content
    '); - } - }); - - it('should throw on nested i18n sections represented with s', () => { - const files = getAppFilesWithTemplate(` - -
    - Some content -
    -
    - `); - try { - compile(files, angularFiles); - } catch (error) { - verifyNestedSectionsError( - error, '[ERROR ->]Some content'); - } - }); - }); - describe('namespaces', () => { it('should handle namespaces inside i18n blocks', () => { const input = ` diff --git a/packages/compiler-cli/test/compliance_old/r3_view_compiler_input_outputs_spec.ts b/packages/compiler-cli/test/compliance_old/r3_view_compiler_input_outputs_spec.ts index 9d012cc70f..8c62938582 100644 --- a/packages/compiler-cli/test/compliance_old/r3_view_compiler_input_outputs_spec.ts +++ b/packages/compiler-cli/test/compliance_old/r3_view_compiler_input_outputs_spec.ts @@ -52,7 +52,7 @@ describe('compiler compliance: listen()', () => { }; const componentDef = ` - MyComponent.ɵcmp = IDENT.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ IDENT.ɵɵdefineComponent({ … inputs:{ componentInput: "componentInput", @@ -66,7 +66,7 @@ describe('compiler compliance: listen()', () => { });`; const directiveDef = ` - MyDirective.ɵdir = IDENT.ɵɵdefineDirective({ + MyDirective.ɵdir = /*@__PURE__*/ IDENT.ɵɵdefineDirective({ … inputs:{ directiveInput: "directiveInput", diff --git a/packages/compiler-cli/test/compliance_old/r3_view_compiler_listener_spec.ts b/packages/compiler-cli/test/compliance_old/r3_view_compiler_listener_spec.ts index 0173f6f0cd..ae160dbdb7 100644 --- a/packages/compiler-cli/test/compliance_old/r3_view_compiler_listener_spec.ts +++ b/packages/compiler-cli/test/compliance_old/r3_view_compiler_listener_spec.ts @@ -194,7 +194,7 @@ describe('compiler compliance: listen()', () => { const MyComponentDefinition = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 4, diff --git a/packages/compiler-cli/test/compliance_old/r3_view_compiler_providers_spec.ts b/packages/compiler-cli/test/compliance_old/r3_view_compiler_providers_spec.ts index bae67d1ad1..f14e0ffe97 100644 --- a/packages/compiler-cli/test/compliance_old/r3_view_compiler_providers_spec.ts +++ b/packages/compiler-cli/test/compliance_old/r3_view_compiler_providers_spec.ts @@ -145,7 +145,7 @@ describe('compiler compliance: providers', () => { export class MyComponent { } MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; - MyComponent.ɵcmp = i0.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 1, diff --git a/packages/compiler-cli/test/compliance_old/r3_view_compiler_styling_spec.ts b/packages/compiler-cli/test/compliance_old/r3_view_compiler_styling_spec.ts index 7fdd3a4132..a28ea3f2b4 100644 --- a/packages/compiler-cli/test/compliance_old/r3_view_compiler_styling_spec.ts +++ b/packages/compiler-cli/test/compliance_old/r3_view_compiler_styling_spec.ts @@ -95,7 +95,7 @@ describe('compiler compliance: styling', () => { }; const template = ` - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … styles: ["div.cool { color: blue; }", ":host.nice p { color: gold; }"], encapsulation: 3 @@ -128,7 +128,7 @@ describe('compiler compliance: styling', () => { }; const template = ` - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors:[["my-component"]], decls: 0, @@ -167,7 +167,7 @@ describe('compiler compliance: styling', () => { }; const template = ` - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors:[["my-component"]], decls: 0, @@ -209,7 +209,7 @@ describe('compiler compliance: styling', () => { const template = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … decls: 3, vars: 3, @@ -270,7 +270,7 @@ describe('compiler compliance: styling', () => { const template = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … decls: 1, vars: 1, @@ -331,7 +331,7 @@ describe('compiler compliance: styling', () => { }; const template = ` - MyAnimDir.ɵdir = $r3$.ɵɵdefineDirective({ + MyAnimDir.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ … hostVars: 1, hostBindings: function MyAnimDir_HostBindings(rf, ctx) { @@ -500,7 +500,7 @@ describe('compiler compliance: styling', () => { const template = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors:[["my-component"]], decls: 1, @@ -546,7 +546,7 @@ describe('compiler compliance: styling', () => { }; const template = ` - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 1, @@ -690,7 +690,7 @@ describe('compiler compliance: styling', () => { const template = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors:[["my-component"]], decls: 1, @@ -738,7 +738,7 @@ describe('compiler compliance: styling', () => { const template = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ type: MyComponent, selectors:[["my-component"]], decls: 1, @@ -1620,7 +1620,7 @@ describe('compiler compliance: styling', () => { const template = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … template: function MyComponent_Template(rf, $ctx$) { … @@ -1658,7 +1658,7 @@ describe('compiler compliance: styling', () => { const template = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … template: function MyComponent_Template(rf, $ctx$) { … @@ -1703,7 +1703,7 @@ describe('compiler compliance: styling', () => { const template = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … template: function MyComponent_Template(rf, $ctx$) { … @@ -1740,7 +1740,7 @@ describe('compiler compliance: styling', () => { const template = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … template: function MyComponent_Template(rf, $ctx$) { … @@ -1779,7 +1779,7 @@ describe('compiler compliance: styling', () => { const template = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … template: function MyComponent_Template(rf, $ctx$) { … @@ -1827,7 +1827,7 @@ describe('compiler compliance: styling', () => { const template = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … template: function MyComponent_Template(rf, $ctx$) { … @@ -1872,7 +1872,7 @@ describe('compiler compliance: styling', () => { const template = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … template: function MyComponent_Template(rf, $ctx$) { … @@ -1924,7 +1924,7 @@ describe('compiler compliance: styling', () => { const template = ` … - MyComponent.ɵcmp = $r3$.ɵɵdefineComponent({ + MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ … hostBindings: function MyComponent_HostBindings(rf, $ctx$) { … diff --git a/packages/compiler-cli/test/compliance_old/r3_view_compiler_template_spec.ts b/packages/compiler-cli/test/compliance_old/r3_view_compiler_template_spec.ts index 7e1895794a..26fd0703e4 100644 --- a/packages/compiler-cli/test/compliance_old/r3_view_compiler_template_spec.ts +++ b/packages/compiler-cli/test/compliance_old/r3_view_compiler_template_spec.ts @@ -55,8 +55,8 @@ describe('compiler compliance: template', () => { const $s$ = $i0$.ɵɵgetCurrentView(); $i0$.ɵɵelementStart(0, "div", 2); $i0$.ɵɵlistener("click", function MyComponent_ul_0_li_1_div_1_Template_div_click_0_listener(){ - $i0$.ɵɵrestoreView($s$); - const $inner$ = ctx.$implicit; + const $sr$ = $i0$.ɵɵrestoreView($s$); + const $inner$ = $sr$.$implicit; const $middle$ = $i0$.ɵɵnextContext().$implicit; const $outer$ = $i0$.ɵɵnextContext().$implicit; const $myComp$ = $i0$.ɵɵnextContext(); @@ -150,9 +150,9 @@ describe('compiler compliance: template', () => { const $s$ = $r3$.ɵɵgetCurrentView(); $r3$.ɵɵelementStart(0, "div", 1); $r3$.ɵɵlistener("click", function MyComponent_div_0_Template_div_click_0_listener() { - $r3$.ɵɵrestoreView($s$); - const $d$ = ctx.$implicit; - const $i$ = ctx.index; + const $sr$ = $r3$.ɵɵrestoreView($s$); + const $d$ = $sr$.$implicit; + const $i$ = $sr$.index; const $comp$ = $r3$.ɵɵnextContext(); return $comp$._handleClick($d$, $i$); }); diff --git a/packages/compiler-cli/test/ngtsc/component_indexing_spec.ts b/packages/compiler-cli/test/ngtsc/component_indexing_spec.ts index 192de7f0a3..d6734a46e3 100644 --- a/packages/compiler-cli/test/ngtsc/component_indexing_spec.ts +++ b/packages/compiler-cli/test/ngtsc/component_indexing_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, resolve} from '@angular/compiler-cli/src/ngtsc/file_system'; +import {AbsoluteFsPath, getFileSystem, PathManipulation} from '@angular/compiler-cli/src/ngtsc/file_system'; import {runInEachFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system/testing'; import {AbsoluteSourceSpan, IdentifierKind, IndexedComponent, TopLevelIdentifier} from '@angular/compiler-cli/src/ngtsc/indexer'; import {ParseSourceFile} from '@angular/compiler/src/compiler'; @@ -14,6 +14,7 @@ import {NgtscTestEnvironment} from './env'; runInEachFileSystem(() => { describe('ngtsc component indexing', () => { + let fs: PathManipulation; let env!: NgtscTestEnvironment; let testSourceFile: AbsoluteFsPath; let testTemplateFile: AbsoluteFsPath; @@ -21,8 +22,9 @@ runInEachFileSystem(() => { beforeEach(() => { env = NgtscTestEnvironment.setup(); env.tsconfig(); - testSourceFile = resolve(env.basePath, 'test.ts'); - testTemplateFile = resolve(env.basePath, 'test.html'); + fs = getFileSystem(); + testSourceFile = fs.resolve(env.basePath, 'test.ts'); + testTemplateFile = fs.resolve(env.basePath, 'test.html'); }); describe('indexing metadata', () => { diff --git a/packages/compiler-cli/test/ngtsc/env.ts b/packages/compiler-cli/test/ngtsc/env.ts index a57eefb0f8..4c68621b65 100644 --- a/packages/compiler-cli/test/ngtsc/env.ts +++ b/packages/compiler-cli/test/ngtsc/env.ts @@ -12,13 +12,13 @@ import * as ts from 'typescript'; import {createCompilerHost, createProgram} from '../../index'; import {main, mainDiagnosticsForTest, readNgcCommandLineAndConfiguration} from '../../src/main'; -import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, NgtscCompilerHost, relativeFrom} from '../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, relativeFrom} 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'; import {DeclarationNode} from '../../src/ngtsc/reflection'; import {LazyRoute} from '../../src/ngtsc/routing'; -import {getCachedSourceFile} from '../../src/ngtsc/testing'; +import {NgtscTestCompilerHost} from '../../src/ngtsc/testing'; import {setWrapHostForTest} from '../../src/transformers/compiler_host'; @@ -72,7 +72,6 @@ export class NgtscTestEnvironment { }, "angularCompilerOptions": { "enableIvy": true, - "ivyTemplateTypeCheck": false }, "exclude": [ "built" @@ -162,7 +161,9 @@ export class NgtscTestEnvironment { const absFilePath = this.fs.resolve(this.basePath, fileName); if (this.multiCompileHostExt !== null) { this.multiCompileHostExt.invalidate(absFilePath); - this.changedResources!.add(absFilePath); + if (!fileName.endsWith('.ts')) { + this.changedResources!.add(absFilePath); + } } this.fs.ensureDir(this.fs.dirname(absFilePath)); this.fs.writeFile(absFilePath, content); @@ -174,6 +175,9 @@ export class NgtscTestEnvironment { throw new Error(`Not caching files - call enableMultipleCompilations()`); } this.multiCompileHostExt.invalidate(absFilePath); + if (!fileName.endsWith('.ts')) { + this.changedResources!.add(absFilePath); + } } tsconfig( @@ -268,16 +272,6 @@ export class NgtscTestEnvironment { } } -class NgtscTestCompilerHost extends NgtscCompilerHost { - getSourceFile(fileName: string, languageVersion: ts.ScriptTarget): ts.SourceFile|undefined { - const cachedSf = getCachedSourceFile(fileName, () => this.readFile(fileName)); - if (cachedSf !== null) { - return cachedSf; - } - return super.getSourceFile(fileName, languageVersion); - } -} - class AugmentedCompilerHost extends NgtscTestCompilerHost { delegate!: ts.CompilerHost; } diff --git a/packages/compiler-cli/test/ngtsc/incremental_error_spec.ts b/packages/compiler-cli/test/ngtsc/incremental_error_spec.ts index ef3e020e2c..0fb1bf8925 100644 --- a/packages/compiler-cli/test/ngtsc/incremental_error_spec.ts +++ b/packages/compiler-cli/test/ngtsc/incremental_error_spec.ts @@ -239,12 +239,13 @@ runInEachFileSystem(() => { export class TargetCmp {} `); env.write('module.ts', ` - import {NgModule} from '@angular/core'; + import {NgModule, NO_ERRORS_SCHEMA} from '@angular/core'; import {TargetCmp} from './target'; import {TestCmp} from './test'; @NgModule({ declarations: [TestCmp, TargetCmp], + schemas: [NO_ERRORS_SCHEMA], }) export class Module {} `); @@ -268,7 +269,7 @@ runInEachFileSystem(() => { env.write('test.ts', ` import {Component} from '@angular/core'; - @Component({selector: 'test-cmp', template: '...'}) + @Component({selector: 'test-cmp-fixed', template: '...'}) export class TestCmp {} `); @@ -283,11 +284,43 @@ runInEachFileSystem(() => { '/module.js', // Because TargetCmp also belongs to the same module, it should be re-emitted since - // TestCmp's elector may have changed. + // TestCmp's selector was changed. '/target.js', ]); }); + it('should recover from an error in an external template', () => { + env.write('mod.ts', ` + import {NgModule} from '@angular/core'; + import {Cmp} from './cmp'; + + @NgModule({ + declarations: [Cmp], + }) + export class Mod {} + `); + env.write('cmp.html', '{{ error = "true" }} '); + env.write('cmp.ts', ` + import {Component} from '@angular/core'; + + @Component({ + templateUrl: './cmp.html', + selector: 'some-cmp', + }) + export class Cmp { + error = 'false'; + } + `); + + // Diagnostics should show for the broken component template. + expect(env.driveDiagnostics().length).toBeGreaterThan(0); + + env.write('cmp.html', '{{ error }} '); + + // There should be no diagnostics. + env.driveMain(); + }); + it('should recover from an error even across multiple NgModules', () => { // This test is a variation on the above. Two components (CmpA and CmpB) exist in an NgModule, // which indirectly imports a LibModule (via another NgModule in the middle). The test is @@ -297,7 +330,7 @@ runInEachFileSystem(() => { env.write('a.ts', ` import {Component} from '@angular/core'; - @Component({selector: 'test-cmp', template: '...'}) + @Component({selector: 'test-cmp', template: '
    '}) export class CmpA {} `); env.write('b.ts', ` @@ -325,17 +358,16 @@ runInEachFileSystem(() => { export class Module {} `); env.write('lib.ts', ` - import {Component, NgModule} from '@angular/core'; + import {Directive, NgModule} from '@angular/core'; - @Component({ - selector: 'lib-cmp', - template: '...', + @Directive({ + selector: '[dir]', }) - export class LibCmp {} + export class LibDir {} @NgModule({ - declarations: [LibCmp], - exports: [LibCmp], + declarations: [LibDir], + exports: [LibDir], }) export class LibModule {} `); @@ -346,17 +378,27 @@ runInEachFileSystem(() => { // Introduce the error in LibModule env.write('lib.ts', ` - import {Component, NgModule} from '@angular/core'; + import {Directive, NgModule} from '@angular/core'; - @Component({ - selector: 'lib-cmp', - template: '...', + @Directive({ + selector: '[dir]', }) - export class LibCmp {} + export class LibDir {} + + @Directive({ + selector: '[dir]', + }) + export class NewDir {} @NgModule({ - declarations: [LibCmp], - exports: [LibCmp], + declarations: [NewDir], + }) + export class NewModule {} + + @NgModule({ + declarations: [LibDir], + imports: [NewModule], + exports: [LibDir, NewModule], }) export class LibModule // missing braces `); @@ -375,9 +417,13 @@ runInEachFileSystem(() => { }) export class LibCmp {} + @NgModule({}) + export class NewModule {} + @NgModule({ declarations: [LibCmp], - exports: [LibCmp], + imports: [NewModule], + exports: [LibCmp, NewModule], }) export class LibModule {} `); @@ -385,9 +431,10 @@ runInEachFileSystem(() => { env.driveMain(); expectToHaveWritten([ - // Both CmpA and CmpB should be re-emitted. + // CmpA should be re-emitted as `NewModule` was added since the successful emit, which added + // `NewDir` as a matching directive to CmpA. Alternatively, CmpB should not be re-emitted + // as it does not use the newly added directive. '/a.js', - '/b.js', // So should the module itself. '/module.js', @@ -407,11 +454,11 @@ runInEachFileSystem(() => { env.driveMain(); env.flushWrittenFileTracking(); - // Update ACmp to have a different selector, isn't matched in BCmp's template. + // Update ACmp env.write('a.ts', ` import {Component} from '@angular/core'; - @Component({selector: 'not-a-cmp', template: '...'}) + @Component({selector: 'a-cmp', template: 'new template'}) export class ACmp {} `); @@ -436,8 +483,7 @@ runInEachFileSystem(() => { '/other.js', '/a.js', - // Bcause they depend on a.ts - '/b.js', + // Because they depend on a.ts '/module.js', ]); }); @@ -480,7 +526,10 @@ runInEachFileSystem(() => { '/other.js', // Because a.html changed - '/a.js', '/module.js', + '/a.js', + + // module.js should not be re-emitted, as it is not affected by the change and its remote + // scope is unaffected. // b.js and module.js should not be re-emitted, because specifically when tracking // resource dependencies, the compiler knows that a change to a resource file only affects diff --git a/packages/compiler-cli/test/ngtsc/incremental_spec.ts b/packages/compiler-cli/test/ngtsc/incremental_spec.ts index 757491bd2e..06c65d53cb 100644 --- a/packages/compiler-cli/test/ngtsc/incremental_spec.ts +++ b/packages/compiler-cli/test/ngtsc/incremental_spec.ts @@ -154,14 +154,20 @@ runInEachFileSystem(() => { setupFooBarProgram(env); // Pretend a change was made to BarDir. - env.invalidateCachedFile('bar_directive.ts'); + env.write('bar_directive.ts', ` + import {Directive} from '@angular/core'; + + @Directive({selector: '[barr]'}) + export class BarDir {} + `); env.driveMain(); let written = env.getFilesWrittenSinceLastFlush(); expect(written).toContain('/bar_directive.js'); expect(written).toContain('/bar_component.js'); expect(written).toContain('/bar_module.js'); - expect(written).toContain('/foo_component.js'); + expect(written).not.toContain('/foo_component.js'); // BarDir is not exported by BarModule, + // so upstream NgModule is not affected expect(written).not.toContain('/foo_pipe.js'); expect(written).not.toContain('/foo_module.js'); }); @@ -178,7 +184,7 @@ runInEachFileSystem(() => { env.write('component2.ts', ` import {Component} from '@angular/core'; - @Component({selector: 'cmp2', template: 'cmp2'}) + @Component({selector: 'cmp2', template: ''}) export class Cmp2 {} `); env.write('dep.ts', ` @@ -197,36 +203,43 @@ runInEachFileSystem(() => { export class MyPipe {} `); env.write('module.ts', ` - import {NgModule} from '@angular/core'; + import {NgModule, NO_ERRORS_SCHEMA} from '@angular/core'; import {Cmp1} from './component1'; import {Cmp2} from './component2'; import {Dir} from './directive'; import {MyPipe} from './pipe'; - @NgModule({declarations: [Cmp1, Cmp2, Dir, MyPipe]}) + @NgModule({declarations: [Cmp1, Cmp2, Dir, MyPipe], schemas: [NO_ERRORS_SCHEMA]}) export class Mod {} `); env.driveMain(); - // Pretend a change was made to 'dep'. Since this may affect the NgModule scope, like it does - // here if the selector is updated, all components in the module scope need to be recompiled. + // Pretend a change was made to 'dep'. Since the selector is updated this affects the NgModule + // scope, so all components in the module scope need to be recompiled. env.flushWrittenFileTracking(); - env.invalidateCachedFile('dep.ts'); + env.write('dep.ts', ` + export const SELECTOR = 'cmp_updated'; + `); env.driveMain(); const written = env.getFilesWrittenSinceLastFlush(); expect(written).not.toContain('/directive.js'); expect(written).not.toContain('/pipe.js'); + expect(written).not.toContain('/module.js'); expect(written).toContain('/component1.js'); expect(written).toContain('/component2.js'); expect(written).toContain('/dep.js'); - expect(written).toContain('/module.js'); }); it('should rebuild components where their NgModule declared dependencies have changed', () => { setupFooBarProgram(env); - // Pretend a change was made to FooPipe. - env.invalidateCachedFile('foo_pipe.ts'); + // Rename the pipe so components that use it need to be recompiled. + env.write('foo_pipe.ts', ` + import {Pipe} from '@angular/core'; + + @Pipe({name: 'foo_changed'}) + export class FooPipe {} + `); env.driveMain(); const written = env.getFilesWrittenSinceLastFlush(); expect(written).not.toContain('/bar_directive.js'); @@ -240,15 +253,25 @@ runInEachFileSystem(() => { it('should rebuild components where their NgModule has changed', () => { setupFooBarProgram(env); - // Pretend a change was made to FooPipe. - env.invalidateCachedFile('foo_module.ts'); + // Pretend a change was made to FooModule. + env.write('foo_module.ts', ` + import {NgModule} from '@angular/core'; + import {FooCmp} from './foo_component'; + import {FooPipe} from './foo_pipe'; + import {BarModule} from './bar_module'; + @NgModule({ + declarations: [FooCmp], // removed FooPipe + imports: [BarModule], + }) + export class FooModule {} + `); env.driveMain(); const written = env.getFilesWrittenSinceLastFlush(); expect(written).not.toContain('/bar_directive.js'); expect(written).not.toContain('/bar_component.js'); expect(written).not.toContain('/bar_module.js'); + expect(written).not.toContain('/foo_pipe.js'); expect(written).toContain('/foo_component.js'); - expect(written).toContain('/foo_pipe.js'); expect(written).toContain('/foo_module.js'); }); @@ -396,7 +419,7 @@ runInEachFileSystem(() => { expect(env.getContents('cmp.js')).not.toContain('DepDir'); }); - it('should rebuild only a Component (but with the correct CompilationScope) and its module if its template has changed', + it('should rebuild only a Component (but with the correct CompilationScope) if its template has changed', () => { setupFooBarProgram(env); @@ -407,9 +430,7 @@ runInEachFileSystem(() => { const written = env.getFilesWrittenSinceLastFlush(); expect(written).not.toContain('/bar_directive.js'); expect(written).toContain('/bar_component.js'); - // /bar_module.js should also be re-emitted, because remote scoping of BarComponent might - // have been affected. - expect(written).toContain('/bar_module.js'); + expect(written).not.toContain('/bar_module.js'); expect(written).not.toContain('/foo_component.js'); expect(written).not.toContain('/foo_pipe.js'); expect(written).not.toContain('/foo_module.js'); @@ -748,12 +769,14 @@ runInEachFileSystem(() => { let diags = env.driveDiagnostics(); expect(diags.length).toBe(1); - expect(diags[0].messageText).toContain('"alpha" | "beta"'); + expect(diags[0].messageText) + .toContain(`Type '"gamma"' is not assignable to type 'keyof Keys'.`); // On a rebuild, the same errors should be present. diags = env.driveDiagnostics(); expect(diags.length).toBe(1); - expect(diags[0].messageText).toContain('"alpha" | "beta"'); + expect(diags[0].messageText) + .toContain(`Type '"gamma"' is not assignable to type 'keyof Keys'.`); }); }); }); @@ -764,7 +787,10 @@ runInEachFileSystem(() => { import {Component} from '@angular/core'; import {fooSelector} from './foo_selector'; - @Component({selector: fooSelector, template: 'foo'}) + @Component({ + selector: fooSelector, + template: '{{ 1 | foo }}' + }) export class FooCmp {} `); env.write('foo_pipe.ts', ` @@ -796,14 +822,21 @@ runInEachFileSystem(() => { @Directive({selector: '[bar]'}) export class BarDir {} + `); + env.write('bar_pipe.ts', ` + import {Pipe} from '@angular/core'; + + @Pipe({name: 'foo'}) + export class BarPipe {} `); env.write('bar_module.ts', ` import {NgModule} from '@angular/core'; import {BarCmp} from './bar_component'; import {BarDir} from './bar_directive'; + import {BarPipe} from './bar_pipe'; @NgModule({ - declarations: [BarCmp, BarDir], - exports: [BarCmp], + declarations: [BarCmp, BarDir, BarPipe], + exports: [BarCmp, BarPipe], }) export class BarModule {} `); diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index ec5a66191a..34da420eff 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -22,14 +22,14 @@ const trim = (input: string): string => input.replace(/\s+/g, ' ').trim(); const varRegExp = (name: string): RegExp => new RegExp(`var \\w+ = \\[\"${name}\"\\];`); -const viewQueryRegExp = (predicate: string, descend: boolean, ref?: string): RegExp => { +const viewQueryRegExp = (predicate: string, flags: number, ref?: string): RegExp => { const maybeRef = ref ? `, ${ref}` : ``; - return new RegExp(`i0\\.ɵɵviewQuery\\(${predicate}, ${descend}${maybeRef}\\)`); + return new RegExp(`i0\\.ɵɵviewQuery\\(${predicate}, ${flags}${maybeRef}\\)`); }; -const contentQueryRegExp = (predicate: string, descend: boolean, ref?: string): RegExp => { +const contentQueryRegExp = (predicate: string, flags: number, ref?: string): RegExp => { const maybeRef = ref ? `, ${ref}` : ``; - return new RegExp(`i0\\.ɵɵcontentQuery\\(dirIndex, ${predicate}, ${descend}${maybeRef}\\)`); + return new RegExp(`i0\\.ɵɵcontentQuery\\(dirIndex, ${predicate}, ${flags}${maybeRef}\\)`); }; const setClassMetadataRegExp = (expectedType: string): RegExp => @@ -41,7 +41,11 @@ function getDiagnosticSourceCode(diag: ts.Diagnostic): string { return diag.file!.text.substr(diag.start!, diag.length!); } -runInEachFileSystem(os => { +runInEachFileSystem(allTests); + +// Wrap all tests into a function to work around clang-format going crazy and (poorly) +// reformatting the entire file. +function allTests(os: string) { describe('ngtsc behavioral tests', () => { let env!: NgtscTestEnvironment; @@ -72,8 +76,8 @@ runInEachFileSystem(os => { const dtsContents = env.getContents('test.d.ts'); expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef;'); expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration;'); }); it('should compile Injectables with a generic service', () => { @@ -90,7 +94,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, never>;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration, never>;'); expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef>;'); }); @@ -121,8 +125,8 @@ runInEachFileSystem(os => { const dtsContents = env.getContents('test.d.ts'); expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef;'); expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration;'); }); it('should compile Injectables with providedIn and factory without errors', () => { @@ -147,7 +151,7 @@ runInEachFileSystem(os => { expect(jsContents).not.toContain('__decorate'); const dtsContents = env.getContents('test.d.ts'); expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration;'); }); it('should compile Injectables with providedIn and factory with deps without errors', () => { @@ -176,7 +180,7 @@ runInEachFileSystem(os => { expect(jsContents).not.toContain('__decorate'); const dtsContents = env.getContents('test.d.ts'); expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration;'); }); it('should compile @Injectable with an @Optional dependency', () => { @@ -233,15 +237,15 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('TestDir.ɵdir = i0.ɵɵdefineDirective'); + expect(jsContents).toContain('TestDir.ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective'); expect(jsContents).toContain('TestDir.ɵfac = function'); expect(jsContents).not.toContain('__decorate'); const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'static ɵdir: i0.ɵɵDirectiveDefWithMeta'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef'); + 'static ɵdir: i0.ɵɵDirectiveDeclaration'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration'); }); it('should compile abstract Directives without errors', () => { @@ -255,15 +259,15 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('TestDir.ɵdir = i0.ɵɵdefineDirective'); + expect(jsContents).toContain('TestDir.ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective'); expect(jsContents).toContain('TestDir.ɵfac = function'); expect(jsContents).not.toContain('__decorate'); const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'static ɵdir: i0.ɵɵDirectiveDefWithMeta'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef'); + 'static ɵdir: i0.ɵɵDirectiveDeclaration'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration'); }); it('should compile Components (inline template) without errors', () => { @@ -280,15 +284,15 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('TestCmp.ɵcmp = i0.ɵɵdefineComponent'); + expect(jsContents).toContain('TestCmp.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent'); expect(jsContents).toContain('TestCmp.ɵfac = function'); expect(jsContents).not.toContain('__decorate'); const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'static ɵcmp: i0.ɵɵComponentDefWithMeta'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef'); + 'static ɵcmp: i0.ɵɵComponentDeclaration'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration'); }); it('should compile Components (dynamic inline template) without errors', () => { @@ -305,7 +309,7 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('TestCmp.ɵcmp = i0.ɵɵdefineComponent'); + expect(jsContents).toContain('TestCmp.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent'); expect(jsContents).toContain('TestCmp.ɵfac = function'); expect(jsContents).not.toContain('__decorate'); @@ -313,9 +317,9 @@ runInEachFileSystem(os => { expect(dtsContents) .toContain( - 'static ɵcmp: i0.ɵɵComponentDefWithMeta' + + 'static ɵcmp: i0.ɵɵComponentDeclaration' + ''); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration'); }); it('should compile Components (function call inline template) without errors', () => { @@ -335,15 +339,15 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('TestCmp.ɵcmp = i0.ɵɵdefineComponent'); + expect(jsContents).toContain('TestCmp.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent'); expect(jsContents).toContain('TestCmp.ɵfac = function'); expect(jsContents).not.toContain('__decorate'); const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'static ɵcmp: i0.ɵɵComponentDefWithMeta'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef'); + 'static ɵcmp: i0.ɵɵComponentDeclaration'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration'); }); it('should compile Components (external template) without errors', () => { @@ -364,11 +368,63 @@ runInEachFileSystem(os => { expect(jsContents).toContain('Hello World'); }); + it('should not report that broken components in modules are not components', () => { + env.write('test.ts', ` + import {Component, NgModule} from '@angular/core'; + + @Component({ + selector: 'broken-cmp', + template: '{{ broken = "true" }}', // assignment not legal in this context + }) + export class BrokenCmp {} + + @NgModule({ + declarations: [BrokenCmp], + }) + export class Module { + broken = "false"; + } + `); + + const diags = env.driveDiagnostics(); + if (diags.some(diag => diag.code === ngErrorCode(ErrorCode.NGMODULE_INVALID_DECLARATION))) { + fail('Should not produce a diagnostic that BrokenCmp is not a component'); + } + }); + // This test triggers the Tsickle compiler which asserts that the file-paths // are valid for the real OS. When on non-Windows systems it doesn't like paths // that start with `C:`. if (os !== 'Windows' || platform() === 'win32') { describe('when closure annotations are requested', () => { + it('should add @pureOrBreakMyCode to getInheritedFactory calls', () => { + env.tsconfig({ + 'annotateForClosureCompiler': true, + }); + env.write(`test.ts`, ` + import {Directive} from '@angular/core'; + + @Directive({ + selector: '[base]', + }) + class Base {} + + @Directive({ + selector: '[test]', + }) + class Dir extends Base { + } + `); + + env.driveMain(); + + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('Dir.ɵfac = /** @pureOrBreakMyCode */ function () {'); + expect(jsContents) + .toContain( + '(ɵDir_BaseFactory || (ɵDir_BaseFactory = i0.ɵɵgetInheritedFactory(Dir)))(t || Dir);'); + }); + it('should add @nocollapse to static fields', () => { env.tsconfig({ 'annotateForClosureCompiler': true, @@ -394,7 +450,6 @@ runInEachFileSystem(os => { env.tsconfig({ 'fullTemplateTypeCheck': false, 'annotateForClosureCompiler': true, - 'ivyTemplateTypeCheck': true, }); env.write('test.ts', ` import {Component, Directive, NgModule} from '@angular/core'; @@ -629,13 +684,13 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('TestBase.ɵdir = i0.ɵɵdefineDirective'); - expect(jsContents).toContain('TestComponent.ɵcmp = i0.ɵɵdefineComponent'); - expect(jsContents).toContain('TestDirective.ɵdir = i0.ɵɵdefineDirective'); - expect(jsContents).toContain('TestPipe.ɵpipe = i0.ɵɵdefinePipe'); - expect(jsContents).toContain('TestInjectable.ɵprov = i0.ɵɵdefineInjectable'); - expect(jsContents).toContain('MyModule.ɵmod = i0.ɵɵdefineNgModule'); - expect(jsContents).toContain('MyModule.ɵinj = i0.ɵɵdefineInjector'); + expect(jsContents).toContain('TestBase.ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective'); + expect(jsContents).toContain('TestComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent'); + expect(jsContents).toContain('TestDirective.ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective'); + expect(jsContents).toContain('TestPipe.ɵpipe = /*@__PURE__*/ i0.ɵɵdefinePipe'); + expect(jsContents).toContain('TestInjectable.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable'); + expect(jsContents).toContain('MyModule.ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule'); + expect(jsContents).toContain('MyModule.ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector'); expect(jsContents).toContain('inputs: { input: "input" }'); expect(jsContents).toContain('outputs: { output: "output" }'); }); @@ -665,7 +720,9 @@ runInEachFileSystem(os => { selector: 'app', template: '{{ count | number }}' }) - export class App {} + export class App { + count = 0; + } @NgModule({ imports: [ModuleA], @@ -714,7 +771,9 @@ runInEachFileSystem(os => { selector: 'app', template: '{{ count | number }}' }) - export class App {} + export class App { + count = 0; + } @NgModule({ imports: [ModuleA, ModuleB], @@ -942,7 +1001,7 @@ runInEachFileSystem(os => { const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - `static ɵdir: i0.ɵɵDirectiveDefWithMeta;`); + `static ɵdir: i0.ɵɵDirectiveDeclaration;`); }); describe('undecorated classes using Angular features', () => { @@ -1042,16 +1101,16 @@ runInEachFileSystem(os => { 'function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(TestModule, { declarations: [TestCmp] }); })();'); expect(jsContents) .toContain( - 'i0.ɵɵdefineInjector({ factory: ' + - 'function TestModule_Factory(t) { return new (t || TestModule)(); } });'); + 'TestModule.ɵfac = function TestModule_Factory(t) { return new (t || TestModule)(); }'); + expect(jsContents).toContain('i0.ɵɵdefineInjector({});'); const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'static ɵcmp: i0.ɵɵComponentDefWithMeta'); + 'static ɵcmp: i0.ɵɵComponentDeclaration'); expect(dtsContents) .toContain( - 'static ɵmod: i0.ɵɵNgModuleDefWithMeta'); + 'static ɵmod: i0.ɵɵNgModuleDeclaration'); expect(dtsContents).not.toContain('__decorate'); }); @@ -1138,15 +1197,15 @@ runInEachFileSystem(os => { export class Comp {} `); env.write('node_modules/@angular/router/index.d.ts', ` - import {ɵɵComponentDefWithMeta, ModuleWithProviders, ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ɵɵComponentDeclaration, ModuleWithProviders, ɵɵNgModuleDeclaration} from '@angular/core'; export declare class RouterComp { - static ɵcmp: ɵɵComponentDefWithMeta + static ɵcmp: ɵɵComponentDeclaration } declare class RouterModule { static forRoot(): ModuleWithProviders; - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); @@ -1155,8 +1214,10 @@ runInEachFileSystem(os => { const jsContents = env.getContents('test.js'); expect(jsContents) .toContain( - 'i0.ɵɵdefineInjector({ factory: function TestModule_Factory(t) ' + - '{ return new (t || TestModule)(); }, imports: [[OtherModule, RouterModule.forRoot()],' + + 'TestModule.ɵfac = function TestModule_Factory(t) { return new (t || TestModule)(); }'); + expect(jsContents) + .toContain( + 'i0.ɵɵdefineInjector({ imports: [[OtherModule, RouterModule.forRoot()],' + ' OtherModule, RouterModule] });'); }); @@ -1186,18 +1247,21 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); + expect(jsContents) + .toContain( + 'TestModule.ɵfac = function TestModule_Factory(t) { return new (t || TestModule)(); }'); expect(jsContents).toContain('i0.ɵɵdefineNgModule({ type: TestModule });'); expect(jsContents) .toContain( - `TestModule.ɵinj = i0.ɵɵdefineInjector({ factory: ` + - `function TestModule_Factory(t) { return new (t || TestModule)(); }, providers: [{ provide: ` + - `Token, useValue: 'test' }], imports: [[OtherModule]] });`); + `TestModule.ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ ` + + `providers: [{ provide: Token, useValue: 'test' }], ` + + `imports: [[OtherModule]] });`); const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'static ɵmod: i0.ɵɵNgModuleDefWithMeta'); - expect(dtsContents).toContain('static ɵinj: i0.ɵɵInjectorDef'); + 'static ɵmod: i0.ɵɵNgModuleDeclaration'); + expect(dtsContents).toContain('static ɵinj: i0.ɵɵInjectorDeclaration'); }); it('should compile NgModules with factory providers without errors', () => { @@ -1226,18 +1290,21 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); + expect(jsContents) + .toContain( + 'TestModule.ɵfac = function TestModule_Factory(t) { return new (t || TestModule)(); }'); expect(jsContents).toContain('i0.ɵɵdefineNgModule({ type: TestModule });'); expect(jsContents) .toContain( - `TestModule.ɵinj = i0.ɵɵdefineInjector({ factory: ` + - `function TestModule_Factory(t) { return new (t || TestModule)(); }, providers: [{ provide: ` + - `Token, useFactory: function () { return new Token(); } }], imports: [[OtherModule]] });`); + `TestModule.ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ ` + + `providers: [{ provide: Token, useFactory: function () { return new Token(); } }], ` + + `imports: [[OtherModule]] });`); const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'static ɵmod: i0.ɵɵNgModuleDefWithMeta'); - expect(dtsContents).toContain('static ɵinj: i0.ɵɵInjectorDef'); + 'static ɵmod: i0.ɵɵNgModuleDeclaration'); + expect(dtsContents).toContain('static ɵinj: i0.ɵɵInjectorDeclaration'); }); it('should compile NgModules with factory providers and deps without errors', () => { @@ -1270,18 +1337,21 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); + expect(jsContents) + .toContain( + 'TestModule.ɵfac = function TestModule_Factory(t) { return new (t || TestModule)(); }'); expect(jsContents).toContain('i0.ɵɵdefineNgModule({ type: TestModule });'); expect(jsContents) .toContain( - `TestModule.ɵinj = i0.ɵɵdefineInjector({ factory: ` + - `function TestModule_Factory(t) { return new (t || TestModule)(); }, providers: [{ provide: ` + - `Token, useFactory: function (dep) { return new Token(dep); }, deps: [Dep] }], imports: [[OtherModule]] });`); + `TestModule.ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ ` + + `providers: [{ provide: Token, useFactory: function (dep) { return new Token(dep); }, deps: [Dep] }], ` + + `imports: [[OtherModule]] });`); const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'static ɵmod: i0.ɵɵNgModuleDefWithMeta'); - expect(dtsContents).toContain('static ɵinj: i0.ɵɵInjectorDef'); + 'static ɵmod: i0.ɵɵNgModuleDeclaration'); + expect(dtsContents).toContain('static ɵinj: i0.ɵɵInjectorDeclaration'); }); it('should compile NgModules with references to local components', () => { @@ -1311,6 +1381,15 @@ runInEachFileSystem(os => { }); it('should compile NgModules with references to absolute components', () => { + env.write('tsconfig.json', JSON.stringify({ + extends: './tsconfig-base.json', + compilerOptions: { + baseUrl: '.', + paths: { + '*': ['*', 'shared/*'], + }, + }, + })); env.write('test.ts', ` import {NgModule} from '@angular/core'; import {Foo} from 'foo'; @@ -1320,7 +1399,7 @@ runInEachFileSystem(os => { }) export class FooModule {} `); - env.write('node_modules/foo/index.ts', ` + env.write('shared/foo/index.ts', ` import {Component} from '@angular/core'; @Component({ @@ -1435,12 +1514,12 @@ runInEachFileSystem(os => { expect(jsContents) .toContain( - 'TestPipe.ɵpipe = i0.ɵɵdefinePipe({ name: "test-pipe", type: TestPipe, pure: false })'); + 'TestPipe.ɵpipe = /*@__PURE__*/ i0.ɵɵdefinePipe({ name: "test-pipe", type: TestPipe, pure: false })'); expect(jsContents) .toContain( 'TestPipe.ɵfac = function TestPipe_Factory(t) { return new (t || TestPipe)(); }'); - expect(dtsContents).toContain('static ɵpipe: i0.ɵɵPipeDefWithMeta;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef;'); + expect(dtsContents).toContain('static ɵpipe: i0.ɵɵPipeDeclaration;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration;'); }); it('should compile pure Pipes without errors', () => { @@ -1460,12 +1539,12 @@ runInEachFileSystem(os => { expect(jsContents) .toContain( - 'TestPipe.ɵpipe = i0.ɵɵdefinePipe({ name: "test-pipe", type: TestPipe, pure: true })'); + 'TestPipe.ɵpipe = /*@__PURE__*/ i0.ɵɵdefinePipe({ name: "test-pipe", type: TestPipe, pure: true })'); expect(jsContents) .toContain( 'TestPipe.ɵfac = function TestPipe_Factory(t) { return new (t || TestPipe)(); }'); - expect(dtsContents).toContain('static ɵpipe: i0.ɵɵPipeDefWithMeta;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef;'); + expect(dtsContents).toContain('static ɵpipe: i0.ɵɵPipeDeclaration;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration;'); }); it('should compile Pipes with dependencies', () => { @@ -1486,7 +1565,7 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('return new (t || TestPipe)(i0.ɵɵdirectiveInject(Dep));'); + expect(jsContents).toContain('return new (t || TestPipe)(i0.ɵɵdirectiveInject(Dep, 16));'); }); it('should compile Pipes with generic types', () => { @@ -1505,8 +1584,8 @@ runInEachFileSystem(os => { expect(jsContents).toContain('TestPipe.ɵpipe ='); const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) - .toContain('static ɵpipe: i0.ɵɵPipeDefWithMeta, "test-pipe">;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef, never>;'); + .toContain('static ɵpipe: i0.ɵɵPipeDeclaration, "test-pipe">;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration, never>;'); }); it('should include @Pipes in @NgModule scopes', () => { @@ -1517,7 +1596,9 @@ runInEachFileSystem(os => { export class TestPipe {} @Component({selector: 'test-cmp', template: '{{value | test}}'}) - export class TestCmp {} + export class TestCmp { + value = ''; + } @NgModule({declarations: [TestPipe, TestCmp]}) export class TestModule {} @@ -1531,7 +1612,7 @@ runInEachFileSystem(os => { const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'i0.ɵɵNgModuleDefWithMeta'); + 'i0.ɵɵNgModuleDeclaration'); }); describe('empty and missing selectors', () => { @@ -2052,10 +2133,10 @@ runInEachFileSystem(os => { expect(jsContents).toContain('TestNgModule.ɵprov ='); // Validate that each class's .d.ts declaration has the primary definition. - expect(dtsContents).toContain('ComponentDefWithMeta { }); it('should report an error when using a missing type as injection token', () => { - // This test replicates the situation where a symbol does not have any declarations at - // all, e.g. because it's imported from a missing module. This would result in a - // semantic TypeScript diagnostic which we ignore in this test to verify that ngtsc's - // analysis is able to operate in this situation. + // This test replicates the situation where a symbol does not have any + // declarations at all, e.g. because it's imported from a missing module. This + // would result in a semantic TypeScript diagnostic which we ignore in this + // test to verify that ngtsc's analysis is able to operate in this situation. env.tsconfig({strictInjectionParameters: true}); env.write(`test.ts`, ` import {Injectable} from '@angular/core'; @@ -2630,7 +2711,7 @@ runInEachFileSystem(os => { expect(dtsContents).toContain(`import * as i1 from "./module";`); expect(dtsContents) .toContain( - 'i0.ɵɵNgModuleDefWithMeta'); + 'i0.ɵɵNgModuleDeclaration'); }); it('should extract the generic type and include it in the module\'s declaration', () => { @@ -2643,11 +2724,11 @@ runInEachFileSystem(os => { `); env.write('node_modules/router/index.d.ts', ` - import {ModuleWithProviders, ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ModuleWithProviders, ɵɵNgModuleDeclaration} from '@angular/core'; declare class RouterModule { static forRoot(): ModuleWithProviders; - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); @@ -2660,7 +2741,7 @@ runInEachFileSystem(os => { expect(dtsContents).toContain(`import * as i1 from "router";`); expect(dtsContents) .toContain( - 'i0.ɵɵNgModuleDefWithMeta'); + 'i0.ɵɵNgModuleDeclaration'); }); it('should throw if ModuleWithProviders is missing its generic type argument', () => { @@ -2673,11 +2754,11 @@ runInEachFileSystem(os => { `); env.write('node_modules/router/index.d.ts', ` - import {ModuleWithProviders, ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ModuleWithProviders, ɵɵNgModuleDeclaration} from '@angular/core'; declare class RouterModule { static forRoot(): ModuleWithProviders; - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); const errors = env.driveDiagnostics(); @@ -2709,9 +2790,9 @@ runInEachFileSystem(os => { `); env.write('node_modules/router/internal.d.ts', ` - import {ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ɵɵNgModuleDeclaration} from '@angular/core'; export declare class InternalRouterModule { - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); @@ -2724,7 +2805,7 @@ runInEachFileSystem(os => { expect(dtsContents).toContain(`import * as i1 from "router";`); expect(dtsContents) .toContain( - 'i0.ɵɵNgModuleDefWithMeta'); + 'i0.ɵɵNgModuleDeclaration'); }); it('should extract the generic type if it is provided as qualified type name from another package', @@ -2745,9 +2826,9 @@ runInEachFileSystem(os => { }`); env.write('node_modules/router2/index.d.ts', ` - import {ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ɵɵNgModuleDeclaration} from '@angular/core'; export declare class Router2Module { - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; }`); env.driveMain(); @@ -2759,17 +2840,17 @@ runInEachFileSystem(os => { expect(dtsContents).toContain(`import * as i1 from "router2";`); expect(dtsContents) .toContain( - 'i0.ɵɵNgModuleDefWithMeta'); + 'i0.ɵɵNgModuleDeclaration'); }); it('should not reference a constant with a ModuleWithProviders value in module def imports', () => { env.write('dep.d.ts', ` - import {ModuleWithProviders, ɵɵNgModuleDefWithMeta as ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ModuleWithProviders, ɵɵNgModuleDeclaration as ɵɵNgModuleDeclaration} from '@angular/core'; export declare class DepModule { static forRoot(arg1: any, arg2: any): ModuleWithProviders; - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); env.write('test.ts', ` @@ -2803,13 +2884,13 @@ runInEachFileSystem(os => { `); env.write('node_modules/router/index.d.ts', ` - import {ModuleWithProviders, ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ModuleWithProviders, ɵɵNgModuleDeclaration} from '@angular/core'; export interface MyType extends ModuleWithProviders {} declare class RouterModule { static forRoot(): (MyType)&{ngModule:RouterModule}; - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); @@ -2822,7 +2903,7 @@ runInEachFileSystem(os => { expect(dtsContents).toContain(`import * as i1 from "router";`); expect(dtsContents) .toContain( - 'i0.ɵɵNgModuleDefWithMeta'); + 'i0.ɵɵNgModuleDeclaration'); }); it('should unwrap a namespace imported ModuleWithProviders function if a generic type is provided for it', @@ -2841,7 +2922,7 @@ runInEachFileSystem(os => { declare class RouterModule { static forRoot(): core.ModuleWithProviders; - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); @@ -2854,7 +2935,7 @@ runInEachFileSystem(os => { expect(dtsContents).toContain(`import * as i1 from "router";`); expect(dtsContents) .toContain( - 'i0.ɵɵNgModuleDefWithMeta'); + 'i0.ɵɵNgModuleDeclaration'); }); it('should inject special types according to the metadata', () => { @@ -2942,15 +3023,18 @@ runInEachFileSystem(os => { const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'static ɵfac: i0.ɵɵFactoryDef'); - expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef`); - expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef`); - expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef`); - expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef`); - expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef`); + expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDeclaration`); + expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDeclaration`); + expect(dtsContents) + .toContain(`static ɵfac: i0.ɵɵFactoryDeclaration`); + expect(dtsContents) + .toContain(`static ɵfac: i0.ɵɵFactoryDeclaration`); + expect(dtsContents) + .toContain(`static ɵfac: i0.ɵɵFactoryDeclaration`); }); it('should include constructor dependency metadata for @Injectable', () => { @@ -2997,17 +3081,21 @@ runInEachFileSystem(os => { env.driveMain(); const dtsContents = env.getContents('test.d.ts'); - expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef`); + expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDeclaration`); expect(dtsContents) - .toContain(`static ɵfac: i0.ɵɵFactoryDef`); - expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef`); + .toContain(`static ɵfac: i0.ɵɵFactoryDeclaration`); expect(dtsContents) - .toContain(`static ɵfac: i0.ɵɵFactoryDef`); + .toContain(`static ɵfac: i0.ɵɵFactoryDeclaration`); expect(dtsContents) - .toContain(`static ɵfac: i0.ɵɵFactoryDef`); + .toContain( + `static ɵfac: i0.ɵɵFactoryDeclaration`); expect(dtsContents) - .toContain(`static ɵfac: i0.ɵɵFactoryDef`); - expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef`); + .toContain(`static ɵfac: i0.ɵɵFactoryDeclaration`); + expect(dtsContents) + .toContain( + `static ɵfac: i0.ɵɵFactoryDeclaration`); + expect(dtsContents) + .toContain(`static ɵfac: i0.ɵɵFactoryDeclaration`); }); it('should include ng-content selectors in the metadata', () => { @@ -3026,7 +3114,7 @@ runInEachFileSystem(os => { const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'static ɵcmp: i0.ɵɵComponentDefWithMeta'); + 'static ɵcmp: i0.ɵɵComponentDeclaration'); }); it('should generate queries for components', () => { @@ -3055,10 +3143,10 @@ runInEachFileSystem(os => { expect(jsContents).toMatch(varRegExp('test1')); expect(jsContents).toMatch(varRegExp('test2')); expect(jsContents).toMatch(varRegExp('accessor')); - // match `i0.ɵɵcontentQuery(dirIndex, _c1, true, TemplateRef)` - expect(jsContents).toMatch(contentQueryRegExp('\\w+', true, 'TemplateRef')); - // match `i0.ɵɵviewQuery(_c2, true, null)` - expect(jsContents).toMatch(viewQueryRegExp('\\w+', true)); + // match `i0.ɵɵcontentQuery(dirIndex, _c1, 5, TemplateRef)` + expect(jsContents).toMatch(contentQueryRegExp('\\w+', 5, 'TemplateRef')); + // match `i0.ɵɵviewQuery(_c2, 5, null)` + expect(jsContents).toMatch(viewQueryRegExp('\\w+', 5)); }); it('should generate queries for directives', () => { @@ -3087,14 +3175,14 @@ runInEachFileSystem(os => { expect(jsContents).toMatch(varRegExp('test1')); expect(jsContents).toMatch(varRegExp('test2')); expect(jsContents).toMatch(varRegExp('accessor')); - // match `i0.ɵɵcontentQuery(dirIndex, _c1, true, TemplateRef)` - expect(jsContents).toMatch(contentQueryRegExp('\\w+', true, 'TemplateRef')); + // match `i0.ɵɵcontentQuery(dirIndex, _c1, 5, TemplateRef)` + expect(jsContents).toMatch(contentQueryRegExp('\\w+', 5, 'TemplateRef')); - // match `i0.ɵɵviewQuery(_c2, true)` + // match `i0.ɵɵviewQuery(_c2, 5)` // Note that while ViewQuery doesn't necessarily make sense on a directive, // because it doesn't have a view, we still need to handle it because a component // could extend the directive. - expect(jsContents).toMatch(viewQueryRegExp('\\w+', true)); + expect(jsContents).toMatch(viewQueryRegExp('\\w+', 5)); }); it('should handle queries that use forwardRef', () => { @@ -3116,13 +3204,13 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); - // match `i0.ɵɵcontentQuery(dirIndex, TemplateRef, true, null)` - expect(jsContents).toMatch(contentQueryRegExp('TemplateRef', true)); - // match `i0.ɵɵcontentQuery(dirIndex, ViewContainerRef, true, null)` - expect(jsContents).toMatch(contentQueryRegExp('ViewContainerRef', true)); - // match `i0.ɵɵcontentQuery(dirIndex, _c0, true, null)` + // match `i0.ɵɵcontentQuery(dirIndex, TemplateRef, 5, null)` + expect(jsContents).toMatch(contentQueryRegExp('TemplateRef', 5)); + // match `i0.ɵɵcontentQuery(dirIndex, ViewContainerRef, 5, null)` + expect(jsContents).toMatch(contentQueryRegExp('ViewContainerRef', 5)); + // match `i0.ɵɵcontentQuery(dirIndex, _c0, 5, null)` expect(jsContents).toContain('_c0 = ["parens"];'); - expect(jsContents).toMatch(contentQueryRegExp('_c0', true)); + expect(jsContents).toMatch(contentQueryRegExp('_c0', 5)); }); it('should handle queries that use an InjectionToken', () => { @@ -3143,10 +3231,10 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); - // match `i0.ɵɵviewQuery(TOKEN, true, null)` - expect(jsContents).toMatch(viewQueryRegExp('TOKEN', true)); - // match `i0.ɵɵcontentQuery(dirIndex, TOKEN, true, null)` - expect(jsContents).toMatch(contentQueryRegExp('TOKEN', true)); + // match `i0.ɵɵviewQuery(TOKEN, 5, null)` + expect(jsContents).toMatch(viewQueryRegExp('TOKEN', 5)); + // match `i0.ɵɵcontentQuery(dirIndex, TOKEN, 5, null)` + expect(jsContents).toMatch(contentQueryRegExp('TOKEN', 5)); }); it('should compile expressions that write keys', () => { @@ -3539,7 +3627,9 @@ runInEachFileSystem(os => { selector: 'test', template: '
    Some text {age, plural, 10 {ten} other {other}}
    ' }) - class FooCmp {}`); + class FooCmp { + age = 1; + }`); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents) @@ -3642,7 +3732,7 @@ runInEachFileSystem(os => { import {Component} from '@angular/core'; @Component({ selector: 'test', - template: '
    ' + template: '
    ' }) class FooCmp {} `); @@ -3759,8 +3849,8 @@ runInEachFileSystem(os => { }); it('should not be generated for .js files', () => { - // This test verifies that the compiler does not attempt to generate shim files for non-TS - // input files (in this case, other.js). + // This test verifies that the compiler does not attempt to generate shim files + // for non-TS input files (in this case, other.js). env.write('test.ts', ` import {Component, NgModule} from '@angular/core'; @@ -3786,10 +3876,10 @@ runInEachFileSystem(os => { }); it('should be able to depend on an existing factory shim', () => { - // This test verifies that ngfactory files from the compilations of dependencies are - // available to import in a fresh compilation. It is derived from a bug observed in g3 where - // the shim system accidentally caused TypeScript to think that *.ngfactory.ts files always - // exist. + // This test verifies that ngfactory files from the compilations of dependencies + // are available to import in a fresh compilation. It is derived from a bug + // observed in g3 where the shim system accidentally caused TypeScript to think + // that *.ngfactory.ts files always exist. env.write('other.ngfactory.d.ts', ` export class OtherNgFactory {} `); @@ -3802,8 +3892,9 @@ runInEachFileSystem(os => { }); it('should generate factory shims for files not listed in root files', () => { - // This test verifies that shims are generated for all files in the user's program, even if - // only a subset of those files are listed in the tsconfig as root files. + // This test verifies that shims are generated for all files in the user's + // program, even if only a subset of those files are listed in the tsconfig as + // root files. env.tsconfig({'generateNgFactoryShims': true}, /* extraRootDirs */ undefined, [ absoluteFrom('/test.ts'), @@ -4050,9 +4141,8 @@ runInEachFileSystem(os => { expect(jsContents) .toContain('function Base_Factory(t) { return new (t || Base)(i0.ɵɵinject(Dep)); }'); expect(jsContents) - .toContain('var \u0275Child_BaseFactory = /*@__PURE__*/ i0.ɵɵgetInheritedFactory(Child)'); - expect(jsContents) - .toContain('function Child_Factory(t) { return \u0275Child_BaseFactory(t || Child); }'); + .toContain( + 'function () { var ɵChild_BaseFactory; return function Child_Factory(t) { return (ɵChild_BaseFactory || (ɵChild_BaseFactory = i0.ɵɵgetInheritedFactory(Child)))(t || Child); }; }();'); expect(jsContents) .toContain('function GrandChild_Factory(t) { return new (t || GrandChild)(); }'); }); @@ -4076,9 +4166,9 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); - expect(jsContents) - .toContain('var \u0275Dir_BaseFactory = /*@__PURE__*/ i0.ɵɵgetInheritedFactory(Dir)'); + .toContain( + '/*@__PURE__*/ function () { var ɵDir_BaseFactory; return function Dir_Factory(t) { return (ɵDir_BaseFactory || (ɵDir_BaseFactory = i0.ɵɵgetInheritedFactory(Dir)))(t || Dir); }; }();'); }); it('should wrap "directives" in component metadata in a closure when forward references are present', @@ -4110,7 +4200,7 @@ runInEachFileSystem(os => { expect(jsContents).toContain('directives: function () { return [CmpB]; }'); }); - it('should wrap setClassMetadata in an iife', () => { + it('should wrap setClassMetadata in an iife with ngDevMode guard', () => { env.write('test.ts', ` import {Injectable} from '@angular/core'; @@ -4122,7 +4212,8 @@ runInEachFileSystem(os => { const jsContents = env.getContents('test.js').replace(/\s+/g, ' '); expect(jsContents) .toContain( - `/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(Service, [{ type: Injectable, args: [{ providedIn: 'root' }] }], null, null); })();`); + `(function () { (typeof ngDevMode === "undefined" || ngDevMode) && ` + + `i0.ɵsetClassMetadata(Service, [{ type: Injectable, args: [{ providedIn: 'root' }] }], null, null); })();`); }); it('should not include `schemas` in component and module defs', () => { @@ -4146,7 +4237,7 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = trim(env.getContents('test.js')); expect(jsContents).toContain(trim(` - MyComp.ɵcmp = i0.ɵɵdefineComponent({ + MyComp.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MyComp, selectors: [["comp"]], decls: 1, @@ -4160,7 +4251,8 @@ runInEachFileSystem(os => { }); `)); expect(jsContents) - .toContain(trim('MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule });')); + .toContain( + trim('MyModule.ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: MyModule });')); }); it('should emit setClassMetadata calls for all types', () => { @@ -4270,6 +4362,29 @@ runInEachFileSystem(os => { expect(jsContents).toMatch(setClassMetadataRegExp('type: i1.Other')); }); + it('should not throw when using an SVG-specific `title` tag', () => { + env.write('test.ts', ` + import {Component, NgModule} from '@angular/core'; + @Component({ + template: \` + + + I'm a title tag + + + \`, + }) + export class SvgCmp {} + @NgModule({ + declarations: [SvgCmp], + }) + export class SvgModule {} + `); + + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(0); + }); + describe('namespace support', () => { it('should generate correct imports for type references to namespaced symbols using a namespace import', () => { @@ -4709,11 +4824,57 @@ runInEachFileSystem(os => { const jsContents = env.getContents('test.js'); expect(jsContents).not.toMatch(/i\d\.ɵɵsetComponentScope\([^)]*OtherComponent[^)]*\)/); }); + + it('should detect a simple cycle and fatally error if doing partial-compilation', () => { + env.tsconfig({ + compilationMode: 'partial', + }); + + env.write('test.ts', ` + import {Component, NgModule} from '@angular/core'; + import {NormalComponent} from './cyclic'; + + @Component({ + selector: 'cyclic-component', + template: 'Importing this causes a cycle', + }) + export class CyclicComponent {} + + @NgModule({ + declarations: [NormalComponent, CyclicComponent], + }) + export class Module {} + `); + + env.write('cyclic.ts', ` + import {Component} from '@angular/core'; + + @Component({ + selector: 'normal-component', + template: '', + }) + export class NormalComponent {} + `); + + const diagnostics = env.driveDiagnostics(); + expect(diagnostics.length).toEqual(1); + const error = diagnostics[0]; + expect(error.code).toBe(ngErrorCode(ErrorCode.IMPORT_CYCLE_DETECTED)); + expect(error.messageText) + .toEqual( + 'One or more import cycles would need to be created to compile this component, ' + + 'which is not supported by the current compiler configuration.'); + const _abs = absoluteFrom; + expect(error.relatedInformation?.map(diag => diag.messageText)).toEqual([ + `The component 'CyclicComponent' is used in the template ` + + `but importing it would create a cycle: ` + + `${_abs('/cyclic.ts')} -> ${_abs('/test.ts')} -> ${_abs('/cyclic.ts')}`, + ]); + }); }); describe('local refs', () => { - it('should not generate an error when a local ref is unresolved' + - ' (outside of template type-checking)', + 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'; @@ -4724,7 +4885,9 @@ runInEachFileSystem(os => { export class TestCmp {} `); const diags = env.driveDiagnostics(); - expect(diags.length).toBe(0); + expect(diags.length).toBe(1); + expect(diags[0].messageText) + .toEqual(`No directive found with exportAs 'unknownTarget'.`); }); }); @@ -4917,14 +5080,14 @@ runInEachFileSystem(os => { describe('when processing external directives', () => { it('should not emit multiple references to the same directive', () => { env.write('node_modules/external/index.d.ts', ` - import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ɵɵDirectiveDeclaration, ɵɵNgModuleDeclaration} from '@angular/core'; export declare class ExternalDir { - static ɵdir: ɵɵDirectiveDefWithMeta; + static ɵdir: ɵɵDirectiveDeclaration; } export declare class ExternalModule { - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); env.write('test.ts', ` @@ -4952,19 +5115,19 @@ runInEachFileSystem(os => { it('should import directives by their external name', () => { env.write('node_modules/external/index.d.ts', ` - import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ɵɵDirectiveDeclaration, ɵɵNgModuleDeclaration} from '@angular/core'; import {InternalDir} from './internal'; export {InternalDir as ExternalDir} from './internal'; export declare class ExternalModule { - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); env.write('node_modules/external/internal.d.ts', ` export declare class InternalDir { - static ɵdir: ɵɵDirectiveDefWithMeta; + static ɵdir: ɵɵDirectiveDeclaration; } `); env.write('test.ts', ` @@ -5256,14 +5419,14 @@ runInEachFileSystem(os => { export class Module {} `); env.write('node_modules/external/index.d.ts', ` - import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ɵɵDirectiveDeclaration, ɵɵNgModuleDeclaration} from '@angular/core'; export declare class ExternalDir { - static ɵdir: ɵɵDirectiveDefWithMeta; + static ɵdir: ɵɵDirectiveDeclaration; } export declare class ExternalModule { - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); @@ -5406,14 +5569,14 @@ runInEachFileSystem(os => { it('should not re-export a directive from an exported, external NgModule', () => { env.write(`node_modules/external/index.d.ts`, ` - import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ɵɵDirectiveDeclaration, ɵɵNgModuleDeclaration} from '@angular/core'; export declare class ExternalDir { - static ɵdir: ɵɵDirectiveDefWithMeta; + static ɵdir: ɵɵDirectiveDeclaration; } export declare class ExternalModule { - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); env.write('module.ts', ` @@ -5506,18 +5669,18 @@ runInEachFileSystem(os => { it('should choose a re-exported symbol if one is present', () => { env.write(`node_modules/external/dir.d.ts`, ` - import {ɵɵDirectiveDefWithMeta} from '@angular/core'; + import {ɵɵDirectiveDeclaration} from '@angular/core'; export declare class ExternalDir { - static ɵdir: ɵɵDirectiveDefWithMeta; + static ɵdir: ɵɵDirectiveDeclaration; } `); env.write('node_modules/external/module.d.ts', ` - import {ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ɵɵNgModuleDeclaration} from '@angular/core'; import {ExternalDir} from './dir'; export declare class ExternalModule { - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } export {ExternalDir as ɵngExportɵExternalModuleɵExternalDir}; @@ -5746,7 +5909,7 @@ runInEachFileSystem(os => { it('should generate sanitizers for unsafe attributes in hostBindings fn in Directives', () => { env.write(`test.ts`, ` - import {Component, Directive, HostBinding} from '@angular/core'; + import {Component, Directive, HostBinding, NgModule, Input} from '@angular/core'; @Directive({ selector: '[unsafeAttrs]' @@ -5769,13 +5932,20 @@ runInEachFileSystem(os => { @HostBinding('attr.title') attrSafeTitle: string; + + @Input() unsafeAttrs: any; } @Component({ selector: 'foo', template: '
    Link Title' }) - class FooCmp {} + class FooCmp { + ctxProp = ''; + } + + @NgModule({declarations: [FooCmp, UnsafeAttrsDirective]}) + export class Module {} `); env.driveMain(); @@ -5794,7 +5964,7 @@ runInEachFileSystem(os => { it('should generate sanitizers for unsafe properties in hostBindings fn in Directives', () => { env.write(`test.ts`, ` - import {Component, Directive, HostBinding} from '@angular/core'; + import {Component, Directive, HostBinding, Input, NgModule} from '@angular/core'; @Directive({ selector: '[unsafeProps]' @@ -5817,13 +5987,20 @@ runInEachFileSystem(os => { @HostBinding('title') propSafeTitle: string; + + @Input() unsafeProps: any; } @Component({ selector: 'foo', template: 'Link Title' }) - class FooCmp {} + class FooCmp { + ctxProp = ''; + } + + @NgModule({declarations: [FooCmp, UnsafePropsDirective]}) + class MyModule {} `); env.driveMain(); @@ -5895,13 +6072,13 @@ runInEachFileSystem(os => { beforeEach(() => { env.write('node_modules/@angular/router/index.d.ts', ` - import {ModuleWithProviders, ɵɵNgModuleDefWithMeta as ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ModuleWithProviders, ɵɵNgModuleDeclaration as ɵɵNgModuleDeclaration} from '@angular/core'; export declare var ROUTES; export declare class RouterModule { static forRoot(arg1: any, arg2: any): ModuleWithProviders; static forChild(arg1: any): ModuleWithProviders; - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); }); @@ -6531,24 +6708,24 @@ export const Foo = Foo__PRE_R3__; // 'alpha' declares the directive which will ultimately be imported. env.write('alpha.d.ts', ` - import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ɵɵDirectiveDeclaration, ɵɵNgModuleDeclaration} from '@angular/core'; export declare class ExternalDir { - static ɵdir: ɵɵDirectiveDefWithMeta; + static ɵdir: ɵɵDirectiveDeclaration; } export declare class AlphaModule { - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); // 'beta' re-exports AlphaModule from alpha. env.write('beta.d.ts', ` - import {ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ɵɵNgModuleDeclaration} from '@angular/core'; import {AlphaModule} from './alpha'; export declare class BetaModule { - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); @@ -6581,26 +6758,26 @@ export const Foo = Foo__PRE_R3__; it('should write alias ES2015 exports for NgModule exported directives', () => { env.tsconfig({'_useHostForImportGeneration': true}); env.write('external.d.ts', ` - import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ɵɵDirectiveDeclaration, ɵɵNgModuleDeclaration} from '@angular/core'; import {LibModule} from './lib'; export declare class ExternalDir { - static ɵdir: ɵɵDirectiveDefWithMeta; + static ɵdir: ɵɵDirectiveDeclaration; } export declare class ExternalModule { - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); env.write('lib.d.ts', ` - import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ɵɵDirectiveDeclaration, ɵɵNgModuleDeclaration} from '@angular/core'; export declare class LibDir { - static ɵdir: ɵɵDirectiveDefWithMeta; + static ɵdir: ɵɵDirectiveDeclaration; } export declare class LibModule { - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); env.write('foo.ts', ` @@ -6739,7 +6916,7 @@ export const Foo = Foo__PRE_R3__; `); env.write('lib.d.ts', ` - import {ɵɵComponentDefWithMeta, ɵɵDirectiveDefWithMeta, ElementRef} from '@angular/core'; + import {ɵɵComponentDeclaration, ɵɵDirectiveDeclaration, ElementRef} from '@angular/core'; export declare class BasePlain {} @@ -6752,11 +6929,11 @@ export const Foo = Foo__PRE_R3__; } export declare class BaseCmp { - static ɵcmp: ɵɵComponentDefWithMeta + static ɵcmp: ɵɵComponentDeclaration } export declare class BaseDir { - static ɵdir: ɵɵDirectiveDefWithMeta; + static ɵdir: ɵɵDirectiveDeclaration; } `); }); @@ -7017,7 +7194,8 @@ export const Foo = Foo__PRE_R3__; env.driveMain(); const jsContents = env.getContents('test.js'); - // Verify that long styles present in both components are extracted to a separate var. + // Verify that long styles present in both components are extracted to a + // separate var. expect(jsContents) .toContain( '_c0 = "div[_ngcontent-%COMP%] { background: url(/some-very-very-long-path.png); }";'); @@ -7025,27 +7203,29 @@ export const Foo = Foo__PRE_R3__; expect(jsContents) .toContain( 'styles: [' + - // This style is present in both components, but was not extracted into a separate - // var since it doesn't reach length threshold (50 chars) in `ConstantPool`. + // This style is present in both components, but was not extracted into + // a separate var since it doesn't reach length threshold (50 chars) in + // `ConstantPool`. '"span[_ngcontent-%COMP%] { font-size: larger; }", ' + - // Style that is present in both components, but reaches length threshold - - // extracted to a separate var. + // Style that is present in both components, but reaches length + // threshold - extracted to a separate var. '_c0, ' + - // Style that is unique to this component, but that reaches length threshold - - // remains a string in the `styles` array. + // Style that is unique to this component, but that reaches length + // threshold - remains a string in the `styles` array. '"img[_ngcontent-%COMP%] { background: url(/a/some-very-very-long-path.png); }"]'); expect(jsContents) .toContain( 'styles: [' + - // This style is present in both components, but was not extracted into a separate - // var since it doesn't reach length threshold (50 chars) in `ConstantPool`. + // This style is present in both components, but was not extracted into + // a separate var since it doesn't reach length threshold (50 chars) in + // `ConstantPool`. '"span[_ngcontent-%COMP%] { font-size: larger; }", ' + - // Style that is present in both components, but reaches length threshold - - // extracted to a separate var. + // Style that is present in both components, but reaches length + // threshold - extracted to a separate var. '_c0, ' + - // Style that is unique to this component, but that reaches length threshold - - // remains a string in the `styles` array. + // Style that is unique to this component, but that reaches length + // threshold - remains a string in the `styles` array. '"img[_ngcontent-%COMP%] { background: url(/b/some-very-very-long-path.png); }"]'); }); @@ -7081,9 +7261,9 @@ export const Foo = Foo__PRE_R3__; env.driveMain(); const jsContents = env.getContents('test.js'); - // Verify that long strings are extracted to a separate var. This should be wrapped in a - // function to trick Closure not to inline the contents for very large strings. - // See: https://github.com/angular/angular/pull/38253. + // Verify that long strings are extracted to a separate var. This should be + // wrapped in a function to trick Closure not to inline the contents for very + // large strings. See: https://github.com/angular/angular/pull/38253. expect(jsContents) .toContain( '_c0 = function () {' + @@ -7433,16 +7613,20 @@ export const Foo = Foo__PRE_R3__; expect(diags.length).toBe(0); }); - it('should error when an undecorated class with a non-trivial constructor in a declaration file is provided via useClass', - () => { - env.write('node_modules/@angular/core/testing/index.d.ts', ` + // TODO(alxhub): this test never worked correctly, as it used to declare a constructor with a + // body, which real declaration files don't have. Without the body, the ReflectionHost used to + // not return any constructor data, preventing an error from showing. That bug was fixed, but + // the error for declaration files is disabled until g3 can be updated. + xit('should error when an undecorated class with a non-trivial constructor in a declaration file is provided via useClass', + () => { + env.write('node_modules/@angular/core/testing/index.d.ts', ` export declare class NgZone {} export declare class Testability { - constructor(ngZone: NgZone) {} + constructor(ngZone: NgZone); } `); - env.write('test.ts', ` + env.write('test.ts', ` import {NgModule, Injectable} from '@angular/core'; import {Testability} from '@angular/core/testing'; @@ -7455,10 +7639,10 @@ export const Foo = Foo__PRE_R3__; export class SomeModule {} `); - const diags = env.driveDiagnostics(); - expect(diags.length).toBe(1); - expect(diags[0].messageText).toContain('cannot be created via dependency injection'); - }); + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(1); + expect(diags[0].messageText).toContain('cannot be created via dependency injection'); + }); it('should not error when an class with a factory definition and a non-trivial constructor in a declaration file is provided via useClass', () => { @@ -7468,8 +7652,8 @@ export const Foo = Foo__PRE_R3__; export declare class NgZone {} export declare class Testability { - static ɵfac: i0.ɵɵFactoryDef; - constructor(ngZone: NgZone) {} + static ɵfac: i0.ɵɵFactoryDeclaration; + constructor(ngZone: NgZone); } `); env.write('test.ts', ` @@ -7490,10 +7674,11 @@ export const Foo = Foo__PRE_R3__; }); describe('template parsing diagnostics', () => { - // These tests validate that errors which occur during template parsing are expressed as - // diagnostics instead of a compiler crash (which used to be the case). They only assert - // that the error is produced with an accurate span - the exact semantics of the errors are - // tested separately, via the parser tests. + // These tests validate that errors which occur during template parsing are + // expressed as diagnostics instead of a compiler crash (which used to be the + // case). They only assert that the error is produced with an accurate span - + // the exact semantics of the errors are tested separately, via the parser + // tests. it('should emit a diagnostic for a template parsing error', () => { env.write('test.ts', ` import {Component} from '@angular/core'; @@ -7512,10 +7697,13 @@ export const Foo = Foo__PRE_R3__; env.write('test.ts', ` import {Component} from '@angular/core'; @Component({ - template: '', + template: '', selector: 'test-cmp', }) - export class TestCmp {} + export class TestCmp { + x = null; + y = null; + } `); const diags = env.driveDiagnostics(); expect(diags.length).toBe(1); @@ -7526,17 +7714,172 @@ export const Foo = Foo__PRE_R3__; env.write('test.ts', ` import {Component} from '@angular/core'; @Component({ - template: '', selector: 'test-cmp', }) - export class TestCmp {} + export class TestCmp { + x = null; + } `); const diags = env.driveDiagnostics(); expect(diags.length).toBe(1); expect(getDiagnosticSourceCode(diags[0])).toBe('\''); }); + + it('should emit both type-check diagnostics and parse error diagnostics', () => { + env.write('test.ts', ` + import {Component} from '@angular/core'; + @Component({ + template: \` {{x = 2}}\`, + selector: 'test-cmp', + }) + export class TestCmp { + x: number = 1; + } + `); + const diags = env.driveDiagnostics(); + + expect(diags.length).toBe(2); + expect(diags[0].messageText).toEqual(`Type 'string' is not assignable to type 'number'.`); + expect(diags[1].messageText) + .toContain( + 'Parser Error: Bindings cannot contain assignments at column 5 in [ {{x = 2}}]'); + }); + }); + + describe('i18n errors', () => { + it('reports a diagnostics on nested i18n sections', () => { + env.write('test.ts', ` + import {Component} from '@angular/core'; + @Component({ + selector: 'test-component', + template: '
    Content
    ' + }) + class TestComponent {} + `); + + const diags = env.driveDiagnostics(); + + expect(diags.length).toEqual(1); + expect(diags[0].messageText) + .toEqual( + 'Cannot mark an element as translatable inside of a translatable section.' + + ' Please remove the nested i18n marker.'); + expect(diags[0].file?.fileName).toEqual(absoluteFrom('/test.ts')); + expect(diags[0].file?.text.substr(diags[0].start!, diags[0].length)) + .toEqual('
    Content
    '); + }); + + it('reports a diagnostic on nested i18n sections with tags in between', () => { + env.write('test.ts', ` + import {Component} from '@angular/core'; + @Component({ + selector: 'test-component', + template: '
    Content
    ' + }) + class TestComponent {} + `); + + const diags = env.driveDiagnostics(); + + expect(diags.length).toEqual(1); + expect(diags[0].messageText) + .toEqual( + 'Cannot mark an element as translatable inside of a translatable section.' + + ' Please remove the nested i18n marker.'); + expect(diags[0].file?.fileName).toEqual(absoluteFrom('/test.ts')); + expect(diags[0].file?.text.substr(diags[0].start!, diags[0].length)) + .toEqual('
    Content
    '); + }); + + it('reports a diagnostic on nested i18n sections represented with s', () => { + env.write('test.ts', ` + import {Component} from '@angular/core'; + @Component({ + selector: 'test-component', + template: '
    Content
    ' + }) + class TestComponent {} + `); + + const diags = env.driveDiagnostics(); + + expect(diags.length).toEqual(1); + expect(diags[0].messageText) + .toEqual( + 'Cannot mark an element as translatable inside of a translatable section.' + + ' Please remove the nested i18n marker.'); + expect(diags[0].file?.fileName).toEqual(absoluteFrom('/test.ts')); + expect(diags[0].file?.text.substr(diags[0].start!, diags[0].length)) + .toEqual('Content'); + }); }); }); + + it('reports a COMPONENT_RESOURCE_NOT_FOUND for a component with a templateUrl' + + ' that points to a non-existent file', + () => { + env.write('test.ts', ` + import {Component} from '@angular/core'; + @Component({ + selector: 'test-component', + templateUrl: './non-existent-file.html' + }) + class TestComponent {} + `); + + const diags = env.driveDiagnostics(); + + expect(diags.length).toEqual(1); + expect(diags[0].code).toEqual(ngErrorCode(ErrorCode.COMPONENT_RESOURCE_NOT_FOUND)); + expect(diags[0].messageText) + .toEqual(`Could not find template file './non-existent-file.html'.`); + }); + + it(`reports a COMPONENT_RESOURCE_NOT_FOUND when style sheet link in a component's template` + + ` does not exist`, + () => { + env.write('test.ts', ` + import {Component} from '@angular/core'; + @Component({ + selector: 'test-component', + templateUrl: './test.html' + }) + class TestComponent {} + `); + env.write('test.html', ` + + `); + + const diags = env.driveDiagnostics(); + + expect(diags.length).toEqual(1); + expect(diags[0].code).toEqual(ngErrorCode(ErrorCode.COMPONENT_RESOURCE_NOT_FOUND)); + expect(diags[0].messageText) + .toEqual( + `Could not find stylesheet file './non-existent-file.css' linked from the template.`); + }); + + it('reports a COMPONENT_RESOURCE_NOT_FOUND for a component with a style url ' + + 'defined in a spread that points to a non-existent file', + () => { + env.write('test.ts', ` + import {Component} from '@angular/core'; + @Component({ + selector: 'test-component', + template: '', + styleUrls: [...['./non-existent-file.css']] + }) + class TestComponent {} + `); + + const diags = env.driveDiagnostics(); + + expect(diags.length).toEqual(1); + expect(diags[0].code).toEqual(ngErrorCode(ErrorCode.COMPONENT_RESOURCE_NOT_FOUND)); + expect(diags[0].messageText) + .toEqual(`Could not find stylesheet file './non-existent-file.css'.`); + }); }); function expectTokenAtPosition( @@ -7550,4 +7893,4 @@ export const Foo = Foo__PRE_R3__; 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 e0037e23b8..3990c5cc86 100644 --- a/packages/compiler-cli/test/ngtsc/scope_spec.ts +++ b/packages/compiler-cli/test/ngtsc/scope_spec.ts @@ -50,10 +50,10 @@ runInEachFileSystem(() => { it('should detect when a declaration lives outside the current compilation', () => { env.write('dir.d.ts', ` - import {ɵɵDirectiveDefWithMeta} from '@angular/core'; + import {ɵɵDirectiveDeclaration} from '@angular/core'; export declare class ExternalDir { - static ɵdir: ɵɵDirectiveDefWithMeta; + static ɵdir: ɵɵDirectiveDeclaration; } `); env.write('test.ts', ` @@ -174,7 +174,7 @@ runInEachFileSystem(() => { const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'static ɵmod: i0.ɵɵNgModuleDefWithMeta'); + 'static ɵmod: i0.ɵɵNgModuleDeclaration'); }); it('should produce an error when an invalid class is imported', () => { @@ -237,7 +237,7 @@ runInEachFileSystem(() => { const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'static ɵmod: i0.ɵɵNgModuleDefWithMeta'); + 'static ɵmod: i0.ɵɵNgModuleDeclaration'); }); it('should produce an error when a non-NgModule class is exported', () => { diff --git a/packages/compiler-cli/test/ngtsc/sourcemap_utils.ts b/packages/compiler-cli/test/ngtsc/sourcemap_utils.ts index fe6f0e1e68..d525205d16 100644 --- a/packages/compiler-cli/test/ngtsc/sourcemap_utils.ts +++ b/packages/compiler-cli/test/ngtsc/sourcemap_utils.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 {MappingItem, SourceMapConsumer} from 'source-map'; +import {MappingItem, RawSourceMap, SourceMapConsumer} from 'source-map'; import {NgtscTestEnvironment} from './env'; class TestSourceFile { @@ -72,7 +72,7 @@ export function getMappedSegments( const mappings: MappingItem[] = []; const mapContents = env.getContents(sourceMapFileName); - const sourceMapConsumer = new SourceMapConsumer(JSON.parse(mapContents)); + const sourceMapConsumer = new SourceMapConsumer(JSON.parse(mapContents) as RawSourceMap); sourceMapConsumer.eachMapping(item => { if (!sources.has(item.source)) { sources.set(item.source, new TestSourceFile(item.source, env.getContents(item.source))); diff --git a/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts b/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts index 017abd8190..8c22ab7aeb 100644 --- a/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts +++ b/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts @@ -430,9 +430,9 @@ runInEachFileSystem((os) => { it('should correctly handle collapsed whitespace in interpolation placeholder source-mappings', () => { const mappings = compileAndMap( - `
    pre-body {{body_value}} post-body
    `); + `
    pre-body {{greeting}} post-body
    `); expectMapping(mappings, { - source: '
    ', + source: '
    ', generated: 'i0.ɵɵelementStart(0, "div", 0)', sourceUrl: '../test.ts', }); @@ -447,7 +447,7 @@ runInEachFileSystem((os) => { sourceUrl: '../test.ts', }); expectMapping(mappings, { - source: '{{body_value}}', + source: '{{greeting}}', generated: '"\\uFFFD0\\uFFFD"', sourceUrl: '../test.ts', }); @@ -491,12 +491,12 @@ runInEachFileSystem((os) => { // ivy instructions expectMapping(mappings, { sourceUrl: '../test.ts', - source: '
    \n ', + source: '
    ', generated: 'i0.ɵɵelementStart(0, "div")', }); expectMapping(mappings, { sourceUrl: '../test.ts', - source: '
    \n ', + source: '
    ', generated: 'i0.ɵɵi18nStart(1, 0)', }); expectMapping(mappings, { @@ -586,37 +586,6 @@ runInEachFileSystem((os) => { }); }); - it('should create (simple backtick string) inline template source-mapping', () => { - const mappings = compileAndMap('
    this is a test
    {{ 1 + 2 }}
    '); - - // Creation mode - expectMapping( - mappings, - {generated: 'i0.ɵɵelementStart(0, "div")', source: '
    ', sourceUrl: '../test.ts'}); - expectMapping(mappings, { - generated: 'i0.ɵɵtext(1, "this is a test")', - source: 'this is a test', - sourceUrl: '../test.ts' - }); - expectMapping( - mappings, {generated: 'i0.ɵɵelementEnd()', source: '
    ', sourceUrl: '../test.ts'}); - expectMapping( - mappings, - {generated: 'i0.ɵɵelementStart(2, "div")', source: '
    ', sourceUrl: '../test.ts'}); - expectMapping( - mappings, {generated: 'i0.ɵɵtext(3)', source: '{{ 1 + 2 }}', sourceUrl: '../test.ts'}); - expectMapping( - mappings, {generated: 'i0.ɵɵelementEnd()', source: '
    ', sourceUrl: '../test.ts'}); - - // TODO(benlesh): We need to circle back and prevent the extra parens from being generated. - // Update mode - expectMapping(mappings, { - generated: 'i0.ɵɵtextInterpolate(1 + 2)', - source: '{{ 1 + 2 }}', - sourceUrl: '../test.ts' - }); - }); - it('should create correct inline template source-mapping when the source contains escape sequences', () => { // Note that the escaped double quotes, which need un-escaping to be parsed correctly. @@ -724,13 +693,38 @@ runInEachFileSystem((os) => { const templateConfig = templateUrl ? `templateUrl: '${templateUrl}'` : ('template: `' + template.replace(/`/g, '\\`') + '`'); env.write('test.ts', ` - import {Component} from '@angular/core'; + import {Component, Directive, Input, Output, EventEmitter, Pipe, NgModule} from '@angular/core'; + + @Directive({ + selector: '[ngModel],[attr],[ngModelChange]' + }) + export class AllDirective { + @Input() ngModel!: any; + @Output() ngModelChange = new EventEmitter(); + @Input() attr!: any; + } + + @Pipe({name: 'percent'}) + export class PercentPipe { + transform(v: any) {} + } @Component({ selector: 'test-cmp', ${templateConfig} }) - export class TestCmp {} + export class TestCmp { + name = ''; + isInitial = false; + doSomething() {} + items: any[] = []; + greeting = ''; + } + + @NgModule({ + declarations: [TestCmp, AllDirective, PercentPipe], + }) + export class Module {} `); if (templateUrl) { env.write(templateUrl, template); diff --git a/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts b/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts index 2125d09409..83306ea997 100644 --- a/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts +++ b/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts @@ -42,7 +42,7 @@ export declare class NgForOfContext = i0.NgIterabl export declare class IndexPipe { transform(value: T[], index: number): T; - static ɵpipe: i0.ɵPipeDefWithMeta; + static ɵpipe: i0.ɵPipeDeclaration; } export declare class SlicePipe { @@ -52,7 +52,7 @@ export declare class SlicePipe { transform(value: undefined, start: number, end?: number): undefined; transform(value: any, start: number, end?: number): any; - static ɵpipe: i0.ɵPipeDefWithMeta; + static ɵpipe: i0.ɵPipeDeclaration; } export declare class NgForOf = i0.NgIterable> implements DoCheck { @@ -62,7 +62,7 @@ export declare class NgForOf = i0.NgIterable> i constructor(_viewContainer: ViewContainerRef, _template: TemplateRef>, _differs: IterableDiffers); ngDoCheck(): void; static ngTemplateContextGuard>(dir: NgForOf, ctx: any): ctx is NgForOfContext; - static ɵdir: i0.ɵɵDirectiveDefWithMeta, '[ngFor][ngForOf]', never, {'ngForOf': 'ngForOf'}, {}, never>; + static ɵdir: i0.ɵɵDirectiveDeclaration, '[ngFor][ngForOf]', never, {'ngForOf': 'ngForOf'}, {}, never>; } export declare class NgIf { @@ -72,7 +72,7 @@ export declare class NgIf { constructor(_viewContainer: ViewContainerRef, templateRef: TemplateRef>); static ngTemplateGuard_ngIf: 'binding'; static ngTemplateContextGuard(dir: NgIf, ctx: any): ctx is NgIfContext>; - static ɵdir: i0.ɵɵDirectiveDefWithMeta, '[ngIf]', never, {'ngIf': 'ngIf'}, {}, never>; + static ɵdir: i0.ɵɵDirectiveDeclaration, '[ngIf]', never, {'ngIf': 'ngIf'}, {}, never>; } export declare class NgIfContext { @@ -81,7 +81,7 @@ export declare class NgIfContext { } export declare class CommonModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵmod: i0.ɵɵNgModuleDeclaration; } `); env.write('node_modules/@angular/animations/index.d.ts', ` @@ -110,6 +110,23 @@ export declare class AnimationEvent { env.driveMain(); }); + it('should have accurate diagnostics in a template using crlf line endings', () => { + env.write('test.ts', ` + import {Component} from '@angular/core'; + + @Component({ + selector: 'test', + templateUrl: './test.html', + }) + class TestCmp {} + `); + env.write('test.html', '\r\n{{does_not_exist}}\r\n'); + + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(1); + expect(getSourceCodeForDiagnostic(diags[0])).toBe('does_not_exist'); + }); + it('should check regular attributes that are directive inputs', () => { env.tsconfig( {fullTemplateTypeCheck: true, strictInputTypes: true, strictAttributeTypes: true}); @@ -1128,7 +1145,7 @@ export declare class AnimationEvent { it('should report an error with an unknown pipe even if `fullTemplateTypeCheck` is disabled', () => { - env.tsconfig({ivyTemplateTypeCheck: true, fullTemplateTypeCheck: false}); + env.tsconfig({fullTemplateTypeCheck: false}); env.write('test.ts', ` import {Component, NgModule} from '@angular/core'; @@ -1329,17 +1346,17 @@ export declare class AnimationEvent { export declare class AbstractDir { fromAbstract: number; - static ɵdir: i0.ɵɵDirectiveDefWithMeta; + static ɵdir: i0.ɵɵDirectiveDeclaration; } export declare class BaseDir extends AbstractDir { fromBase: string; - static ɵdir: i0.ɵɵDirectiveDefWithMeta; + static ɵdir: i0.ɵɵDirectiveDeclaration; } export declare class ExternalModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵmod: i0.ɵɵNgModuleDeclaration; } `); @@ -1455,25 +1472,25 @@ export declare class AnimationEvent { // 'alpha' declares the directive which will ultimately be imported. env.write('alpha.d.ts', ` - import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ɵɵDirectiveDeclaration, ɵɵNgModuleDeclaration} from '@angular/core'; export declare class ExternalDir { input: string; - static ɵdir: ɵɵDirectiveDefWithMeta; + static ɵdir: ɵɵDirectiveDeclaration; } export declare class AlphaModule { - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); // 'beta' re-exports AlphaModule from alpha. env.write('beta.d.ts', ` - import {ɵɵNgModuleDefWithMeta} from '@angular/core'; + import {ɵɵNgModuleDeclaration} from '@angular/core'; import {AlphaModule} from './alpha'; export declare class BetaModule { - static ɵmod: ɵɵNgModuleDefWithMeta; + static ɵmod: ɵɵNgModuleDeclaration; } `); @@ -1508,12 +1525,12 @@ export declare class AnimationEvent { export declare class MatInput { value: string; - static ɵdir: i0.ɵɵDirectiveDefWithMeta; + static ɵdir: i0.ɵɵDirectiveDeclaration; static ngAcceptInputType_value: string|number; } export declare class MatInputModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵmod: i0.ɵɵNgModuleDeclaration; } `); }); @@ -1896,7 +1913,7 @@ export declare class AnimationEvent { describe('legacy schema checking with the DOM schema', () => { beforeEach(() => { - env.tsconfig({ivyTemplateTypeCheck: true, fullTemplateTypeCheck: false}); + env.tsconfig({fullTemplateTypeCheck: false}); }); it('should check for unknown elements', () => { diff --git a/packages/compiler-cli/test/perform_compile_spec.ts b/packages/compiler-cli/test/perform_compile_spec.ts index 9c6238e689..c140f487e9 100644 --- a/packages/compiler-cli/test/perform_compile_spec.ts +++ b/packages/compiler-cli/test/perform_compile_spec.ts @@ -7,6 +7,7 @@ */ import * as path from 'path'; +import * as ts from 'typescript'; import {readConfiguration} from '../src/perform_compile'; @@ -77,4 +78,62 @@ describe('perform_compile', () => { const {options} = readConfiguration(path.resolve(basePath, 'tsconfig-level-1.json')); expect(options.enableIvy).toBe(false); }); + + it('should override options defined in tsconfig with those defined in `existingOptions`', () => { + support.writeFiles({ + 'tsconfig-level-1.json': `{ + "compilerOptions": { + "target": "es2020" + }, + "angularCompilerOptions": { + "annotateForClosureCompiler": true + } + } + ` + }); + + const {options} = readConfiguration( + path.resolve(basePath, 'tsconfig-level-1.json'), + {annotateForClosureCompiler: false, target: ts.ScriptTarget.ES2015, enableIvy: false}); + + expect(options).toEqual(jasmine.objectContaining({ + enableIvy: false, + target: ts.ScriptTarget.ES2015, + annotateForClosureCompiler: false, + })); + }); + + it('should merge tsconfig "angularCompilerOptions" when extends point to node package', () => { + support.writeFiles({ + 'tsconfig-level-1.json': `{ + "extends": "@angular-ru/tsconfig", + "angularCompilerOptions": { + "enableIvy": false + } + } + `, + 'node_modules/@angular-ru/tsconfig/tsconfig.json': `{ + "compilerOptions": { + "strict": true + }, + "angularCompilerOptions": { + "skipMetadataEmit": true + } + } + `, + 'node_modules/@angular-ru/tsconfig/package.json': `{ + "name": "@angular-ru/tsconfig", + "version": "0.0.0", + "main": "./tsconfig.json" + } + `, + }); + + const {options} = readConfiguration(path.resolve(basePath, 'tsconfig-level-1.json')); + expect(options).toEqual(jasmine.objectContaining({ + strict: true, + skipMetadataEmit: true, + enableIvy: false, + })); + }); }); diff --git a/packages/compiler-cli/test/transformers/downlevel_decorators_transform_spec.ts b/packages/compiler-cli/test/transformers/downlevel_decorators_transform_spec.ts index 28fc1708a9..7aa942901c 100644 --- a/packages/compiler-cli/test/transformers/downlevel_decorators_transform_spec.ts +++ b/packages/compiler-cli/test/transformers/downlevel_decorators_transform_spec.ts @@ -740,6 +740,107 @@ describe('downlevel decorator transform', () => { `); }); }); + + describe('transforming multiple files', () => { + it('should work correctly for multiple files that import distinct declarations', () => { + context.writeFile('foo_service.d.ts', ` + export declare class Foo {}; + `); + context.writeFile('foo.ts', ` + import {Injectable} from '@angular/core'; + import {Foo} from './foo_service'; + + @Injectable() + export class MyService { + constructor(foo: Foo) {} + } + `); + + context.writeFile('bar_service.d.ts', ` + export declare class Bar {}; + `); + context.writeFile('bar.ts', ` + import {Injectable} from '@angular/core'; + import {Bar} from './bar_service'; + + @Injectable() + export class MyService { + constructor(bar: Bar) {} + } + `); + + const {program, transformers} = createProgramWithTransform(['/foo.ts', '/bar.ts']); + program.emit(undefined, undefined, undefined, undefined, transformers); + + expect(context.readFile('/foo.js')).toContain(`import { Foo } from './foo_service';`); + expect(context.readFile('/bar.js')).toContain(`import { Bar } from './bar_service';`); + }); + + it('should not result in a stack overflow for a large number of files', () => { + // The decorators transform used to patch `ts.EmitResolver.isReferencedAliasDeclaration` + // repeatedly for each source file in the program, causing a stack overflow once a large + // number of source files was reached. This test verifies that emit succeeds even when there's + // lots of source files. See https://github.com/angular/angular/issues/40276. + context.writeFile('foo.d.ts', ` + export declare class Foo {}; + `); + + // A somewhat minimal number of source files that used to trigger a stack overflow. + const numberOfTestFiles = 6500; + const files: string[] = []; + for (let i = 0; i < numberOfTestFiles; i++) { + const file = `/${i}.ts`; + files.push(file); + context.writeFile(file, ` + import {Injectable} from '@angular/core'; + import {Foo} from './foo'; + + @Injectable() + export class MyService { + constructor(foo: Foo) {} + } + `); + } + + const {program, transformers} = createProgramWithTransform(files); + + let written = 0; + program.emit(undefined, (fileName, outputText) => { + written++; + + // The below assertion throws an explicit error instead of using a Jasmine expectation, + // as we want to abort on the first failure, if any. This avoids as many as `numberOfFiles` + // expectation failures, which would bloat the test output. + if (!outputText.includes(`import { Foo } from './foo';`)) { + throw new Error(`Transform failed to preserve the import in ${fileName}:\n${outputText}`); + } + }, undefined, undefined, transformers); + expect(written).toBe(numberOfTestFiles); + }); + + function createProgramWithTransform(files: string[]) { + const program = ts.createProgram( + files, { + moduleResolution: ts.ModuleResolutionKind.NodeJs, + importHelpers: true, + lib: [], + module: ts.ModuleKind.ESNext, + target: ts.ScriptTarget.Latest, + declaration: false, + experimentalDecorators: true, + emitDecoratorMetadata: false, + }, + host); + const typeChecker = program.getTypeChecker(); + const reflectionHost = new TypeScriptReflectionHost(typeChecker); + const transformers: ts.CustomTransformers = { + before: [getDownlevelDecoratorsTransform( + program.getTypeChecker(), reflectionHost, diagnostics, + /* isCore */ false, isClosureEnabled, skipClassDecorators)] + }; + return {program, transformers}; + } + }); }); /** Template string function that can be used to dedent a given string literal. */ diff --git a/packages/compiler-cli/test/transformers/node_emitter_spec.ts b/packages/compiler-cli/test/transformers/node_emitter_spec.ts index f4915d7704..1d2f303321 100644 --- a/packages/compiler-cli/test/transformers/node_emitter_spec.ts +++ b/packages/compiler-cli/test/transformers/node_emitter_spec.ts @@ -518,7 +518,7 @@ describe('TypeScriptNodeEmitter', () => { const sourceMapBase64 = sourceMapMatch![1]; const sourceMapBuffer = Buffer.from(sourceMapBase64, 'base64'); const sourceMapText = sourceMapBuffer.toString('utf8'); - const sourceMapParsed = JSON.parse(sourceMapText); + const sourceMapParsed = JSON.parse(sourceMapText) as unknown; const consumer = new sourceMap.SourceMapConsumer(sourceMapParsed); const mappings: any[] = []; consumer.eachMapping((mapping: any) => { diff --git a/packages/compiler/design/separate_compilation.md b/packages/compiler/design/separate_compilation.md index 3082775469..b6f53ad2a1 100644 --- a/packages/compiler/design/separate_compilation.md +++ b/packages/compiler/design/separate_compilation.md @@ -415,55 +415,6 @@ except for the call to `ɵɵdefineInjector` would generate a `{ __symbolic: 'err value which is ignored by the ivy compiler. This allows the system to ignore the difference between manually and mechanically created module definitions. - -## Manual Considerations - -With this proposal, the compiler treats manually and mechanically generated -Angular definitions identically. This allows flexibility not only in the future -for how the declarations are mechanically produced, it also allows an alternative -mechanism to generate declarations that can be easily explored without altering the -compiler or dependent tool chain. It also allows third-party code generators -with possibly different component syntaxes to generate a component that is fully -understood by the compiler. - -Unfortunately, manually generated modules contain references to -classes that might not be necessary at runtime. Manually or third-party -components can get the same payload properties of an Angular generated -component by annotating the `ngSelector` and `ngModuleScope` properties with -`// @__BUILD_OPTIMIZER_REMOVE_` comment which will cause the build optimizer -to remove the declaration. - -##### Example - -For example the above manually created module would have better payload -properties by including a `// @__BUILD_OPTIMIZER_REMOVE_` comment: - -```ts -export class MyModule { - static ɵinj = ɵɵdefineInjector({ - providers: [{ - provide: Service, useClass: ServiceImpl - }], - imports: [CommonModule, UtilityModule] - }); - - // @__BUILD_OPTIMIZER_REMOVE_ - static ngModuleScope = [{ - type: MyComponent, - selector: 'my-comp' - }, { - type: MyDirective, - selector: '[my-dir]' - }, { - type: MyPipe, - name: 'myPipe' - }, { - type: UtilityModule, - isModule: true - }]; -} -``` - ## `ngc` output (non-Bazel) The cases that `ngc` handle are producing an application and producing a @@ -724,7 +675,7 @@ To produce an Ivy library the options would look like, ##### Example - Ivy package Ivy packages are not supported in Angular 6.0 as they are not recommended in -npm packages as they would only be usable if inside Ivy applications. +npm packages as they would only be usable if inside Ivy applications. Ivy applications support Renderer2 libraries so npm packages should all be Renderer2 libraries. diff --git a/packages/compiler/package.json b/packages/compiler/package.json index b12dee8beb..2c9256deba 100644 --- a/packages/compiler/package.json +++ b/packages/compiler/package.json @@ -5,7 +5,7 @@ "author": "angular", "license": "MIT", "dependencies": { - "tslib": "^2.0.0" + "tslib": "^2.1.0" }, "repository": { "type": "git", diff --git a/packages/compiler/src/aot/compiler.ts b/packages/compiler/src/aot/compiler.ts index 1a123791e1..47db67073a 100644 --- a/packages/compiler/src/aot/compiler.ts +++ b/packages/compiler/src/aot/compiler.ts @@ -15,23 +15,16 @@ import {createTokenForExternalReference, Identifiers} from '../identifiers'; import {InjectableCompiler} from '../injectable_compiler'; import {CompileMetadataResolver} from '../metadata_resolver'; import {HtmlParser} from '../ml_parser/html_parser'; -import {removeWhitespaces} from '../ml_parser/html_whitespaces'; -import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/interpolation_config'; +import {InterpolationConfig} from '../ml_parser/interpolation_config'; import {NgModuleCompiler} from '../ng_module_compiler'; import {OutputEmitter} from '../output/abstract_emitter'; import * as o from '../output/output_ast'; import {ParseError} from '../parse_util'; -import {compileNgModuleFromRender2 as compileR3Module} from '../render3/r3_module_compiler'; -import {compilePipeFromRender2 as compileR3Pipe} from '../render3/r3_pipe_compiler'; -import {htmlAstToRender3Ast} from '../render3/r3_template_transform'; -import {compileComponentFromRender2 as compileR3Component, compileDirectiveFromRender2 as compileR3Directive} from '../render3/view/compiler'; -import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry'; import {CompiledStylesheet, StyleCompiler} from '../style_compiler'; 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 {error, newArray, OutputContext, syntaxError, ValueVisitor, visitValue} from '../util'; +import {newArray, OutputContext, syntaxError, ValueVisitor, visitValue} from '../util'; import {TypeCheckCompiler} from '../view_compiler/type_check_compiler'; import {ViewCompiler, ViewCompileResult} from '../view_compiler/view_compiler'; @@ -341,107 +334,6 @@ export class AotCompiler { return messageBundle; } - emitAllPartialModules( - {ngModuleByPipeOrDirective, files}: NgAnalyzedModules, - r3Files: NgAnalyzedFileWithInjectables[]): PartialModule[] { - const contextMap = new Map(); - - const getContext = (fileName: string): OutputContext => { - if (!contextMap.has(fileName)) { - contextMap.set(fileName, this._createOutputContext(fileName)); - } - return contextMap.get(fileName)!; - }; - - files.forEach( - file => this._compilePartialModule( - file.fileName, ngModuleByPipeOrDirective, file.directives, file.pipes, file.ngModules, - file.injectables, getContext(file.fileName))); - r3Files.forEach( - file => this._compileShallowModules( - file.fileName, file.shallowModules, getContext(file.fileName))); - - return Array.from(contextMap.values()) - .map(context => ({ - fileName: context.genFilePath, - statements: [...context.constantPool.statements, ...context.statements], - })); - } - - private _compileShallowModules( - fileName: string, shallowModules: CompileShallowModuleMetadata[], - context: OutputContext): void { - shallowModules.forEach(module => compileR3Module(context, module, this._injectableCompiler)); - } - - private _compilePartialModule( - fileName: string, ngModuleByPipeOrDirective: Map, - directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: CompileNgModuleMetadata[], - injectables: CompileInjectableMetadata[], context: OutputContext): void { - const errors: ParseError[] = []; - - const schemaRegistry = new DomElementSchemaRegistry(); - const hostBindingParser = new BindingParser( - this._templateParser.expressionParser, DEFAULT_INTERPOLATION_CONFIG, schemaRegistry, [], - errors); - - // Process all components and directives - directives.forEach(directiveType => { - const directiveMetadata = this._metadataResolver.getDirectiveMetadata(directiveType); - if (directiveMetadata.isComponent) { - const module = ngModuleByPipeOrDirective.get(directiveType)!; - module || - error(`Cannot determine the module for component '${ - identifierName(directiveMetadata.type)}'`); - - let htmlAst = directiveMetadata.template !.htmlAst!; - const preserveWhitespaces = directiveMetadata!.template !.preserveWhitespaces; - - if (!preserveWhitespaces) { - htmlAst = removeWhitespaces(htmlAst); - } - const render3Ast = htmlAstToRender3Ast(htmlAst.rootNodes, hostBindingParser); - - // Map of StaticType by directive selectors - const directiveTypeBySel = new Map(); - - const directives = module.transitiveModule.directives.map( - dir => this._metadataResolver.getDirectiveSummary(dir.reference)); - - directives.forEach(directive => { - if (directive.selector) { - directiveTypeBySel.set(directive.selector, directive.type.reference); - } - }); - - // Map of StaticType by pipe names - const pipeTypeByName = new Map(); - - const pipes = module.transitiveModule.pipes.map( - pipe => this._metadataResolver.getPipeSummary(pipe.reference)); - - pipes.forEach(pipe => { - pipeTypeByName.set(pipe.name, pipe.type.reference); - }); - - compileR3Component( - context, directiveMetadata, render3Ast, this.reflector, hostBindingParser, - directiveTypeBySel, pipeTypeByName); - } else { - compileR3Directive(context, directiveMetadata, this.reflector, hostBindingParser); - } - }); - - pipes.forEach(pipeType => { - const pipeMetadata = this._metadataResolver.getPipeMetadata(pipeType); - if (pipeMetadata) { - compileR3Pipe(context, pipeMetadata, this.reflector); - } - }); - - injectables.forEach(injectable => this._injectableCompiler.compile(injectable, context)); - } - emitAllPartialModules2(files: NgAnalyzedFileWithInjectables[]): PartialModule[] { // Using reduce like this is a select many pattern (where map is a select pattern) return files.reduce((r, file) => { diff --git a/packages/compiler/src/aot/summary_serializer.ts b/packages/compiler/src/aot/summary_serializer.ts index ccf517b65f..956910f311 100644 --- a/packages/compiler/src/aot/summary_serializer.ts +++ b/packages/compiler/src/aot/summary_serializer.ts @@ -461,7 +461,7 @@ class FromJsonDeserializer extends ValueTransformer { summaries: Summary[], importAs: {symbol: StaticSymbol, importAs: StaticSymbol}[] } { - const data: {moduleName: string|null, summaries: any[], symbols: any[]} = JSON.parse(json); + const data = JSON.parse(json) as {moduleName: string | null, summaries: any[], symbols: any[]}; const allImportAs: {symbol: StaticSymbol, importAs: StaticSymbol}[] = []; this.symbols = data.symbols.map( (serializedSymbol) => this.symbolCache.get( diff --git a/packages/compiler/src/compile_metadata.ts b/packages/compiler/src/compile_metadata.ts index 80232d0aaf..e194e6183e 100644 --- a/packages/compiler/src/compile_metadata.ts +++ b/packages/compiler/src/compile_metadata.ts @@ -169,6 +169,7 @@ export interface CompileQueryMetadata { propertyName: string; read: CompileTokenMetadata; static?: boolean; + emitDistinctChangesOnly?: boolean; } /** diff --git a/packages/compiler/src/compiler.ts b/packages/compiler/src/compiler.ts index a1041f372e..9f1bc70dc6 100644 --- a/packages/compiler/src/compiler.ts +++ b/packages/compiler/src/compiler.ts @@ -78,7 +78,7 @@ export * from './ml_parser/tags'; export {LexerRange} from './ml_parser/lexer'; export * from './ml_parser/xml_parser'; export {NgModuleCompiler} from './ng_module_compiler'; -export {ArrayType, AssertNotNull, DYNAMIC_TYPE, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinType, BuiltinTypeName, BuiltinVar, CastExpr, ClassField, ClassMethod, ClassStmt, CommaExpr, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, ExternalReference, literalMap, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, NONE_TYPE, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, ThrowStmt, TryCatchStmt, Type, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, STRING_TYPE, TypeofExpr, collectExternalReferences, jsDocComment, leadingComment, LeadingComment, JSDocComment, UnaryOperator, UnaryOperatorExpr, LocalizedString} from './output/output_ast'; +export {ArrayType, AssertNotNull, DYNAMIC_TYPE, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinType, BuiltinTypeName, BuiltinVar, CastExpr, ClassField, ClassMethod, ClassStmt, CommaExpr, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, ExternalReference, literalMap, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, NONE_TYPE, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, TaggedTemplateExpr, TemplateLiteral, TemplateLiteralElement, ThrowStmt, TryCatchStmt, Type, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, STRING_TYPE, TypeofExpr, collectExternalReferences, jsDocComment, leadingComment, LeadingComment, JSDocComment, UnaryOperator, UnaryOperatorExpr, LocalizedString} from './output/output_ast'; export {EmitterVisitorContext} from './output/abstract_emitter'; export {JitEvaluator} from './output/output_jit'; export * from './output/ts_emitter'; @@ -91,19 +91,25 @@ export {ViewCompiler} from './view_compiler/view_compiler'; export {getParseErrors, isSyntaxError, syntaxError, Version} from './util'; export {SourceMap} from './output/source_map'; export * from './injectable_compiler_2'; +export * from './render3/partial/api'; export * from './render3/view/api'; export {BoundAttribute as TmplAstBoundAttribute, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, Element as TmplAstElement, Icu as TmplAstIcu, Node as TmplAstNode, RecursiveVisitor as TmplAstRecursiveVisitor, Reference as TmplAstReference, Template as TmplAstTemplate, Text as TmplAstText, TextAttribute as TmplAstTextAttribute, Variable as TmplAstVariable} from './render3/r3_ast'; export * from './render3/view/t2_api'; export * from './render3/view/t2_binder'; export {Identifiers as R3Identifiers} from './render3/r3_identifiers'; -export {R3DependencyMetadata, R3ResolvedDependencyType, compileFactoryFunction, R3FactoryMetadata, R3FactoryTarget} from './render3/r3_factory'; -export {compileInjector, compileNgModule, R3InjectorMetadata, R3NgModuleMetadata} from './render3/r3_module_compiler'; +export {compileFactoryFunction, R3DependencyMetadata, R3FactoryMetadata, FactoryTarget} from './render3/r3_factory'; +export {compileNgModule, R3NgModuleMetadata} from './render3/r3_module_compiler'; +export {compileInjector, R3InjectorMetadata} from './render3/r3_injector_compiler'; export {compilePipeFromMetadata, R3PipeMetadata} from './render3/r3_pipe_compiler'; export {makeBindingParser, ParsedTemplate, parseTemplate, ParseTemplateOptions} from './render3/view/template'; -export {R3Reference} from './render3/util'; +export {R3CompiledExpression, R3Reference, devOnlyGuardedExpression, getSafePropertyAccessString} from './render3/util'; export {compileComponentFromMetadata, compileDirectiveFromMetadata, parseHostBindings, ParsedHostBindings, verifyHostBindings} from './render3/view/compiler'; export {compileDeclareComponentFromMetadata} from './render3/partial/component'; export {compileDeclareDirectiveFromMetadata} from './render3/partial/directive'; +export {compileDeclareFactoryFunction} from './render3/partial/factory'; +export {compileDeclareInjectorFromMetadata} from './render3/partial/injector'; +export {compileDeclareNgModuleFromMetadata} from './render3/partial/ng_module'; +export {compileDeclarePipeFromMetadata} from './render3/partial/pipe'; export {publishFacade} from './jit_compiler_facade'; // This file only reexports content of the `src` folder. Keep it that way. diff --git a/packages/compiler/src/compiler_facade_interface.ts b/packages/compiler/src/compiler_facade_interface.ts index d60b8be014..5343e4583c 100644 --- a/packages/compiler/src/compiler_facade_interface.ts +++ b/packages/compiler/src/compiler_facade_interface.ts @@ -29,23 +29,38 @@ export interface ExportedCompilerFacade { export interface CompilerFacade { compilePipe(angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3PipeMetadataFacade): any; + compilePipeDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, declaration: R3DeclarePipeFacade): any; compileInjectable( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3InjectableMetadataFacade): any; compileInjector( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3InjectorMetadataFacade): any; + compileInjectorDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, + declaration: R3DeclareInjectorFacade): any; compileNgModule( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3NgModuleMetadataFacade): any; + compileNgModuleDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, + declaration: R3DeclareNgModuleFacade): any; compileDirective( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3DirectiveMetadataFacade): any; + compileDirectiveDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, + declaration: R3DeclareDirectiveFacade): any; compileComponent( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3ComponentMetadataFacade): any; + compileComponentDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, + declaration: R3DeclareComponentFacade): any; compileFactory( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3FactoryDefMetadataFacade): any; + compileFactoryDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3DeclareFactoryFacade): any; createParseSourceSpan(kind: string, typeName: string, sourceUrl: string): ParseSourceSpan; - R3ResolvedDependencyType: typeof R3ResolvedDependencyType; - R3FactoryTarget: typeof R3FactoryTarget; + FactoryTarget: typeof FactoryTarget; ResourceLoader: {new(): ResourceLoader}; } @@ -67,14 +82,7 @@ export type StringMapWithRename = { export type Provider = any; -export enum R3ResolvedDependencyType { - Token = 0, - Attribute = 1, - ChangeDetectorRef = 2, - Invalid = 3, -} - -export enum R3FactoryTarget { +export enum FactoryTarget { Directive = 0, Component = 1, Injectable = 2, @@ -83,20 +91,27 @@ export enum R3FactoryTarget { } export interface R3DependencyMetadataFacade { - token: any; - resolved: R3ResolvedDependencyType; + token: unknown; + attribute: string|null; host: boolean; optional: boolean; self: boolean; skipSelf: boolean; } +export interface R3DeclareDependencyMetadataFacade { + token: unknown; + attribute?: boolean; + host?: boolean; + optional?: boolean; + self?: boolean; + skipSelf?: boolean; +} + export interface R3PipeMetadataFacade { name: string; type: any; - typeArgumentCount: number; pipeName: string; - deps: R3DependencyMetadataFacade[]|null; pure: boolean; } @@ -125,7 +140,6 @@ export interface R3NgModuleMetadataFacade { export interface R3InjectorMetadataFacade { name: string; type: any; - deps: R3DependencyMetadataFacade[]|null; providers: any[]; imports: any[]; } @@ -133,9 +147,7 @@ export interface R3InjectorMetadataFacade { export interface R3DirectiveMetadataFacade { name: string; type: any; - typeArgumentCount: number; typeSourceSpan: ParseSourceSpan; - deps: R3DependencyMetadataFacade[]|null; selector: string|null; queries: R3QueryMetadataFacade[]; host: {[key: string]: string}; @@ -162,6 +174,51 @@ export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade { changeDetection?: ChangeDetectionStrategy; } +export type OpaqueValue = unknown; + +export interface R3DeclareDirectiveFacade { + selector?: string; + type: Function; + inputs?: {[classPropertyName: string]: string|[string, string]}; + outputs?: {[classPropertyName: string]: string}; + host?: { + attributes?: {[key: string]: OpaqueValue}; + listeners?: {[key: string]: string}; + properties?: {[key: string]: string}; + classAttribute?: string; + styleAttribute?: string; + }; + queries?: R3DeclareQueryMetadataFacade[]; + viewQueries?: R3DeclareQueryMetadataFacade[]; + providers?: OpaqueValue; + exportAs?: string[]; + usesInheritance?: boolean; + usesOnChanges?: boolean; +} + +export interface R3DeclareComponentFacade extends R3DeclareDirectiveFacade { + template: string; + isInline?: boolean; + styles?: string[]; + components?: R3DeclareUsedDirectiveFacade[]; + directives?: R3DeclareUsedDirectiveFacade[]; + pipes?: {[pipeName: string]: OpaqueValue|(() => OpaqueValue)}; + viewProviders?: OpaqueValue; + animations?: OpaqueValue; + changeDetection?: ChangeDetectionStrategy; + encapsulation?: ViewEncapsulation; + interpolation?: [string, string]; + preserveWhitespaces?: boolean; +} + +export interface R3DeclareUsedDirectiveFacade { + selector: string; + type: OpaqueValue|(() => OpaqueValue); + inputs?: string[]; + outputs?: string[]; + exportAs?: string[]; +} + export interface R3UsedDirectiveMetadata { selector: string; inputs: string[]; @@ -175,8 +232,13 @@ export interface R3FactoryDefMetadataFacade { type: any; typeArgumentCount: number; deps: R3DependencyMetadataFacade[]|null; - injectFn: 'directiveInject'|'inject'; - target: R3FactoryTarget; + target: FactoryTarget; +} + +export interface R3DeclareFactoryFacade { + type: Function; + deps: R3DeclareDependencyMetadataFacade[]|null; + target: FactoryTarget; } export enum ViewEncapsulation { @@ -193,10 +255,43 @@ export interface R3QueryMetadataFacade { first: boolean; predicate: any|string[]; descendants: boolean; + emitDistinctChangesOnly: boolean; read: any|null; static: boolean; } +export interface R3DeclareQueryMetadataFacade { + propertyName: string; + first?: boolean; + predicate: OpaqueValue|string[]; + descendants?: boolean; + read?: OpaqueValue; + static?: boolean; + emitDistinctChangesOnly?: boolean; +} + +export interface R3DeclareInjectorFacade { + type: Function; + imports?: OpaqueValue[]; + providers?: OpaqueValue[]; +} + +export interface R3DeclareNgModuleFacade { + type: Function; + bootstrap?: OpaqueValue[]|(() => OpaqueValue[]); + declarations?: OpaqueValue[]|(() => OpaqueValue[]); + imports?: OpaqueValue[]|(() => OpaqueValue[]); + exports?: OpaqueValue[]|(() => OpaqueValue[]); + schemas?: OpaqueValue[]; + id?: OpaqueValue; +} + +export interface R3DeclarePipeFacade { + type: Function; + name: string; + pure?: boolean; +} + export interface ParseSourceSpan { start: any; end: any; diff --git a/packages/compiler/src/constant_pool.ts b/packages/compiler/src/constant_pool.ts index 0046f7cc87..8b119f0094 100644 --- a/packages/compiler/src/constant_pool.ts +++ b/packages/compiler/src/constant_pool.ts @@ -252,8 +252,6 @@ export class ConstantPool { case DefinitionKind.Pipe: return this.pipeDefinitions; } - error(`Unknown definition kind ${kind}`); - return this.componentDefinitions; } public propertyNameOf(kind: DefinitionKind): string { @@ -267,8 +265,6 @@ export class ConstantPool { case DefinitionKind.Pipe: return 'ɵpipe'; } - error(`Unknown definition kind ${kind}`); - return ''; } private freshName(): string { @@ -324,6 +320,7 @@ class KeyVisitor implements o.ExpressionVisitor { visitWritePropExpr = invalid; visitInvokeMethodExpr = invalid; visitInvokeFunctionExpr = invalid; + visitTaggedTemplateExpr = invalid; visitInstantiateExpr = invalid; visitConditionalExpr = invalid; visitNotExpr = invalid; diff --git a/packages/compiler/src/core.ts b/packages/compiler/src/core.ts index d9360f45fa..dc19a925ae 100644 --- a/packages/compiler/src/core.ts +++ b/packages/compiler/src/core.ts @@ -27,6 +27,11 @@ export interface Attribute { export const createAttribute = makeMetadataFactory('Attribute', (attributeName: string) => ({attributeName})); +// Stores the default value of `emitDistinctChangesOnly` when the `emitDistinctChangesOnly` is not +// explicitly set. +export const emitDistinctChangesOnlyDefaultValue = true; + + export interface Query { descendants: boolean; first: boolean; @@ -34,20 +39,31 @@ export interface Query { isViewQuery: boolean; selector: any; static?: boolean; + emitDistinctChangesOnly: boolean; } export const createContentChildren = makeMetadataFactory( - 'ContentChildren', - (selector?: any, data: any = {}) => - ({selector, first: false, isViewQuery: false, descendants: false, ...data})); + 'ContentChildren', (selector?: any, data: any = {}) => ({ + selector, + first: false, + isViewQuery: false, + descendants: false, + emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue, + ...data + })); export const createContentChild = makeMetadataFactory( 'ContentChild', (selector?: any, data: any = {}) => ({selector, first: true, isViewQuery: false, descendants: true, ...data})); export const createViewChildren = makeMetadataFactory( - '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, + emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue, + ...data + })); export const createViewChild = makeMetadataFactory( 'ViewChild', (selector: any, data: any) => @@ -224,6 +240,7 @@ export const enum NodeFlags { StaticQuery = 1 << 28, DynamicQuery = 1 << 29, TypeModuleProvider = 1 << 30, + EmitDistinctChangesOnly = 1 << 31, CatQuery = TypeContentQuery | TypeViewQuery, // mutually exclusive values... @@ -255,6 +272,11 @@ export const enum InjectFlags { SkipSelf = 1 << 2, /** Inject `defaultValue` instead if token not found. */ Optional = 1 << 3, + /** + * This token is being injected into a pipe. + * @internal + */ + ForPipe = 1 << 4, } export const enum ArgumentType { diff --git a/packages/compiler/src/css_parser/css_ast.ts b/packages/compiler/src/css_parser/css_ast.ts deleted file mode 100644 index b36b8195ab..0000000000 --- a/packages/compiler/src/css_parser/css_ast.ts +++ /dev/null @@ -1,264 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {ParseLocation, ParseSourceSpan} from '../parse_util'; - -import {CssToken, CssTokenType} from './css_lexer'; - -export enum BlockType { - Import, - Charset, - Namespace, - Supports, - Keyframes, - MediaQuery, - Selector, - FontFace, - Page, - Document, - Viewport, - Unsupported -} - -export interface CssAstVisitor { - visitCssValue(ast: CssStyleValueAst, context?: any): any; - visitCssInlineRule(ast: CssInlineRuleAst, context?: any): any; - visitCssAtRulePredicate(ast: CssAtRulePredicateAst, context?: any): any; - visitCssKeyframeRule(ast: CssKeyframeRuleAst, context?: any): any; - visitCssKeyframeDefinition(ast: CssKeyframeDefinitionAst, context?: any): any; - visitCssMediaQueryRule(ast: CssMediaQueryRuleAst, context?: any): any; - visitCssSelectorRule(ast: CssSelectorRuleAst, context?: any): any; - visitCssSelector(ast: CssSelectorAst, context?: any): any; - visitCssSimpleSelector(ast: CssSimpleSelectorAst, context?: any): any; - visitCssPseudoSelector(ast: CssPseudoSelectorAst, context?: any): any; - visitCssDefinition(ast: CssDefinitionAst, context?: any): any; - visitCssBlock(ast: CssBlockAst, context?: any): any; - visitCssStylesBlock(ast: CssStylesBlockAst, context?: any): any; - visitCssStyleSheet(ast: CssStyleSheetAst, context?: any): any; - visitCssUnknownRule(ast: CssUnknownRuleAst, context?: any): any; - visitCssUnknownTokenList(ast: CssUnknownTokenListAst, context?: any): any; -} - -export abstract class CssAst { - constructor(public location: ParseSourceSpan) {} - get start(): ParseLocation { - return this.location.start; - } - get end(): ParseLocation { - return this.location.end; - } - abstract visit(visitor: CssAstVisitor, context?: any): any; -} - -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); - } -} - -export abstract class CssRuleAst extends CssAst { - constructor(location: ParseSourceSpan) { - super(location); - } -} - -export class CssBlockRuleAst extends CssRuleAst { - constructor( - public location: ParseSourceSpan, public type: BlockType, public block: CssBlockAst, - public name: CssToken|null = null) { - super(location); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssBlock(this.block, context); - } -} - -export class CssKeyframeRuleAst extends CssBlockRuleAst { - constructor(location: ParseSourceSpan, name: CssToken, block: CssBlockAst) { - super(location, BlockType.Keyframes, block, name); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssKeyframeRule(this, context); - } -} - -export class CssKeyframeDefinitionAst extends CssBlockRuleAst { - constructor(location: ParseSourceSpan, public steps: CssToken[], block: CssBlockAst) { - super(location, BlockType.Keyframes, block, mergeTokens(steps, ',')); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssKeyframeDefinition(this, context); - } -} - -export class CssBlockDefinitionRuleAst extends CssBlockRuleAst { - constructor( - location: ParseSourceSpan, public strValue: string, type: BlockType, - public query: CssAtRulePredicateAst, block: CssBlockAst) { - super(location, type, block); - const firstCssToken: CssToken = query.tokens[0]; - this.name = new CssToken( - firstCssToken.index, firstCssToken.column, firstCssToken.line, CssTokenType.Identifier, - this.strValue); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssBlock(this.block, context); - } -} - -export class CssMediaQueryRuleAst extends CssBlockDefinitionRuleAst { - constructor( - location: ParseSourceSpan, strValue: string, query: CssAtRulePredicateAst, - block: CssBlockAst) { - super(location, strValue, BlockType.MediaQuery, query, block); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssMediaQueryRule(this, context); - } -} - -export class CssAtRulePredicateAst extends CssAst { - constructor(location: ParseSourceSpan, public strValue: string, public tokens: CssToken[]) { - super(location); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssAtRulePredicate(this, context); - } -} - -export class CssInlineRuleAst extends CssRuleAst { - constructor(location: ParseSourceSpan, public type: BlockType, public value: CssStyleValueAst) { - super(location); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssInlineRule(this, context); - } -} - -export class CssSelectorRuleAst extends CssBlockRuleAst { - public strValue: string; - - constructor(location: ParseSourceSpan, public selectors: CssSelectorAst[], block: CssBlockAst) { - super(location, BlockType.Selector, block); - this.strValue = selectors.map(selector => selector.strValue).join(','); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssSelectorRule(this, context); - } -} - -export class CssDefinitionAst extends CssAst { - constructor( - location: ParseSourceSpan, public property: CssToken, public value: CssStyleValueAst) { - super(location); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssDefinition(this, context); - } -} - -export abstract class CssSelectorPartAst extends CssAst { - constructor(location: ParseSourceSpan) { - super(location); - } -} - -export class CssSelectorAst extends CssSelectorPartAst { - public strValue: string; - constructor(location: ParseSourceSpan, public selectorParts: CssSimpleSelectorAst[]) { - super(location); - this.strValue = selectorParts.map(part => part.strValue).join(''); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssSelector(this, context); - } -} - -export class CssSimpleSelectorAst extends CssSelectorPartAst { - constructor( - location: ParseSourceSpan, public tokens: CssToken[], public strValue: string, - public pseudoSelectors: CssPseudoSelectorAst[], public operator: CssToken) { - super(location); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssSimpleSelector(this, context); - } -} - -export class CssPseudoSelectorAst extends CssSelectorPartAst { - constructor( - location: ParseSourceSpan, public strValue: string, public name: string, - public tokens: CssToken[], public innerSelectors: CssSelectorAst[]) { - super(location); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssPseudoSelector(this, context); - } -} - -export class CssBlockAst extends CssAst { - constructor(location: ParseSourceSpan, public entries: CssAst[]) { - super(location); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssBlock(this, context); - } -} - -/* - a style block is different from a standard block because it contains - css prop:value definitions. A regular block can contain a list of Ast entries. - */ -export class CssStylesBlockAst extends CssBlockAst { - constructor(location: ParseSourceSpan, public definitions: CssDefinitionAst[]) { - super(location, definitions); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssStylesBlock(this, context); - } -} - -export class CssStyleSheetAst extends CssAst { - constructor(location: ParseSourceSpan, public rules: CssAst[]) { - super(location); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssStyleSheet(this, context); - } -} - -export class CssUnknownRuleAst extends CssRuleAst { - constructor(location: ParseSourceSpan, public ruleName: string, public tokens: CssToken[]) { - super(location); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssUnknownRule(this, context); - } -} - -export class CssUnknownTokenListAst extends CssRuleAst { - constructor(location: ParseSourceSpan, public name: string, public tokens: CssToken[]) { - super(location); - } - visit(visitor: CssAstVisitor, context?: any): any { - return visitor.visitCssUnknownTokenList(this, context); - } -} - -export function mergeTokens(tokens: CssToken[], separator: string = ''): CssToken { - const mainToken = tokens[0]; - let str = mainToken.strValue; - for (let i = 1; i < tokens.length; i++) { - str += separator + tokens[i].strValue; - } - - return new CssToken(mainToken.index, mainToken.column, mainToken.line, mainToken.type, str); -} diff --git a/packages/compiler/src/css_parser/css_lexer.ts b/packages/compiler/src/css_parser/css_lexer.ts deleted file mode 100644 index c9142e8143..0000000000 --- a/packages/compiler/src/css_parser/css_lexer.ts +++ /dev/null @@ -1,725 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - - -import * as chars from '../chars'; - -export enum CssTokenType { - EOF, - String, - Comment, - Identifier, - Number, - IdentifierOrNumber, - AtKeyword, - Character, - Whitespace, - Invalid -} - -export enum CssLexerMode { - ALL, - ALL_TRACK_WS, - SELECTOR, - PSEUDO_SELECTOR, - PSEUDO_SELECTOR_WITH_ARGUMENTS, - ATTRIBUTE_SELECTOR, - AT_RULE_QUERY, - MEDIA_QUERY, - BLOCK, - KEYFRAME_BLOCK, - STYLE_BLOCK, - STYLE_VALUE, - STYLE_VALUE_FUNCTION, - STYLE_CALC_FUNCTION -} - -export class LexedCssResult { - constructor(public error: Error|null, public token: CssToken) {} -} - -export function generateErrorMessage( - input: string, message: string, errorValue: string, index: number, row: number, - column: number): string { - return `${message} at column ${row}:${column} in expression [` + - findProblemCode(input, errorValue, index, column) + ']'; -} - -export function findProblemCode( - input: string, errorValue: string, index: number, column: number): string { - let endOfProblemLine = index; - let current = charCode(input, index); - while (current > 0 && !isNewline(current)) { - current = charCode(input, ++endOfProblemLine); - } - const choppedString = input.substring(0, endOfProblemLine); - let pointerPadding = ''; - for (let i = 0; i < column; i++) { - pointerPadding += ' '; - } - let pointerString = ''; - for (let i = 0; i < errorValue.length; i++) { - pointerString += '^'; - } - return choppedString + '\n' + pointerPadding + pointerString + '\n'; -} - -export class CssToken { - numValue: number; - constructor( - public index: number, public column: number, public line: number, public type: CssTokenType, - public strValue: string) { - this.numValue = charCode(strValue, 0); - } -} - -export class CssLexer { - scan(text: string, trackComments: boolean = false): CssScanner { - return new CssScanner(text, trackComments); - } -} - -export function cssScannerError(token: CssToken, message: string): Error { - const error = Error('CssParseError: ' + message); - (error as any)[ERROR_RAW_MESSAGE] = message; - (error as any)[ERROR_TOKEN] = token; - return error; -} - -const ERROR_TOKEN = 'ngToken'; -const ERROR_RAW_MESSAGE = 'ngRawMessage'; - -export function getRawMessage(error: Error): string { - return (error as any)[ERROR_RAW_MESSAGE]; -} - -export function getToken(error: Error): CssToken { - return (error as any)[ERROR_TOKEN]; -} - -function _trackWhitespace(mode: CssLexerMode) { - switch (mode) { - case CssLexerMode.SELECTOR: - case CssLexerMode.PSEUDO_SELECTOR: - case CssLexerMode.ALL_TRACK_WS: - case CssLexerMode.STYLE_VALUE: - return true; - - default: - return false; - } -} - -export class CssScanner { - // TODO(issue/24571): remove '!'. - peek!: number; - peekPeek: number; - length: number = 0; - index: number = -1; - column: number = -1; - line: number = 0; - - /** @internal */ - _currentMode: CssLexerMode = CssLexerMode.BLOCK; - /** @internal */ - _currentError: Error|null = null; - - constructor(public input: string, private _trackComments: boolean = false) { - this.length = this.input.length; - this.peekPeek = this.peekAt(0); - this.advance(); - } - - getMode(): CssLexerMode { - return this._currentMode; - } - - setMode(mode: CssLexerMode) { - if (this._currentMode != mode) { - if (_trackWhitespace(this._currentMode) && !_trackWhitespace(mode)) { - this.consumeWhitespace(); - } - this._currentMode = mode; - } - } - - advance(): void { - if (isNewline(this.peek)) { - this.column = 0; - this.line++; - } else { - this.column++; - } - - this.index++; - this.peek = this.peekPeek; - this.peekPeek = this.peekAt(this.index + 1); - } - - peekAt(index: number): number { - return index >= this.length ? chars.$EOF : this.input.charCodeAt(index); - } - - consumeEmptyStatements(): void { - this.consumeWhitespace(); - while (this.peek == chars.$SEMICOLON) { - this.advance(); - this.consumeWhitespace(); - } - } - - consumeWhitespace(): void { - while (chars.isWhitespace(this.peek) || isNewline(this.peek)) { - this.advance(); - if (!this._trackComments && isCommentStart(this.peek, this.peekPeek)) { - this.advance(); // / - this.advance(); // * - while (!isCommentEnd(this.peek, this.peekPeek)) { - if (this.peek == chars.$EOF) { - this.error('Unterminated comment'); - } - this.advance(); - } - this.advance(); // * - this.advance(); // / - } - } - } - - consume(type: CssTokenType, value: string|null = null): LexedCssResult { - const mode = this._currentMode; - - this.setMode(_trackWhitespace(mode) ? CssLexerMode.ALL_TRACK_WS : CssLexerMode.ALL); - - const previousIndex = this.index; - const previousLine = this.line; - const previousColumn = this.column; - - let next: CssToken = undefined!; - const output = this.scan(); - if (output != null) { - // just incase the inner scan method returned an error - if (output.error != null) { - this.setMode(mode); - return output; - } - - next = output.token; - } - - if (next == null) { - next = new CssToken(this.index, this.column, this.line, CssTokenType.EOF, 'end of file'); - } - - let isMatchingType: boolean = false; - if (type == CssTokenType.IdentifierOrNumber) { - // TODO (matsko): implement array traversal for lookup here - isMatchingType = next.type == CssTokenType.Number || next.type == CssTokenType.Identifier; - } else { - isMatchingType = next.type == type; - } - - // before throwing the error we need to bring back the former - // mode so that the parser can recover... - this.setMode(mode); - - let error: Error|null = null; - if (!isMatchingType || (value != null && value != next.strValue)) { - let errorMessage = - CssTokenType[next.type] + ' does not match expected ' + CssTokenType[type] + ' value'; - - if (value != null) { - errorMessage += ' ("' + next.strValue + '" should match "' + value + '")'; - } - - error = cssScannerError( - next, - generateErrorMessage( - this.input, errorMessage, next.strValue, previousIndex, previousLine, - previousColumn)); - } - - return new LexedCssResult(error, next); - } - - - scan(): LexedCssResult|null { - const trackWS = _trackWhitespace(this._currentMode); - if (this.index == 0 && !trackWS) { // first scan - this.consumeWhitespace(); - } - - const token = this._scan(); - if (token == null) return null; - - const error = this._currentError!; - this._currentError = null; - - if (!trackWS) { - this.consumeWhitespace(); - } - return new LexedCssResult(error, token); - } - - /** @internal */ - _scan(): CssToken|null { - let peek = this.peek; - let peekPeek = this.peekPeek; - if (peek == chars.$EOF) return null; - - if (isCommentStart(peek, peekPeek)) { - // even if comments are not tracked we still lex the - // comment so we can move the pointer forward - const commentToken = this.scanComment(); - if (this._trackComments) { - return commentToken; - } - } - - if (_trackWhitespace(this._currentMode) && (chars.isWhitespace(peek) || isNewline(peek))) { - return this.scanWhitespace(); - } - - peek = this.peek; - peekPeek = this.peekPeek; - if (peek == chars.$EOF) return null; - - if (isStringStart(peek, peekPeek)) { - return this.scanString(); - } - - // something like url(cool) - if (this._currentMode == CssLexerMode.STYLE_VALUE_FUNCTION) { - return this.scanCssValueFunction(); - } - - const isModifier = peek == chars.$PLUS || peek == chars.$MINUS; - const digitA = isModifier ? false : chars.isDigit(peek); - const digitB = chars.isDigit(peekPeek); - if (digitA || (isModifier && (peekPeek == chars.$PERIOD || digitB)) || - (peek == chars.$PERIOD && digitB)) { - return this.scanNumber(); - } - - if (peek == chars.$AT) { - return this.scanAtExpression(); - } - - if (isIdentifierStart(peek, peekPeek)) { - return this.scanIdentifier(); - } - - if (isValidCssCharacter(peek, this._currentMode)) { - return this.scanCharacter(); - } - - return this.error(`Unexpected character [${String.fromCharCode(peek)}]`); - } - - scanComment(): CssToken|null { - if (this.assertCondition( - isCommentStart(this.peek, this.peekPeek), 'Expected comment start value')) { - return null; - } - - const start = this.index; - const startingColumn = this.column; - const startingLine = this.line; - - this.advance(); // / - this.advance(); // * - - while (!isCommentEnd(this.peek, this.peekPeek)) { - if (this.peek == chars.$EOF) { - this.error('Unterminated comment'); - } - this.advance(); - } - - this.advance(); // * - this.advance(); // / - - const str = this.input.substring(start, this.index); - return new CssToken(start, startingColumn, startingLine, CssTokenType.Comment, str); - } - - scanWhitespace(): CssToken { - const start = this.index; - const startingColumn = this.column; - const startingLine = this.line; - while (chars.isWhitespace(this.peek) && this.peek != chars.$EOF) { - this.advance(); - } - const str = this.input.substring(start, this.index); - return new CssToken(start, startingColumn, startingLine, CssTokenType.Whitespace, str); - } - - scanString(): CssToken|null { - if (this.assertCondition( - isStringStart(this.peek, this.peekPeek), 'Unexpected non-string starting value')) { - return null; - } - - const target = this.peek; - const start = this.index; - const startingColumn = this.column; - const startingLine = this.line; - let previous = target; - this.advance(); - - while (!isCharMatch(target, previous, this.peek)) { - if (this.peek == chars.$EOF || isNewline(this.peek)) { - this.error('Unterminated quote'); - } - previous = this.peek; - this.advance(); - } - - if (this.assertCondition(this.peek == target, 'Unterminated quote')) { - return null; - } - this.advance(); - - const str = this.input.substring(start, this.index); - return new CssToken(start, startingColumn, startingLine, CssTokenType.String, str); - } - - scanNumber(): CssToken { - const start = this.index; - const startingColumn = this.column; - if (this.peek == chars.$PLUS || this.peek == chars.$MINUS) { - this.advance(); - } - let periodUsed = false; - while (chars.isDigit(this.peek) || this.peek == chars.$PERIOD) { - if (this.peek == chars.$PERIOD) { - if (periodUsed) { - this.error('Unexpected use of a second period value'); - } - periodUsed = true; - } - this.advance(); - } - const strValue = this.input.substring(start, this.index); - return new CssToken(start, startingColumn, this.line, CssTokenType.Number, strValue); - } - - scanIdentifier(): CssToken|null { - if (this.assertCondition( - isIdentifierStart(this.peek, this.peekPeek), 'Expected identifier starting value')) { - return null; - } - - const start = this.index; - const startingColumn = this.column; - while (isIdentifierPart(this.peek)) { - this.advance(); - } - const strValue = this.input.substring(start, this.index); - return new CssToken(start, startingColumn, this.line, CssTokenType.Identifier, strValue); - } - - scanCssValueFunction(): CssToken { - const start = this.index; - const startingColumn = this.column; - let parenBalance = 1; - while (this.peek != chars.$EOF && parenBalance > 0) { - this.advance(); - if (this.peek == chars.$LPAREN) { - parenBalance++; - } else if (this.peek == chars.$RPAREN) { - parenBalance--; - } - } - const strValue = this.input.substring(start, this.index); - return new CssToken(start, startingColumn, this.line, CssTokenType.Identifier, strValue); - } - - scanCharacter(): CssToken|null { - const start = this.index; - const startingColumn = this.column; - if (this.assertCondition( - isValidCssCharacter(this.peek, this._currentMode), - charStr(this.peek) + ' is not a valid CSS character')) { - return null; - } - - const c = this.input.substring(start, start + 1); - this.advance(); - - return new CssToken(start, startingColumn, this.line, CssTokenType.Character, c); - } - - scanAtExpression(): CssToken|null { - if (this.assertCondition(this.peek == chars.$AT, 'Expected @ value')) { - return null; - } - - const start = this.index; - const startingColumn = this.column; - this.advance(); - if (isIdentifierStart(this.peek, this.peekPeek)) { - const ident = this.scanIdentifier()!; - const strValue = '@' + ident.strValue; - return new CssToken(start, startingColumn, this.line, CssTokenType.AtKeyword, strValue); - } else { - return this.scanCharacter(); - } - } - - assertCondition(status: boolean, errorMessage: string): boolean { - if (!status) { - this.error(errorMessage); - return true; - } - return false; - } - - error(message: string, errorTokenValue: string|null = null, doNotAdvance: boolean = false): - CssToken { - const index: number = this.index; - const column: number = this.column; - const line: number = this.line; - errorTokenValue = errorTokenValue || String.fromCharCode(this.peek); - const invalidToken = new CssToken(index, column, line, CssTokenType.Invalid, errorTokenValue); - const errorMessage = - generateErrorMessage(this.input, message, errorTokenValue, index, line, column); - if (!doNotAdvance) { - this.advance(); - } - this._currentError = cssScannerError(invalidToken, errorMessage); - return invalidToken; - } -} - -function isCharMatch(target: number, previous: number, code: number): boolean { - return code == target && previous != chars.$BACKSLASH; -} - -function isCommentStart(code: number, next: number): boolean { - return code == chars.$SLASH && next == chars.$STAR; -} - -function isCommentEnd(code: number, next: number): boolean { - return code == chars.$STAR && next == chars.$SLASH; -} - -function isStringStart(code: number, next: number): boolean { - let target = code; - if (target == chars.$BACKSLASH) { - target = next; - } - return target == chars.$DQ || target == chars.$SQ; -} - -function isIdentifierStart(code: number, next: number): boolean { - let target = code; - if (target == chars.$MINUS) { - target = next; - } - - return chars.isAsciiLetter(target) || target == chars.$BACKSLASH || target == chars.$MINUS || - target == chars.$_; -} - -function isIdentifierPart(target: number): boolean { - return chars.isAsciiLetter(target) || target == chars.$BACKSLASH || target == chars.$MINUS || - target == chars.$_ || chars.isDigit(target); -} - -function isValidPseudoSelectorCharacter(code: number): boolean { - switch (code) { - case chars.$LPAREN: - case chars.$RPAREN: - return true; - default: - return false; - } -} - -function isValidKeyframeBlockCharacter(code: number): boolean { - return code == chars.$PERCENT; -} - -function isValidAttributeSelectorCharacter(code: number): boolean { - // value^*|$~=something - switch (code) { - case chars.$$: - case chars.$PIPE: - case chars.$CARET: - case chars.$TILDA: - case chars.$STAR: - case chars.$EQ: - return true; - default: - return false; - } -} - -function isValidSelectorCharacter(code: number): boolean { - // selector [ key = value ] - // IDENT C IDENT C IDENT C - // #id, .class, *+~> - // tag:PSEUDO - switch (code) { - case chars.$HASH: - case chars.$PERIOD: - case chars.$TILDA: - case chars.$STAR: - case chars.$PLUS: - case chars.$GT: - case chars.$COLON: - case chars.$PIPE: - case chars.$COMMA: - case chars.$LBRACKET: - case chars.$RBRACKET: - return true; - default: - return false; - } -} - -function isValidStyleBlockCharacter(code: number): boolean { - // key:value; - // key:calc(something ... ) - switch (code) { - case chars.$HASH: - case chars.$SEMICOLON: - case chars.$COLON: - case chars.$PERCENT: - case chars.$SLASH: - case chars.$BACKSLASH: - case chars.$BANG: - case chars.$PERIOD: - case chars.$LPAREN: - case chars.$RPAREN: - return true; - default: - return false; - } -} - -function isValidMediaQueryRuleCharacter(code: number): boolean { - // (min-width: 7.5em) and (orientation: landscape) - switch (code) { - case chars.$LPAREN: - case chars.$RPAREN: - case chars.$COLON: - case chars.$PERCENT: - case chars.$PERIOD: - return true; - default: - return false; - } -} - -function isValidAtRuleCharacter(code: number): boolean { - // @document url(http://www.w3.org/page?something=on#hash), - switch (code) { - case chars.$LPAREN: - case chars.$RPAREN: - case chars.$COLON: - case chars.$PERCENT: - case chars.$PERIOD: - case chars.$SLASH: - case chars.$BACKSLASH: - case chars.$HASH: - case chars.$EQ: - case chars.$QUESTION: - case chars.$AMPERSAND: - case chars.$STAR: - case chars.$COMMA: - case chars.$MINUS: - case chars.$PLUS: - return true; - default: - return false; - } -} - -function isValidStyleFunctionCharacter(code: number): boolean { - switch (code) { - case chars.$PERIOD: - case chars.$MINUS: - case chars.$PLUS: - case chars.$STAR: - case chars.$SLASH: - case chars.$LPAREN: - case chars.$RPAREN: - case chars.$COMMA: - return true; - default: - return false; - } -} - -function isValidBlockCharacter(code: number): boolean { - // @something { } - // IDENT - return code == chars.$AT; -} - -function isValidCssCharacter(code: number, mode: CssLexerMode): boolean { - switch (mode) { - case CssLexerMode.ALL: - case CssLexerMode.ALL_TRACK_WS: - return true; - - case CssLexerMode.SELECTOR: - return isValidSelectorCharacter(code); - - case CssLexerMode.PSEUDO_SELECTOR_WITH_ARGUMENTS: - return isValidPseudoSelectorCharacter(code); - - case CssLexerMode.ATTRIBUTE_SELECTOR: - return isValidAttributeSelectorCharacter(code); - - case CssLexerMode.MEDIA_QUERY: - return isValidMediaQueryRuleCharacter(code); - - case CssLexerMode.AT_RULE_QUERY: - return isValidAtRuleCharacter(code); - - case CssLexerMode.KEYFRAME_BLOCK: - return isValidKeyframeBlockCharacter(code); - - case CssLexerMode.STYLE_BLOCK: - case CssLexerMode.STYLE_VALUE: - return isValidStyleBlockCharacter(code); - - case CssLexerMode.STYLE_CALC_FUNCTION: - return isValidStyleFunctionCharacter(code); - - case CssLexerMode.BLOCK: - return isValidBlockCharacter(code); - - default: - return false; - } -} - -function charCode(input: string, index: number): number { - return index >= input.length ? chars.$EOF : input.charCodeAt(index); -} - -function charStr(code: number): string { - return String.fromCharCode(code); -} - -export function isNewline(code: number): boolean { - switch (code) { - case chars.$FF: - case chars.$CR: - case chars.$LF: - case chars.$VTAB: - return true; - - default: - return false; - } -} diff --git a/packages/compiler/src/css_parser/css_parser.ts b/packages/compiler/src/css_parser/css_parser.ts deleted file mode 100644 index 64ba7752d5..0000000000 --- a/packages/compiler/src/css_parser/css_parser.ts +++ /dev/null @@ -1,918 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -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, CssStylesBlockAst, CssStyleSheetAst, CssStyleValueAst, CssUnknownRuleAst, CssUnknownTokenListAst, mergeTokens} from './css_ast'; -import {CssLexer, CssLexerMode, CssScanner, CssToken, CssTokenType, generateErrorMessage, getRawMessage, isNewline} from './css_lexer'; - -const SPACE_OPERATOR = ' '; - -export {CssToken} from './css_lexer'; -export {BlockType} from './css_ast'; - -const SLASH_CHARACTER = '/'; -const GT_CHARACTER = '>'; -const TRIPLE_GT_OPERATOR_STR = '>>>'; -const DEEP_OPERATOR_STR = '/deep/'; - -const EOF_DELIM_FLAG = 1; -const RBRACE_DELIM_FLAG = 2; -const LBRACE_DELIM_FLAG = 4; -const COMMA_DELIM_FLAG = 8; -const COLON_DELIM_FLAG = 16; -const SEMICOLON_DELIM_FLAG = 32; -const NEWLINE_DELIM_FLAG = 64; -const RPAREN_DELIM_FLAG = 128; -const LPAREN_DELIM_FLAG = 256; -const SPACE_DELIM_FLAG = 512; - -function _pseudoSelectorSupportsInnerSelectors(name: string): boolean { - return ['not', 'host', 'host-context'].indexOf(name) >= 0; -} - -function isSelectorOperatorCharacter(code: number): boolean { - switch (code) { - case chars.$SLASH: - case chars.$TILDA: - case chars.$PLUS: - case chars.$GT: - return true; - default: - return chars.isWhitespace(code); - } -} - -function getDelimFromCharacter(code: number): number { - switch (code) { - case chars.$EOF: - return EOF_DELIM_FLAG; - case chars.$COMMA: - return COMMA_DELIM_FLAG; - case chars.$COLON: - return COLON_DELIM_FLAG; - case chars.$SEMICOLON: - return SEMICOLON_DELIM_FLAG; - case chars.$RBRACE: - return RBRACE_DELIM_FLAG; - case chars.$LBRACE: - return LBRACE_DELIM_FLAG; - case chars.$RPAREN: - return RPAREN_DELIM_FLAG; - case chars.$SPACE: - case chars.$TAB: - return SPACE_DELIM_FLAG; - default: - return isNewline(code) ? NEWLINE_DELIM_FLAG : 0; - } -} - -function characterContainsDelimiter(code: number, delimiters: number): boolean { - return (getDelimFromCharacter(code) & delimiters) > 0; -} - -export class ParsedCssResult { - constructor(public errors: CssParseError[], public ast: CssStyleSheetAst) {} -} - -export class CssParser { - private _errors: CssParseError[] = []; - // TODO(issue/24571): remove '!'. - private _file!: ParseSourceFile; - // TODO(issue/24571): remove '!'. - private _scanner!: CssScanner; - // TODO(issue/24571): remove '!'. - private _lastToken!: CssToken; - - /** - * @param css the CSS code that will be parsed - * @param url the name of the CSS file containing the CSS source code - */ - parse(css: string, url: string): ParsedCssResult { - const lexer = new CssLexer(); - this._file = new ParseSourceFile(css, url); - this._scanner = lexer.scan(css, false); - - const ast = this._parseStyleSheet(EOF_DELIM_FLAG); - - const errors = this._errors; - this._errors = []; - - const result = new ParsedCssResult(errors, ast); - this._file = null as any; - this._scanner = null as any; - return result; - } - - /** @internal */ - _parseStyleSheet(delimiters: number): CssStyleSheetAst { - const results: CssRuleAst[] = []; - this._scanner.consumeEmptyStatements(); - while (this._scanner.peek != chars.$EOF) { - this._scanner.setMode(CssLexerMode.BLOCK); - results.push(this._parseRule(delimiters)); - } - let span: ParseSourceSpan|null = null; - if (results.length > 0) { - const firstRule = results[0]; - // we collect the last token like so incase there was an - // EOF token that was emitted sometime during the lexing - span = this._generateSourceSpan(firstRule, this._lastToken); - } - return new CssStyleSheetAst(span!, results); - } - - /** @internal */ - _getSourceContent(): string { - return this._scanner != null ? this._scanner.input : ''; - } - - /** @internal */ - _extractSourceContent(start: number, end: number): string { - return this._getSourceContent().substring(start, end + 1); - } - - /** @internal */ - _generateSourceSpan(start: CssToken|CssAst, end: CssToken|CssAst|null = null): ParseSourceSpan { - let startLoc: ParseLocation; - if (start instanceof CssAst) { - startLoc = start.location.start; - } else { - let token = start; - if (token == null) { - // the data here is invalid, however, if and when this does - // occur, any other errors associated with this will be collected - token = this._lastToken; - } - startLoc = new ParseLocation(this._file, token.index, token.line, token.column); - } - - if (end == null) { - end = this._lastToken; - } - - let endLine: number = -1; - 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!; - } else if (end instanceof CssToken) { - endLine = end.line; - endColumn = end.column; - endIndex = end.index; - } - - const endLoc = new ParseLocation(this._file, endIndex, endLine, endColumn); - return new ParseSourceSpan(startLoc, endLoc); - } - - /** @internal */ - _resolveBlockType(token: CssToken): BlockType { - switch (token.strValue) { - case '@-o-keyframes': - case '@-moz-keyframes': - case '@-webkit-keyframes': - case '@keyframes': - return BlockType.Keyframes; - - case '@charset': - return BlockType.Charset; - - case '@import': - return BlockType.Import; - - case '@namespace': - return BlockType.Namespace; - - case '@page': - return BlockType.Page; - - case '@document': - return BlockType.Document; - - case '@media': - return BlockType.MediaQuery; - - case '@font-face': - return BlockType.FontFace; - - case '@viewport': - return BlockType.Viewport; - - case '@supports': - return BlockType.Supports; - - default: - return BlockType.Unsupported; - } - } - - /** @internal */ - _parseRule(delimiters: number): CssRuleAst { - if (this._scanner.peek == chars.$AT) { - return this._parseAtRule(delimiters); - } - return this._parseSelectorRule(delimiters); - } - - /** @internal */ - _parseAtRule(delimiters: number): CssRuleAst { - const start = this._getScannerIndex(); - - this._scanner.setMode(CssLexerMode.BLOCK); - const token = this._scan(); - const startToken = token; - - this._assertCondition( - token.type == CssTokenType.AtKeyword, - `The CSS Rule ${token.strValue} is not a valid [@] rule.`, token); - - let block: CssBlockAst; - const type = this._resolveBlockType(token); - let span: ParseSourceSpan; - let tokens: CssToken[]; - let endToken: CssToken; - let end: number; - let strValue: string; - let query: CssAtRulePredicateAst; - switch (type) { - case BlockType.Charset: - case BlockType.Namespace: - case BlockType.Import: - let value = this._parseValue(delimiters); - this._scanner.setMode(CssLexerMode.BLOCK); - this._scanner.consumeEmptyStatements(); - span = this._generateSourceSpan(startToken, value); - return new CssInlineRuleAst(span, type, value); - - case BlockType.Viewport: - case BlockType.FontFace: - block = this._parseStyleBlock(delimiters)!; - span = this._generateSourceSpan(startToken, block); - return new CssBlockRuleAst(span, type, block); - - case BlockType.Keyframes: - tokens = this._collectUntilDelim(delimiters | RBRACE_DELIM_FLAG | LBRACE_DELIM_FLAG); - // keyframes only have one identifier name - let name = tokens[0]; - block = this._parseKeyframeBlock(delimiters); - span = this._generateSourceSpan(startToken, block); - return new CssKeyframeRuleAst(span, name, block); - - case BlockType.MediaQuery: - this._scanner.setMode(CssLexerMode.MEDIA_QUERY); - tokens = this._collectUntilDelim(delimiters | RBRACE_DELIM_FLAG | LBRACE_DELIM_FLAG); - endToken = tokens[tokens.length - 1]; - // we do not track the whitespace after the mediaQuery predicate ends - // so we have to calculate the end string value on our own - end = endToken.index + endToken.strValue.length - 1; - strValue = this._extractSourceContent(start, end); - span = this._generateSourceSpan(startToken, endToken); - query = new CssAtRulePredicateAst(span, strValue, tokens); - block = this._parseBlock(delimiters); - strValue = this._extractSourceContent(start, this._getScannerIndex() - 1); - span = this._generateSourceSpan(startToken, block); - return new CssMediaQueryRuleAst(span, strValue, query, block); - - case BlockType.Document: - case BlockType.Supports: - case BlockType.Page: - this._scanner.setMode(CssLexerMode.AT_RULE_QUERY); - tokens = this._collectUntilDelim(delimiters | RBRACE_DELIM_FLAG | LBRACE_DELIM_FLAG); - endToken = tokens[tokens.length - 1]; - // we do not track the whitespace after this block rule predicate ends - // so we have to calculate the end string value on our own - end = endToken.index + endToken.strValue.length - 1; - strValue = this._extractSourceContent(start, end); - 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!); - span = this._generateSourceSpan(startToken, block); - return new CssBlockDefinitionRuleAst(span, strValue, type, query, block); - - // if a custom @rule { ... } is used it should still tokenize the insides - default: - let listOfTokens: CssToken[] = []; - let tokenName = token.strValue; - this._scanner.setMode(CssLexerMode.ALL); - this._error( - generateErrorMessage( - this._getSourceContent(), - `The CSS "at" rule "${tokenName}" is not allowed to used here`, token.strValue, - token.index, token.line, token.column), - token); - - this._collectUntilDelim(delimiters | LBRACE_DELIM_FLAG | SEMICOLON_DELIM_FLAG) - .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); - }); - listOfTokens.push(this._consume(CssTokenType.Character, '}')); - } - endToken = listOfTokens[listOfTokens.length - 1]; - span = this._generateSourceSpan(startToken, endToken); - return new CssUnknownRuleAst(span, tokenName, listOfTokens); - } - } - - /** @internal */ - _parseSelectorRule(delimiters: number): CssRuleAst { - const start = this._getScannerIndex(); - const selectors = this._parseSelectors(delimiters); - const block = this._parseStyleBlock(delimiters); - let ruleAst: CssRuleAst; - let span: ParseSourceSpan; - const startSelector = selectors[0]; - if (block != null) { - span = this._generateSourceSpan(startSelector, block); - ruleAst = new CssSelectorRuleAst(span, selectors, block); - } else { - const name = this._extractSourceContent(start, this._getScannerIndex() - 1); - const innerTokens: CssToken[] = []; - selectors.forEach((selector: CssSelectorAst) => { - selector.selectorParts.forEach((part: CssSimpleSelectorAst) => { - part.tokens.forEach((token: CssToken) => { - innerTokens.push(token); - }); - }); - }); - const endToken = innerTokens[innerTokens.length - 1]; - span = this._generateSourceSpan(startSelector, endToken); - ruleAst = new CssUnknownTokenListAst(span, name, innerTokens); - } - this._scanner.setMode(CssLexerMode.BLOCK); - this._scanner.consumeEmptyStatements(); - return ruleAst; - } - - /** @internal */ - _parseSelectors(delimiters: number): CssSelectorAst[] { - delimiters |= LBRACE_DELIM_FLAG | SEMICOLON_DELIM_FLAG; - - const selectors: CssSelectorAst[] = []; - let isParsingSelectors = true; - while (isParsingSelectors) { - selectors.push(this._parseSelector(delimiters)); - - isParsingSelectors = !characterContainsDelimiter(this._scanner.peek, delimiters); - - if (isParsingSelectors) { - this._consume(CssTokenType.Character, ','); - isParsingSelectors = !characterContainsDelimiter(this._scanner.peek, delimiters); - if (isParsingSelectors) { - this._scanner.consumeWhitespace(); - } - } - } - - return selectors; - } - - /** @internal */ - _scan(): CssToken { - const output = this._scanner.scan()!; - const token = output.token; - const error = output.error; - if (error != null) { - this._error(getRawMessage(error), token); - } - this._lastToken = token; - return token; - } - - /** @internal */ - _getScannerIndex(): number { - return this._scanner.index; - } - - /** @internal */ - _consume(type: CssTokenType, value: string|null = null): CssToken { - const output = this._scanner.consume(type, value); - const token = output.token; - const error = output.error; - if (error != null) { - this._error(getRawMessage(error), token); - } - this._lastToken = token; - return token; - } - - /** @internal */ - _parseKeyframeBlock(delimiters: number): CssBlockAst { - delimiters |= RBRACE_DELIM_FLAG; - this._scanner.setMode(CssLexerMode.KEYFRAME_BLOCK); - - const startToken = this._consume(CssTokenType.Character, '{'); - - const definitions: CssKeyframeDefinitionAst[] = []; - while (!characterContainsDelimiter(this._scanner.peek, delimiters)) { - definitions.push(this._parseKeyframeDefinition(delimiters)); - } - - const endToken = this._consume(CssTokenType.Character, '}'); - - const span = this._generateSourceSpan(startToken, endToken); - return new CssBlockAst(span, definitions); - } - - /** @internal */ - _parseKeyframeDefinition(delimiters: number): CssKeyframeDefinitionAst { - const start = this._getScannerIndex(); - const stepTokens: CssToken[] = []; - delimiters |= LBRACE_DELIM_FLAG; - while (!characterContainsDelimiter(this._scanner.peek, delimiters)) { - stepTokens.push(this._parseKeyframeLabel(delimiters | COMMA_DELIM_FLAG)); - if (this._scanner.peek != chars.$LBRACE) { - this._consume(CssTokenType.Character, ','); - } - } - const stylesBlock = this._parseStyleBlock(delimiters | RBRACE_DELIM_FLAG); - const span = this._generateSourceSpan(stepTokens[0], stylesBlock); - const ast = new CssKeyframeDefinitionAst(span, stepTokens, stylesBlock!); - - this._scanner.setMode(CssLexerMode.BLOCK); - return ast; - } - - /** @internal */ - _parseKeyframeLabel(delimiters: number): CssToken { - this._scanner.setMode(CssLexerMode.KEYFRAME_BLOCK); - return mergeTokens(this._collectUntilDelim(delimiters)); - } - - /** @internal */ - _parsePseudoSelector(delimiters: number): CssPseudoSelectorAst { - const start = this._getScannerIndex(); - - delimiters &= ~COMMA_DELIM_FLAG; - - // we keep the original value since we may use it to recurse when :not, :host are used - const startingDelims = delimiters; - - const startToken = this._consume(CssTokenType.Character, ':'); - const tokens = [startToken]; - - if (this._scanner.peek == chars.$COLON) { // ::something - tokens.push(this._consume(CssTokenType.Character, ':')); - } - - const innerSelectors: CssSelectorAst[] = []; - - this._scanner.setMode(CssLexerMode.PSEUDO_SELECTOR); - - // host, host-context, lang, not, nth-child are all identifiers - const pseudoSelectorToken = this._consume(CssTokenType.Identifier); - const pseudoSelectorName = pseudoSelectorToken.strValue; - tokens.push(pseudoSelectorToken); - - // host(), lang(), nth-child(), etc... - if (this._scanner.peek == chars.$LPAREN) { - this._scanner.setMode(CssLexerMode.PSEUDO_SELECTOR_WITH_ARGUMENTS); - - const openParenToken = this._consume(CssTokenType.Character, '('); - tokens.push(openParenToken); - - // :host(innerSelector(s)), :not(selector), etc... - if (_pseudoSelectorSupportsInnerSelectors(pseudoSelectorName)) { - let innerDelims = startingDelims | LPAREN_DELIM_FLAG | RPAREN_DELIM_FLAG; - if (pseudoSelectorName == 'not') { - // the inner selector inside of :not(...) can only be one - // CSS selector (no commas allowed) ... This is according - // to the CSS specification - innerDelims |= COMMA_DELIM_FLAG; - } - - // :host(a, b, c) { - this._parseSelectors(innerDelims).forEach((selector, index) => { - innerSelectors.push(selector); - }); - } else { - // this branch is for things like "en-us, 2k + 1, etc..." - // which all end up in pseudoSelectors like :lang, :nth-child, etc.. - const innerValueDelims = delimiters | LBRACE_DELIM_FLAG | COLON_DELIM_FLAG | - RPAREN_DELIM_FLAG | LPAREN_DELIM_FLAG; - while (!characterContainsDelimiter(this._scanner.peek, innerValueDelims)) { - const token = this._scan(); - tokens.push(token); - } - } - - const closeParenToken = this._consume(CssTokenType.Character, ')'); - tokens.push(closeParenToken); - } - - const end = this._getScannerIndex() - 1; - const strValue = this._extractSourceContent(start, end); - - const endToken = tokens[tokens.length - 1]; - const span = this._generateSourceSpan(startToken, endToken); - return new CssPseudoSelectorAst(span, strValue, pseudoSelectorName, tokens, innerSelectors); - } - - /** @internal */ - _parseSimpleSelector(delimiters: number): CssSimpleSelectorAst { - const start = this._getScannerIndex(); - - delimiters |= COMMA_DELIM_FLAG; - - this._scanner.setMode(CssLexerMode.SELECTOR); - const selectorCssTokens: CssToken[] = []; - const pseudoSelectors: CssPseudoSelectorAst[] = []; - - let previousToken: CssToken = undefined!; - - const selectorPartDelimiters = delimiters | SPACE_DELIM_FLAG; - let loopOverSelector = !characterContainsDelimiter(this._scanner.peek, selectorPartDelimiters); - - let hasAttributeError = false; - while (loopOverSelector) { - const peek = this._scanner.peek; - - switch (peek) { - case chars.$COLON: - let innerPseudo = this._parsePseudoSelector(delimiters); - pseudoSelectors.push(innerPseudo); - this._scanner.setMode(CssLexerMode.SELECTOR); - break; - - case chars.$LBRACKET: - // we set the mode after the scan because attribute mode does not - // allow attribute [] values. And this also will catch any errors - // if an extra "[" is used inside. - selectorCssTokens.push(this._scan()); - this._scanner.setMode(CssLexerMode.ATTRIBUTE_SELECTOR); - break; - - case chars.$RBRACKET: - if (this._scanner.getMode() != CssLexerMode.ATTRIBUTE_SELECTOR) { - hasAttributeError = true; - } - // we set the mode early because attribute mode does not - // allow attribute [] values - this._scanner.setMode(CssLexerMode.SELECTOR); - selectorCssTokens.push(this._scan()); - break; - - default: - if (isSelectorOperatorCharacter(peek)) { - loopOverSelector = false; - continue; - } - - let token = this._scan(); - previousToken = token; - selectorCssTokens.push(token); - break; - } - - loopOverSelector = !characterContainsDelimiter(this._scanner.peek, selectorPartDelimiters); - } - - hasAttributeError = - hasAttributeError || this._scanner.getMode() == CssLexerMode.ATTRIBUTE_SELECTOR; - if (hasAttributeError) { - this._error( - `Unbalanced CSS attribute selector at column ${previousToken.line}:${ - previousToken.column}`, - previousToken); - } - - let end = this._getScannerIndex() - 1; - - // this happens if the selector is not directly followed by - // a comma or curly brace without a space in between - let operator: CssToken|null = null; - let operatorScanCount = 0; - let lastOperatorToken: CssToken|null = null; - if (!characterContainsDelimiter(this._scanner.peek, delimiters)) { - while (operator == null && !characterContainsDelimiter(this._scanner.peek, delimiters) && - isSelectorOperatorCharacter(this._scanner.peek)) { - let token = this._scan(); - const tokenOperator = token.strValue; - operatorScanCount++; - lastOperatorToken = token; - if (tokenOperator != SPACE_OPERATOR) { - switch (tokenOperator) { - case SLASH_CHARACTER: - // /deep/ operator - let deepToken = this._consume(CssTokenType.Identifier); - let deepSlash = this._consume(CssTokenType.Character); - let index = lastOperatorToken.index; - let line = lastOperatorToken.line; - let column = lastOperatorToken.column; - if (deepToken != null && deepToken.strValue.toLowerCase() == 'deep' && - deepSlash.strValue == SLASH_CHARACTER) { - token = new CssToken( - lastOperatorToken.index, lastOperatorToken.column, lastOperatorToken.line, - CssTokenType.Identifier, DEEP_OPERATOR_STR); - } else { - const text = SLASH_CHARACTER + deepToken.strValue + deepSlash.strValue; - this._error( - generateErrorMessage( - this._getSourceContent(), `${text} is an invalid CSS operator`, text, index, - line, column), - lastOperatorToken); - token = new CssToken(index, column, line, CssTokenType.Invalid, text); - } - break; - - case GT_CHARACTER: - // >>> operator - if (this._scanner.peek == chars.$GT && this._scanner.peekPeek == chars.$GT) { - this._consume(CssTokenType.Character, GT_CHARACTER); - this._consume(CssTokenType.Character, GT_CHARACTER); - token = new CssToken( - lastOperatorToken.index, lastOperatorToken.column, lastOperatorToken.line, - CssTokenType.Identifier, TRIPLE_GT_OPERATOR_STR); - } - break; - } - - operator = token; - } - } - - // so long as there is an operator then we can have an - // ending value that is beyond the selector value ... - // otherwise it's just a bunch of trailing whitespace - if (operator != null) { - end = operator.index; - } - } - - this._scanner.consumeWhitespace(); - - const strValue = this._extractSourceContent(start, end); - - // if we do come across one or more spaces inside of - // the operators loop then an empty space is still a - // valid operator to use if something else was not found - if (operator == null && operatorScanCount > 0 && this._scanner.peek != chars.$LBRACE) { - operator = lastOperatorToken; - } - - // please note that `endToken` is reassigned multiple times below - // so please do not optimize the if statements into if/elseif - let startTokenOrAst: CssToken|CssAst|null = null; - let endTokenOrAst: CssToken|CssAst|null = null; - if (selectorCssTokens.length > 0) { - startTokenOrAst = startTokenOrAst || selectorCssTokens[0]; - endTokenOrAst = selectorCssTokens[selectorCssTokens.length - 1]; - } - if (pseudoSelectors.length > 0) { - startTokenOrAst = startTokenOrAst || pseudoSelectors[0]; - endTokenOrAst = pseudoSelectors[pseudoSelectors.length - 1]; - } - if (operator != null) { - startTokenOrAst = startTokenOrAst || operator; - endTokenOrAst = operator; - } - - const span = this._generateSourceSpan(startTokenOrAst!, endTokenOrAst); - return new CssSimpleSelectorAst(span, selectorCssTokens, strValue, pseudoSelectors, operator!); - } - - /** @internal */ - _parseSelector(delimiters: number): CssSelectorAst { - delimiters |= COMMA_DELIM_FLAG; - this._scanner.setMode(CssLexerMode.SELECTOR); - - const simpleSelectors: CssSimpleSelectorAst[] = []; - while (!characterContainsDelimiter(this._scanner.peek, delimiters)) { - simpleSelectors.push(this._parseSimpleSelector(delimiters)); - this._scanner.consumeWhitespace(); - } - - const firstSelector = simpleSelectors[0]; - const lastSelector = simpleSelectors[simpleSelectors.length - 1]; - const span = this._generateSourceSpan(firstSelector, lastSelector); - return new CssSelectorAst(span, simpleSelectors); - } - - /** @internal */ - _parseValue(delimiters: number): CssStyleValueAst { - delimiters |= RBRACE_DELIM_FLAG | SEMICOLON_DELIM_FLAG | NEWLINE_DELIM_FLAG; - - this._scanner.setMode(CssLexerMode.STYLE_VALUE); - const start = this._getScannerIndex(); - - const tokens: CssToken[] = []; - let wsStr = ''; - let previous: CssToken = undefined!; - while (!characterContainsDelimiter(this._scanner.peek, delimiters)) { - let token: CssToken; - if (previous != null && previous.type == CssTokenType.Identifier && - this._scanner.peek == chars.$LPAREN) { - token = this._consume(CssTokenType.Character, '('); - tokens.push(token); - - this._scanner.setMode(CssLexerMode.STYLE_VALUE_FUNCTION); - - token = this._scan(); - tokens.push(token); - - this._scanner.setMode(CssLexerMode.STYLE_VALUE); - - token = this._consume(CssTokenType.Character, ')'); - tokens.push(token); - } else { - token = this._scan(); - if (token.type == CssTokenType.Whitespace) { - wsStr += token.strValue; - } else { - wsStr = ''; - tokens.push(token); - } - } - previous = token; - } - - const end = this._getScannerIndex() - 1; - this._scanner.consumeWhitespace(); - - const code = this._scanner.peek; - if (code == chars.$SEMICOLON) { - this._consume(CssTokenType.Character, ';'); - } else if (code != chars.$RBRACE) { - this._error( - generateErrorMessage( - this._getSourceContent(), `The CSS key/value definition did not end with a semicolon`, - previous.strValue, previous.index, previous.line, previous.column), - previous); - } - - const strValue = this._extractSourceContent(start, end); - const startToken = tokens[0]; - const endToken = tokens[tokens.length - 1]; - const span = this._generateSourceSpan(startToken, endToken); - return new CssStyleValueAst(span, tokens, strValue); - } - - /** @internal */ - _collectUntilDelim(delimiters: number, assertType: CssTokenType|null = null): CssToken[] { - const tokens: CssToken[] = []; - while (!characterContainsDelimiter(this._scanner.peek, delimiters)) { - const val = assertType != null ? this._consume(assertType) : this._scan(); - tokens.push(val); - } - return tokens; - } - - /** @internal */ - _parseBlock(delimiters: number): CssBlockAst { - delimiters |= RBRACE_DELIM_FLAG; - - this._scanner.setMode(CssLexerMode.BLOCK); - - const startToken = this._consume(CssTokenType.Character, '{'); - this._scanner.consumeEmptyStatements(); - - const results: CssRuleAst[] = []; - while (!characterContainsDelimiter(this._scanner.peek, delimiters)) { - results.push(this._parseRule(delimiters)); - } - - const endToken = this._consume(CssTokenType.Character, '}'); - - this._scanner.setMode(CssLexerMode.BLOCK); - this._scanner.consumeEmptyStatements(); - - const span = this._generateSourceSpan(startToken, endToken); - return new CssBlockAst(span, results); - } - - /** @internal */ - _parseStyleBlock(delimiters: number): CssStylesBlockAst|null { - delimiters |= RBRACE_DELIM_FLAG | LBRACE_DELIM_FLAG; - - this._scanner.setMode(CssLexerMode.STYLE_BLOCK); - - const startToken = this._consume(CssTokenType.Character, '{'); - if (startToken.numValue != chars.$LBRACE) { - return null; - } - - const definitions: CssDefinitionAst[] = []; - this._scanner.consumeEmptyStatements(); - - while (!characterContainsDelimiter(this._scanner.peek, delimiters)) { - definitions.push(this._parseDefinition(delimiters)); - this._scanner.consumeEmptyStatements(); - } - - const endToken = this._consume(CssTokenType.Character, '}'); - - this._scanner.setMode(CssLexerMode.STYLE_BLOCK); - this._scanner.consumeEmptyStatements(); - - const span = this._generateSourceSpan(startToken, endToken); - return new CssStylesBlockAst(span, definitions); - } - - /** @internal */ - _parseDefinition(delimiters: number): CssDefinitionAst { - this._scanner.setMode(CssLexerMode.STYLE_BLOCK); - - let prop = this._consume(CssTokenType.Identifier); - let parseValue: boolean = false; - let value: CssStyleValueAst|null = null; - let endToken: CssToken|CssStyleValueAst = prop; - - // the colon value separates the prop from the style. - // there are a few cases as to what could happen if it - // is missing - switch (this._scanner.peek) { - case chars.$SEMICOLON: - case chars.$RBRACE: - case chars.$EOF: - parseValue = false; - break; - - default: - let propStr = [prop.strValue]; - if (this._scanner.peek != chars.$COLON) { - // this will throw the error - const nextValue = this._consume(CssTokenType.Character, ':'); - propStr.push(nextValue.strValue); - - const remainingTokens = this._collectUntilDelim( - delimiters | COLON_DELIM_FLAG | SEMICOLON_DELIM_FLAG, CssTokenType.Identifier); - if (remainingTokens.length > 0) { - remainingTokens.forEach((token) => { - propStr.push(token.strValue); - }); - } - - endToken = prop = - new CssToken(prop.index, prop.column, prop.line, prop.type, propStr.join(' ')); - } - - // this means we've reached the end of the definition and/or block - if (this._scanner.peek == chars.$COLON) { - this._consume(CssTokenType.Character, ':'); - parseValue = true; - } - break; - } - - if (parseValue) { - value = this._parseValue(delimiters); - endToken = value; - } else { - this._error( - generateErrorMessage( - this._getSourceContent(), `The CSS property was not paired with a style value`, - prop.strValue, prop.index, prop.line, prop.column), - prop); - } - - const span = this._generateSourceSpan(prop, endToken); - return new CssDefinitionAst(span, prop, value!); - } - - /** @internal */ - _assertCondition(status: boolean, errorMessage: string, problemToken: CssToken): boolean { - if (!status) { - this._error(errorMessage, problemToken); - return true; - } - return false; - } - - /** @internal */ - _error(message: string, problemToken: CssToken) { - const length = problemToken.strValue.length; - const error = CssParseError.create( - this._file, 0, problemToken.line, problemToken.column, length, message); - this._errors.push(error); - } -} - -export class CssParseError extends ParseError { - static create( - file: ParseSourceFile, offset: number, line: number, col: number, length: number, - errMsg: string): CssParseError { - const start = new ParseLocation(file, offset, line, col); - const end = new ParseLocation(file, offset, line, col + length); - const span = new ParseSourceSpan(start, end); - return new CssParseError(span, 'CSS Parse Error: ' + errMsg); - } - - constructor(span: ParseSourceSpan, message: string) { - super(span, message); - } -} diff --git a/packages/compiler/src/expression_parser/parser.ts b/packages/compiler/src/expression_parser/parser.ts index 958f7e1230..a20d1057ae 100644 --- a/packages/compiler/src/expression_parser/parser.ts +++ b/packages/compiler/src/expression_parser/parser.ts @@ -8,7 +8,6 @@ import * as chars from '../chars'; import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/interpolation_config'; -import {escapeRegExp} from '../util'; 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, RecursiveAstVisitor, SafeMethodCall, SafePropertyRead, TemplateBinding, TemplateBindingIdentifier, ThisReceiver, Unary, VariableBinding} from './ast'; import {EOF, isIdentifier, isQuote, Lexer, Token, TokenType} from './lexer'; @@ -30,20 +29,6 @@ export class TemplateBindingParseResult { public errors: ParserError[]) {} } -const defaultInterpolateRegExp = _createInterpolateRegExp(DEFAULT_INTERPOLATION_CONFIG); -function _getInterpolateRegExp(config: InterpolationConfig): RegExp { - if (config === DEFAULT_INTERPOLATION_CONFIG) { - return defaultInterpolateRegExp; - } else { - return _createInterpolateRegExp(config); - } -} - -function _createInterpolateRegExp(config: InterpolationConfig): RegExp { - const pattern = escapeRegExp(config.start) + '([\\s\\S]*?)' + escapeRegExp(config.end); - return new RegExp(pattern, 'g'); -} - export class Parser { private errors: ParserError[] = []; @@ -244,10 +229,10 @@ export class Parser { atInterpolation = true; } else { - // parse from starting {{ to ending }} + // parse from starting {{ to ending }} while ignoring content inside quotes. const fullStart = i; const exprStart = fullStart + interpStart.length; - const exprEnd = input.indexOf(interpEnd, exprStart); + const exprEnd = this._getInterpolationEndIndex(input, interpEnd, exprStart); if (exprEnd === -1) { // Could not find the end of the interpolation; do not parse an expression. // Instead we should extend the content on the last raw string. @@ -258,14 +243,12 @@ export class Parser { const fullEnd = exprEnd + interpEnd.length; const text = input.substring(exprStart, exprEnd); - if (text.trim().length > 0) { - expressions.push({text, start: fullStart, end: fullEnd}); - } else { + if (text.trim().length === 0) { this._reportError( 'Blank expressions are not allowed in interpolated strings', input, `at column ${i} in`, location); - expressions.push({text: '$implicit', start: fullStart, end: fullEnd}); } + expressions.push({text, start: fullStart, end: fullEnd}); offsets.push(exprStart); i = fullEnd; @@ -315,35 +298,76 @@ export class Parser { return null; } - private _checkNoInterpolation( - input: string, location: string, interpolationConfig: InterpolationConfig): void { - const regexp = _getInterpolateRegExp(interpolationConfig); - const parts = input.split(regexp); - if (parts.length > 1) { + private _checkNoInterpolation(input: string, location: string, {start, end}: InterpolationConfig): + void { + let startIndex = -1; + let endIndex = -1; + + for (const charIndex of this._forEachUnquotedChar(input, 0)) { + if (startIndex === -1) { + if (input.startsWith(start)) { + startIndex = charIndex; + } + } else { + endIndex = this._getInterpolationEndIndex(input, end, charIndex); + if (endIndex > -1) { + break; + } + } + } + + if (startIndex > -1 && endIndex > -1) { this._reportError( - `Got interpolation (${interpolationConfig.start}${ - interpolationConfig.end}) where expression was expected`, - input, - `at column ${this._findInterpolationErrorColumn(parts, 1, interpolationConfig)} in`, - location); + `Got interpolation (${start}${end}) where expression was expected`, input, + `at column ${startIndex} in`, location); } } - private _findInterpolationErrorColumn( - parts: string[], partInErrIdx: number, interpolationConfig: InterpolationConfig): number { - let errLocation = ''; - for (let j = 0; j < partInErrIdx; j++) { - errLocation += j % 2 === 0 ? - parts[j] : - `${interpolationConfig.start}${parts[j]}${interpolationConfig.end}`; + /** + * Finds the index of the end of an interpolation expression + * while ignoring comments and quoted content. + */ + private _getInterpolationEndIndex(input: string, expressionEnd: string, start: number): number { + for (const charIndex of this._forEachUnquotedChar(input, start)) { + if (input.startsWith(expressionEnd, charIndex)) { + return charIndex; + } + + // Nothing else in the expression matters after we've + // hit a comment so look directly for the end token. + if (input.startsWith('//', charIndex)) { + return input.indexOf(expressionEnd, charIndex); + } } - return errLocation.length; + return -1; + } + + /** + * Generator used to iterate over the character indexes of a string that are outside of quotes. + * @param input String to loop through. + * @param start Index within the string at which to start. + */ + private * _forEachUnquotedChar(input: string, start: number) { + let currentQuote: string|null = null; + let escapeCount = 0; + for (let i = start; i < input.length; i++) { + const char = input[i]; + // Skip the characters inside quotes. Note that we only care about the outer-most + // quotes matching up and we need to account for escape characters. + if (isQuote(input.charCodeAt(i)) && (currentQuote === null || currentQuote === char) && + escapeCount % 2 === 0) { + currentQuote = currentQuote === null ? char : null; + } else if (currentQuote === null) { + yield i; + } + escapeCount = char === '\\' ? escapeCount + 1 : 0; + } } } export class IvyParser extends Parser { - simpleExpressionChecker = IvySimpleExpressionChecker; // + simpleExpressionChecker = IvySimpleExpressionChecker; } /** Describes a stateful context an expression parser is in. */ @@ -424,14 +448,27 @@ export class _ParseAST { return this.absoluteOffset + this.inputIndex; } - span(start: number) { - return new ParseSpan(start, this.currentEndIndex); + /** + * Retrieve a `ParseSpan` from `start` to the current position (or to `artificialEndIndex` if + * provided). + * + * @param start Position from which the `ParseSpan` will start. + * @param artificialEndIndex Optional ending index to be used if provided (and if greater than the + * natural ending index) + */ + span(start: number, artificialEndIndex?: number): ParseSpan { + let endIndex = this.currentEndIndex; + if (artificialEndIndex !== undefined && artificialEndIndex > this.currentEndIndex) { + endIndex = artificialEndIndex; + } + return new ParseSpan(start, endIndex); } - sourceSpan(start: number): AbsoluteSourceSpan { - const serial = `${start}@${this.inputIndex}`; + sourceSpan(start: number, artificialEndIndex?: number): AbsoluteSourceSpan { + const serial = `${start}@${this.inputIndex}:${artificialEndIndex}`; if (!this.sourceSpanCache.has(serial)) { - this.sourceSpanCache.set(serial, this.span(start).toAbsolute(this.absoluteOffset)); + this.sourceSpanCache.set( + serial, this.span(start, artificialEndIndex).toAbsolute(this.absoluteOffset)); } return this.sourceSpanCache.get(serial)!; } @@ -495,11 +532,11 @@ export class _ParseAST { return tok === EOF ? 'end of input' : `token ${tok}`; } - expectIdentifierOrKeyword(): string { + expectIdentifierOrKeyword(): string|null { const n = this.next; if (!n.isIdentifier() && !n.isKeyword()) { this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier or keyword`); - return ''; + return null; } this.advance(); return n.toString() as string; @@ -532,12 +569,20 @@ export class _ParseAST { this.error(`Unexpected token '${this.next}'`); } } - if (exprs.length == 0) return new EmptyExpr(this.span(start), this.sourceSpan(start)); + if (exprs.length == 0) { + // We have no expressions so create an empty expression that spans the entire input length + const artificialStart = this.offset; + const artificialEnd = this.offset + this.inputLength; + return new EmptyExpr( + this.span(artificialStart, artificialEnd), + this.sourceSpan(artificialStart, artificialEnd)); + } if (exprs.length == 1) return exprs[0]; return new Chain(this.span(start), this.sourceSpan(start), exprs); } parsePipe(): AST { + const start = this.inputIndex; let result = this.parseExpression(); if (this.consumeOptionalOperator('|')) { if (this.parseAction) { @@ -546,15 +591,39 @@ export class _ParseAST { do { const nameStart = this.inputIndex; - const name = this.expectIdentifierOrKeyword(); - const nameSpan = this.sourceSpan(nameStart); + let nameId = this.expectIdentifierOrKeyword(); + let nameSpan: AbsoluteSourceSpan; + let fullSpanEnd: number|undefined = undefined; + if (nameId !== null) { + nameSpan = this.sourceSpan(nameStart); + } else { + // No valid identifier was found, so we'll assume an empty pipe name (''). + nameId = ''; + + // However, there may have been whitespace present between the pipe character and the next + // token in the sequence (or the end of input). We want to track this whitespace so that + // the `BindingPipe` we produce covers not just the pipe character, but any trailing + // whitespace beyond it. Another way of thinking about this is that the zero-length name + // is assumed to be at the end of any whitespace beyond the pipe character. + // + // Therefore, we push the end of the `ParseSpan` for this pipe all the way up to the + // beginning of the next token, or until the end of input if the next token is EOF. + fullSpanEnd = this.next.index !== -1 ? this.next.index : this.inputLength + this.offset; + + // The `nameSpan` for an empty pipe name is zero-length at the end of any whitespace + // beyond the pipe character. + nameSpan = new ParseSpan(fullSpanEnd, fullSpanEnd).toAbsolute(this.absoluteOffset); + } + const args: AST[] = []; while (this.consumeOptionalCharacter(chars.$COLON)) { args.push(this.parseExpression()); + + // If there are additional expressions beyond the name, then the artificial end for the + // name is no longer relevant. } - const {start} = result.span; - result = - new BindingPipe(this.span(start), this.sourceSpan(start), result, name, args, nameSpan); + result = new BindingPipe( + this.span(start), this.sourceSpan(start, fullSpanEnd), result, nameId, args, nameSpan); } while (this.consumeOptionalOperator('|')); } @@ -588,10 +657,10 @@ export class _ParseAST { parseLogicalOr(): AST { // '||' + const start = this.inputIndex; let result = this.parseLogicalAnd(); while (this.consumeOptionalOperator('||')) { const right = this.parseLogicalAnd(); - const {start} = result.span; result = new Binary(this.span(start), this.sourceSpan(start), '||', result, right); } return result; @@ -599,10 +668,10 @@ export class _ParseAST { parseLogicalAnd(): AST { // '&&' + const start = this.inputIndex; let result = this.parseEquality(); while (this.consumeOptionalOperator('&&')) { const right = this.parseEquality(); - const {start} = result.span; result = new Binary(this.span(start), this.sourceSpan(start), '&&', result, right); } return result; @@ -610,6 +679,7 @@ export class _ParseAST { parseEquality(): AST { // '==','!=','===','!==' + const start = this.inputIndex; let result = this.parseRelational(); while (this.next.type == TokenType.Operator) { const operator = this.next.strValue; @@ -620,7 +690,6 @@ export class _ParseAST { case '!==': this.advance(); const right = this.parseRelational(); - const {start} = result.span; result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right); continue; } @@ -631,6 +700,7 @@ export class _ParseAST { parseRelational(): AST { // '<', '>', '<=', '>=' + const start = this.inputIndex; let result = this.parseAdditive(); while (this.next.type == TokenType.Operator) { const operator = this.next.strValue; @@ -641,7 +711,6 @@ export class _ParseAST { case '>=': this.advance(); const right = this.parseAdditive(); - const {start} = result.span; result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right); continue; } @@ -652,6 +721,7 @@ export class _ParseAST { parseAdditive(): AST { // '+', '-' + const start = this.inputIndex; let result = this.parseMultiplicative(); while (this.next.type == TokenType.Operator) { const operator = this.next.strValue; @@ -660,7 +730,6 @@ export class _ParseAST { case '-': this.advance(); let right = this.parseMultiplicative(); - const {start} = result.span; result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right); continue; } @@ -671,6 +740,7 @@ export class _ParseAST { parseMultiplicative(): AST { // '*', '%', '/' + const start = this.inputIndex; let result = this.parsePrefix(); while (this.next.type == TokenType.Operator) { const operator = this.next.strValue; @@ -680,7 +750,6 @@ export class _ParseAST { case '/': this.advance(); let right = this.parsePrefix(); - const {start} = result.span; result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right); continue; } @@ -713,14 +782,14 @@ export class _ParseAST { } parseCallChain(): AST { + const start = this.inputIndex; let result = this.parsePrimary(); - const resultStart = result.span.start; while (true) { if (this.consumeOptionalCharacter(chars.$PERIOD)) { - result = this.parseAccessMemberOrMethodCall(result, false); + result = this.parseAccessMemberOrMethodCall(result, start, false); } else if (this.consumeOptionalOperator('?.')) { - result = this.parseAccessMemberOrMethodCall(result, true); + result = this.parseAccessMemberOrMethodCall(result, start, true); } else if (this.consumeOptionalCharacter(chars.$LBRACKET)) { this.withContext(ParseContextFlags.Writable, () => { @@ -733,11 +802,9 @@ export class _ParseAST { this.expectCharacter(chars.$RBRACKET); if (this.consumeOptionalOperator('=')) { const value = this.parseConditional(); - result = new KeyedWrite( - this.span(resultStart), this.sourceSpan(resultStart), result, key, value); + result = new KeyedWrite(this.span(start), this.sourceSpan(start), result, key, value); } else { - result = - new KeyedRead(this.span(resultStart), this.sourceSpan(resultStart), result, key); + result = new KeyedRead(this.span(start), this.sourceSpan(start), result, key); } }); } else if (this.consumeOptionalCharacter(chars.$LPAREN)) { @@ -745,11 +812,10 @@ export class _ParseAST { const args = this.parseCallArguments(); this.rparensExpected--; this.expectCharacter(chars.$RPAREN); - result = - new FunctionCall(this.span(resultStart), this.sourceSpan(resultStart), result, args); + result = new FunctionCall(this.span(start), this.sourceSpan(start), result, args); } else if (this.consumeOptionalOperator('!')) { - result = new NonNullAssert(this.span(resultStart), this.sourceSpan(resultStart), result); + result = new NonNullAssert(this.span(start), this.sourceSpan(start), result); } else { return result; @@ -797,7 +863,7 @@ export class _ParseAST { } else if (this.next.isIdentifier()) { return this.parseAccessMemberOrMethodCall( - new ImplicitReceiver(this.span(start), this.sourceSpan(start)), false); + new ImplicitReceiver(this.span(start), this.sourceSpan(start)), start, false); } else if (this.next.isNumber()) { const value = this.next.toNumber(); @@ -820,11 +886,14 @@ export class _ParseAST { parseExpressionList(terminator: number): AST[] { const result: AST[] = []; - if (!this.next.isCharacter(terminator)) { - do { + + do { + if (!this.next.isCharacter(terminator)) { result.push(this.parsePipe()); - } while (this.consumeOptionalCharacter(chars.$COMMA)); - } + } else { + break; + } + } while (this.consumeOptionalCharacter(chars.$COMMA)); return result; } @@ -848,11 +917,10 @@ export class _ParseAST { return new LiteralMap(this.span(start), this.sourceSpan(start), keys, values); } - parseAccessMemberOrMethodCall(receiver: AST, isSafe: boolean = false): AST { - const start = receiver.span.start; + parseAccessMemberOrMethodCall(receiver: AST, start: number, isSafe: boolean = false): AST { const nameStart = this.inputIndex; const id = this.withContext(ParseContextFlags.Writable, () => { - const id = this.expectIdentifierOrKeyword(); + const id = this.expectIdentifierOrKeyword() ?? ''; if (id.length === 0) { this.error(`Expected identifier for property access`, receiver.span.end); } diff --git a/packages/compiler/src/injectable_compiler.ts b/packages/compiler/src/injectable_compiler.ts index f7c26148e8..272a4306c8 100644 --- a/packages/compiler/src/injectable_compiler.ts +++ b/packages/compiler/src/injectable_compiler.ts @@ -116,7 +116,8 @@ export class InjectableCompiler { mapEntry('token', ctx.importExpr(injectable.type.reference)), mapEntry('providedIn', providedIn), ]; - return o.importExpr(Identifiers.ɵɵdefineInjectable).callFn([o.literalMap(def)]); + return o.importExpr(Identifiers.ɵɵdefineInjectable) + .callFn([o.literalMap(def)], undefined, true); } compile(injectable: CompileInjectableMetadata, ctx: OutputContext): void { diff --git a/packages/compiler/src/injectable_compiler_2.ts b/packages/compiler/src/injectable_compiler_2.ts index 874232dfc5..68b7fdb737 100644 --- a/packages/compiler/src/injectable_compiler_2.ts +++ b/packages/compiler/src/injectable_compiler_2.ts @@ -8,8 +8,9 @@ import {Identifiers} from './identifiers'; import * as o from './output/output_ast'; -import {compileFactoryFunction, R3DependencyMetadata, R3FactoryDelegateType, R3FactoryMetadata, R3FactoryTarget} from './render3/r3_factory'; -import {mapToMapExpression, R3Reference, typeWithParameters} from './render3/util'; +import {compileFactoryFunction, FactoryTarget, R3DependencyMetadata, R3FactoryDelegateType, R3FactoryMetadata} from './render3/r3_factory'; +import {R3Reference, typeWithParameters} from './render3/util'; +import {DefinitionMap} from './render3/view/util'; export interface InjectableDef { expression: o.Expression; @@ -31,7 +32,7 @@ export interface R3InjectableMetadata { } export function compileInjectable(meta: R3InjectableMetadata): InjectableDef { - let result: {factory: o.Expression, statements: o.Statement[]}|null = null; + let result: {expression: o.Expression, statements: o.Statement[]}|null = null; const factoryMeta: R3FactoryMetadata = { name: meta.name, @@ -39,8 +40,7 @@ export function compileInjectable(meta: R3InjectableMetadata): InjectableDef { internalType: meta.internalType, typeArgumentCount: meta.typeArgumentCount, deps: [], - injectFn: Identifiers.inject, - target: R3FactoryTarget.Injectable, + target: FactoryTarget.Injectable, }; if (meta.useClass !== undefined) { @@ -82,7 +82,7 @@ export function compileInjectable(meta: R3InjectableMetadata): InjectableDef { } else { result = { statements: [], - factory: o.fn([], [new o.ReturnStatement(meta.useFactory.callFn([]))]) + expression: o.fn([], [new o.ReturnStatement(meta.useFactory.callFn([]))]) }; } } else if (meta.useValue !== undefined) { @@ -106,15 +106,18 @@ export function compileInjectable(meta: R3InjectableMetadata): InjectableDef { const token = meta.internalType; - const injectableProps: {[key: string]: o.Expression} = {token, factory: result.factory}; + const injectableProps = + new DefinitionMap<{token: o.Expression, factory: o.Expression, providedIn: o.Expression}>(); + injectableProps.set('token', token); + injectableProps.set('factory', result.expression); // Only generate providedIn property if it has a non-null value if ((meta.providedIn as o.LiteralExpr).value !== null) { - injectableProps.providedIn = meta.providedIn; + injectableProps.set('providedIn', meta.providedIn); } - const expression = - o.importExpr(Identifiers.ɵɵdefineInjectable).callFn([mapToMapExpression(injectableProps)]); + const expression = o.importExpr(Identifiers.ɵɵdefineInjectable) + .callFn([injectableProps.toLiteralMap()], undefined, true); const type = new o.ExpressionType(o.importExpr( Identifiers.InjectableDef, [typeWithParameters(meta.type.type, meta.typeArgumentCount)])); @@ -131,7 +134,7 @@ function delegateToFactory(type: o.WrappedNodeExpr, internalType: o.Wrapped // If types are the same, we can generate `factory: type.ɵfac` // If types are different, we have to generate a wrapper function to ensure // the internal type has been resolved (`factory: function(t) { return type.ɵfac(t); }`) - factory: type.node === internalType.node ? + expression: type.node === internalType.node ? internalType.prop('ɵfac') : o.fn([new o.FnParam('t', o.DYNAMIC_TYPE)], [new o.ReturnStatement(internalType.callMethod( 'ɵfac', [o.variable('t')]))]) diff --git a/packages/compiler/src/jit_compiler_facade.ts b/packages/compiler/src/jit_compiler_facade.ts index e7a7ab4342..32496f8789 100644 --- a/packages/compiler/src/jit_compiler_facade.ts +++ b/packages/compiler/src/jit_compiler_facade.ts @@ -7,29 +7,28 @@ */ -import {CompilerFacade, CoreEnvironment, ExportedCompilerFacade, R3ComponentMetadataFacade, R3DependencyMetadataFacade, R3DirectiveMetadataFacade, R3FactoryDefMetadataFacade, R3InjectableMetadataFacade, R3InjectorMetadataFacade, R3NgModuleMetadataFacade, R3PipeMetadataFacade, R3QueryMetadataFacade, StringMap, StringMapWithRename} from './compiler_facade_interface'; +import {CompilerFacade, CoreEnvironment, ExportedCompilerFacade, OpaqueValue, R3ComponentMetadataFacade, R3DeclareComponentFacade, R3DeclareDependencyMetadataFacade, R3DeclareDirectiveFacade, R3DeclareFactoryFacade, R3DeclareInjectorFacade, R3DeclareNgModuleFacade, R3DeclarePipeFacade, R3DeclareQueryMetadataFacade, R3DeclareUsedDirectiveFacade, R3DependencyMetadataFacade, R3DirectiveMetadataFacade, R3FactoryDefMetadataFacade, R3InjectableMetadataFacade, R3InjectorMetadataFacade, R3NgModuleMetadataFacade, R3PipeMetadataFacade, R3QueryMetadataFacade, StringMap, StringMapWithRename} from './compiler_facade_interface'; import {ConstantPool} from './constant_pool'; -import {HostBinding, HostListener, Input, Output, Type} from './core'; -import {Identifiers} from './identifiers'; +import {ChangeDetectionStrategy, HostBinding, HostListener, Input, Output, Type, ViewEncapsulation} from './core'; import {compileInjectable} from './injectable_compiler_2'; import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './ml_parser/interpolation_config'; -import {DeclareVarStmt, Expression, LiteralExpr, Statement, StmtModifier, WrappedNodeExpr} from './output/output_ast'; +import {DeclareVarStmt, Expression, literal, LiteralExpr, Statement, StmtModifier, WrappedNodeExpr} from './output/output_ast'; import {JitEvaluator} from './output/output_jit'; import {ParseError, ParseSourceSpan, r3JitTypeSourceSpan} from './parse_util'; -import {compileFactoryFunction, R3DependencyMetadata, R3FactoryTarget, R3ResolvedDependencyType} from './render3/r3_factory'; +import {compileFactoryFunction, FactoryTarget, R3DependencyMetadata} from './render3/r3_factory'; +import {compileInjector, R3InjectorMetadata} from './render3/r3_injector_compiler'; import {R3JitReflector} from './render3/r3_jit'; -import {compileInjector, compileNgModule, R3InjectorMetadata, R3NgModuleMetadata} from './render3/r3_module_compiler'; +import {compileNgModule, compileNgModuleDeclarationExpression, R3NgModuleMetadata} from './render3/r3_module_compiler'; import {compilePipeFromMetadata, R3PipeMetadata} from './render3/r3_pipe_compiler'; -import {R3Reference} from './render3/util'; -import {R3ComponentMetadata, R3DirectiveMetadata, R3QueryMetadata} from './render3/view/api'; +import {getSafePropertyAccessString, wrapReference} from './render3/util'; +import {DeclarationListEmitMode, R3ComponentMetadata, R3DirectiveMetadata, R3HostMetadata, R3QueryMetadata, R3UsedDirectiveMetadata} from './render3/view/api'; 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'; export class CompilerFacadeImpl implements CompilerFacade { - R3ResolvedDependencyType = R3ResolvedDependencyType as any; - R3FactoryTarget = R3FactoryTarget as any; + FactoryTarget = FactoryTarget as any; ResourceLoader = ResourceLoader; private elementSchemaRegistry = new DomElementSchemaRegistry(); @@ -41,8 +40,8 @@ export class CompilerFacadeImpl implements CompilerFacade { name: facade.name, type: wrapReference(facade.type), internalType: new WrappedNodeExpr(facade.type), - typeArgumentCount: facade.typeArgumentCount, - deps: convertR3DependencyMetadataArray(facade.deps), + typeArgumentCount: 0, + deps: null, pipeName: facade.pipeName, pure: facade.pure, }; @@ -50,6 +49,14 @@ export class CompilerFacadeImpl implements CompilerFacade { return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []); } + compilePipeDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, + declaration: R3DeclarePipeFacade): any { + const meta = convertDeclarePipeFacadeToMetadata(declaration); + const res = compilePipeFromMetadata(meta); + return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []); + } + compileInjectable( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, facade: R3InjectableMetadataFacade): any { @@ -76,12 +83,19 @@ export class CompilerFacadeImpl implements CompilerFacade { name: facade.name, type: wrapReference(facade.type), internalType: new WrappedNodeExpr(facade.type), - deps: convertR3DependencyMetadataArray(facade.deps), providers: new WrappedNodeExpr(facade.providers), imports: facade.imports.map(i => new WrappedNodeExpr(i)), }; const res = compileInjector(meta); - return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, res.statements); + return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []); + } + + compileInjectorDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, + declaration: R3DeclareInjectorFacade): any { + const meta = convertDeclareInjectorFacadeToMetadata(declaration); + const res = compileInjector(meta); + return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []); } compileNgModule( @@ -104,13 +118,33 @@ export class CompilerFacadeImpl implements CompilerFacade { return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []); } + compileNgModuleDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, + declaration: R3DeclareNgModuleFacade): any { + const expression = compileNgModuleDeclarationExpression(declaration); + return this.jitExpression(expression, angularCoreEnv, sourceMapUrl, []); + } + compileDirective( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, facade: R3DirectiveMetadataFacade): any { + const meta: R3DirectiveMetadata = convertDirectiveFacadeToMetadata(facade); + return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta); + } + + compileDirectiveDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, + declaration: R3DeclareDirectiveFacade): any { + const typeSourceSpan = + this.createParseSourceSpan('Directive', declaration.type.name, sourceMapUrl); + const meta = convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan); + return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta); + } + + private compileDirectiveFromMeta( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3DirectiveMetadata): any { const constantPool = new ConstantPool(); const bindingParser = makeBindingParser(); - - const meta: R3DirectiveMetadata = convertDirectiveFacadeToMetadata(facade); const res = compileDirectiveFromMetadata(meta, constantPool, bindingParser); return this.jitExpression( res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements); @@ -119,32 +153,21 @@ export class CompilerFacadeImpl implements CompilerFacade { compileComponent( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, facade: R3ComponentMetadataFacade): any { - // The ConstantPool is a requirement of the JIT'er. - const constantPool = new ConstantPool(); - - const interpolationConfig = facade.interpolation ? - InterpolationConfig.fromArray(facade.interpolation) : - DEFAULT_INTERPOLATION_CONFIG; // Parse the template and check for errors. - const template = parseTemplate( - facade.template, sourceMapUrl, - {preserveWhitespaces: facade.preserveWhitespaces, interpolationConfig}); - if (template.errors !== null) { - const errors = template.errors.map(err => err.toString()).join(', '); - throw new Error(`Errors during JIT compilation of template for ${facade.name}: ${errors}`); - } + const {template, interpolation} = parseJitTemplate( + facade.template, facade.name, sourceMapUrl, facade.preserveWhitespaces, + facade.interpolation); // Compile the component metadata, including template, into an expression. - // TODO(alxhub): implement inputs, outputs, queries, etc. - const metadata: R3ComponentMetadata = { + const meta: R3ComponentMetadata = { ...facade as R3ComponentMetadataFacadeNoPropAndWhitespace, ...convertDirectiveFacadeToMetadata(facade), selector: facade.selector || this.elementSchemaRegistry.getDefaultComponentElementName(), template, - wrapDirectivesAndPipesInClosure: false, + declarationListEmitMode: DeclarationListEmitMode.Direct, styles: [...facade.styles, ...template.styles], encapsulation: facade.encapsulation as any, - interpolation: interpolationConfig, + interpolation, changeDetection: facade.changeDetection, animations: facade.animations != null ? new WrappedNodeExpr(facade.animations) : null, viewProviders: facade.viewProviders != null ? new WrappedNodeExpr(facade.viewProviders) : @@ -152,11 +175,26 @@ export class CompilerFacadeImpl implements CompilerFacade { relativeContextFilePath: '', i18nUseExternalIds: true, }; - const res = compileComponentFromMetadata( - metadata, constantPool, makeBindingParser(interpolationConfig)); const jitExpressionSourceMap = `ng:///${facade.name}.js`; + return this.compileComponentFromMeta(angularCoreEnv, jitExpressionSourceMap, meta); + } + + compileComponentDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, + declaration: R3DeclareComponentFacade): any { + const typeSourceSpan = + this.createParseSourceSpan('Component', declaration.type.name, sourceMapUrl); + const meta = convertDeclareComponentFacadeToMetadata(declaration, typeSourceSpan, sourceMapUrl); + return this.compileComponentFromMeta(angularCoreEnv, sourceMapUrl, meta); + } + + private compileComponentFromMeta( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3ComponentMetadata): any { + const constantPool = new ConstantPool(); + const bindingParser = makeBindingParser(meta.interpolation); + const res = compileComponentFromMetadata(meta, constantPool, bindingParser); return this.jitExpression( - res.expression, angularCoreEnv, jitExpressionSourceMap, constantPool.statements); + res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements); } compileFactory( @@ -167,14 +205,27 @@ export class CompilerFacadeImpl implements CompilerFacade { internalType: new WrappedNodeExpr(meta.type), typeArgumentCount: meta.typeArgumentCount, deps: convertR3DependencyMetadataArray(meta.deps), - injectFn: meta.injectFn === 'directiveInject' ? Identifiers.directiveInject : - Identifiers.inject, target: meta.target, }); return this.jitExpression( - factoryRes.factory, angularCoreEnv, sourceMapUrl, factoryRes.statements); + factoryRes.expression, angularCoreEnv, sourceMapUrl, factoryRes.statements); } + compileFactoryDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3DeclareFactoryFacade) { + const factoryRes = compileFactoryFunction({ + name: meta.type.name, + type: wrapReference(meta.type), + internalType: new WrappedNodeExpr(meta.type), + typeArgumentCount: 0, + deps: meta.deps && meta.deps.map(convertR3DeclareDependencyMetadata), + target: meta.target, + }); + return this.jitExpression( + factoryRes.expression, angularCoreEnv, sourceMapUrl, factoryRes.statements); + } + + createParseSourceSpan(kind: string, typeName: string, sourceUrl: string): ParseSourceSpan { return r3JitTypeSourceSpan(kind, typeName, sourceUrl); } @@ -215,18 +266,28 @@ const USE_FACTORY = Object.keys({useFactory: null})[0]; const USE_VALUE = Object.keys({useValue: null})[0]; const USE_EXISTING = Object.keys({useExisting: null})[0]; -const wrapReference = function(value: any): R3Reference { - const wrapped = new WrappedNodeExpr(value); - return {value: wrapped, type: wrapped}; -}; - function convertToR3QueryMetadata(facade: R3QueryMetadataFacade): R3QueryMetadata { return { ...facade, predicate: Array.isArray(facade.predicate) ? facade.predicate : new WrappedNodeExpr(facade.predicate), read: facade.read ? new WrappedNodeExpr(facade.read) : null, - static: facade.static + static: facade.static, + emitDistinctChangesOnly: facade.emitDistinctChangesOnly, + }; +} + +function convertQueryDeclarationToMetadata(declaration: R3DeclareQueryMetadataFacade): + R3QueryMetadata { + return { + propertyName: declaration.propertyName, + first: declaration.first ?? false, + predicate: Array.isArray(declaration.predicate) ? declaration.predicate : + new WrappedNodeExpr(declaration.predicate), + descendants: declaration.descendants ?? false, + read: declaration.read ? new WrappedNodeExpr(declaration.read) : null, + static: declaration.static ?? false, + emitDistinctChangesOnly: declaration.emitDistinctChangesOnly ?? true, }; } @@ -251,10 +312,11 @@ function convertDirectiveFacadeToMetadata(facade: R3DirectiveMetadataFacade): R3 return { ...facade as R3DirectiveMetadataFacadeNoPropAndWhitespace, + typeArgumentCount: 0, typeSourceSpan: facade.typeSourceSpan, type: wrapReference(facade.type), internalType: new WrappedNodeExpr(facade.type), - deps: convertR3DependencyMetadataArray(facade.deps), + deps: null, host: extractHostBindings(facade.propMetadata, facade.typeSourceSpan, facade.host), inputs: {...inputsFromMetadata, ...inputsFromType}, outputs: {...outputsFromMetadata, ...outputsFromType}, @@ -265,6 +327,121 @@ function convertDirectiveFacadeToMetadata(facade: R3DirectiveMetadataFacade): R3 }; } +function convertDeclareDirectiveFacadeToMetadata( + declaration: R3DeclareDirectiveFacade, typeSourceSpan: ParseSourceSpan): R3DirectiveMetadata { + return { + name: declaration.type.name, + type: wrapReference(declaration.type), + typeSourceSpan, + internalType: new WrappedNodeExpr(declaration.type), + selector: declaration.selector ?? null, + inputs: declaration.inputs ?? {}, + outputs: declaration.outputs ?? {}, + host: convertHostDeclarationToMetadata(declaration.host), + queries: (declaration.queries ?? []).map(convertQueryDeclarationToMetadata), + viewQueries: (declaration.viewQueries ?? []).map(convertQueryDeclarationToMetadata), + providers: declaration.providers !== undefined ? new WrappedNodeExpr(declaration.providers) : + null, + exportAs: declaration.exportAs ?? null, + usesInheritance: declaration.usesInheritance ?? false, + lifecycle: {usesOnChanges: declaration.usesOnChanges ?? false}, + deps: null, + typeArgumentCount: 0, + fullInheritance: false, + }; +} + +function convertHostDeclarationToMetadata(host: R3DeclareDirectiveFacade['host'] = {}): + R3HostMetadata { + return { + attributes: convertOpaqueValuesToExpressions(host.attributes ?? {}), + listeners: host.listeners ?? {}, + properties: host.properties ?? {}, + specialAttributes: { + classAttr: host.classAttribute, + styleAttr: host.styleAttribute, + }, + }; +} + +function convertOpaqueValuesToExpressions(obj: {[key: string]: OpaqueValue}): + {[key: string]: WrappedNodeExpr} { + const result: {[key: string]: WrappedNodeExpr} = {}; + for (const key of Object.keys(obj)) { + result[key] = new WrappedNodeExpr(obj[key]); + } + return result; +} + +function convertDeclareComponentFacadeToMetadata( + declaration: R3DeclareComponentFacade, typeSourceSpan: ParseSourceSpan, + sourceMapUrl: string): R3ComponentMetadata { + const {template, interpolation} = parseJitTemplate( + declaration.template, declaration.type.name, sourceMapUrl, + declaration.preserveWhitespaces ?? false, declaration.interpolation); + + return { + ...convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan), + template, + styles: declaration.styles ?? [], + directives: (declaration.components ?? []) + .concat(declaration.directives ?? []) + .map(convertUsedDirectiveDeclarationToMetadata), + pipes: convertUsedPipesToMetadata(declaration.pipes), + viewProviders: declaration.viewProviders !== undefined ? + new WrappedNodeExpr(declaration.viewProviders) : + null, + animations: declaration.animations !== undefined ? new WrappedNodeExpr(declaration.animations) : + null, + changeDetection: declaration.changeDetection ?? ChangeDetectionStrategy.Default, + encapsulation: declaration.encapsulation ?? ViewEncapsulation.Emulated, + interpolation, + declarationListEmitMode: DeclarationListEmitMode.ClosureResolved, + relativeContextFilePath: '', + i18nUseExternalIds: true, + }; +} + +function convertUsedDirectiveDeclarationToMetadata(declaration: R3DeclareUsedDirectiveFacade): + R3UsedDirectiveMetadata { + return { + selector: declaration.selector, + type: new WrappedNodeExpr(declaration.type), + inputs: declaration.inputs ?? [], + outputs: declaration.outputs ?? [], + exportAs: declaration.exportAs ?? null, + }; +} + +function convertUsedPipesToMetadata(declaredPipes: R3DeclareComponentFacade['pipes']): + Map { + const pipes = new Map(); + if (declaredPipes === undefined) { + return pipes; + } + + for (const pipeName of Object.keys(declaredPipes)) { + const pipeType = declaredPipes[pipeName]; + pipes.set(pipeName, new WrappedNodeExpr(pipeType)); + } + return pipes; +} + +function parseJitTemplate( + template: string, typeName: string, sourceMapUrl: string, preserveWhitespaces: boolean, + interpolation: [string, string]|undefined) { + const interpolationConfig = + interpolation ? InterpolationConfig.fromArray(interpolation) : DEFAULT_INTERPOLATION_CONFIG; + // Parse the template and check for errors. + const parsed = parseTemplate( + template, sourceMapUrl, {preserveWhitespaces: preserveWhitespaces, interpolationConfig}); + if (parsed.errors !== null) { + const errors = parsed.errors.map(err => err.toString()).join(', '); + throw new Error(`Errors during JIT compilation of template for ${typeName}: ${errors}`); + } + return {template: parsed, interpolation: interpolationConfig}; +} + // This seems to be needed to placate TS v3.0 only type R3DirectiveMetadataFacadeNoPropAndWhitespace = Pick>; @@ -285,31 +462,40 @@ function computeProvidedIn(providedIn: Type|string|null|undefined): Expression { } } -function convertR3DependencyMetadata(facade: R3DependencyMetadataFacade): R3DependencyMetadata { - let tokenExpr; - if (facade.token === null) { - tokenExpr = new LiteralExpr(null); - } else if (facade.resolved === R3ResolvedDependencyType.Attribute) { - tokenExpr = new LiteralExpr(facade.token); - } else { - tokenExpr = new WrappedNodeExpr(facade.token); - } - return { - token: tokenExpr, - attribute: null, - resolved: facade.resolved, - host: facade.host, - optional: facade.optional, - self: facade.self, - skipSelf: facade.skipSelf, - }; -} - function convertR3DependencyMetadataArray(facades: R3DependencyMetadataFacade[]|null| undefined): R3DependencyMetadata[]|null { return facades == null ? null : facades.map(convertR3DependencyMetadata); } +function convertR3DependencyMetadata(facade: R3DependencyMetadataFacade): R3DependencyMetadata { + const isAttributeDep = facade.attribute != null; // both `null` and `undefined` + const rawToken = facade.token === null ? null : new WrappedNodeExpr(facade.token); + // In JIT mode, if the dep is an `@Attribute()` then we use the attribute name given in + // `attribute` rather than the `token`. + const token = isAttributeDep ? new WrappedNodeExpr(facade.attribute) : rawToken; + return createR3DependencyMetadata( + token, isAttributeDep, facade.host, facade.optional, facade.self, facade.skipSelf); +} + +function convertR3DeclareDependencyMetadata(facade: R3DeclareDependencyMetadataFacade): + R3DependencyMetadata { + const isAttributeDep = facade.attribute ?? false; + const token = facade.token === null ? null : new WrappedNodeExpr(facade.token); + return createR3DependencyMetadata( + token, isAttributeDep, facade.host ?? false, facade.optional ?? false, facade.self ?? false, + facade.skipSelf ?? false); +} + +function createR3DependencyMetadata( + token: WrappedNodeExpr|null, isAttributeDep: boolean, host: boolean, optional: boolean, + self: boolean, skipSelf: boolean): R3DependencyMetadata { + // If the dep is an `@Attribute()` the `attributeNameType` ought to be the `unknown` type. + // But types are not available at runtime so we just use a literal `""` string as a dummy + // marker. + const attributeNameType = isAttributeDep ? literal('unknown') : null; + return {token, attributeNameType, host, optional, self, skipSelf}; +} + function extractHostBindings( propMetadata: {[key: string]: any[]}, sourceSpan: ParseSourceSpan, host?: {[key: string]: string}): ParsedHostBindings { @@ -327,7 +513,11 @@ function extractHostBindings( if (propMetadata.hasOwnProperty(field)) { propMetadata[field].forEach(ann => { if (isHostBinding(ann)) { - bindings.properties[ann.hostPropertyName || field] = field; + // Since this is a decorator, we know that the value is a class member. Always access it + // through `this` so that further down the line it can't be confused for a literal value + // (e.g. if there's a property called `true`). + bindings.properties[ann.hostPropertyName || field] = + getSafePropertyAccessString('this', field); } else if (isHostListener(ann)) { bindings.listeners[ann.eventName || field] = `${field}(${(ann.args || []).join(',')})`; } @@ -363,6 +553,32 @@ function parseInputOutputs(values: string[]): StringMap { }, {} as StringMap); } +function convertDeclarePipeFacadeToMetadata(declaration: R3DeclarePipeFacade): R3PipeMetadata { + return { + name: declaration.type.name, + type: wrapReference(declaration.type), + internalType: new WrappedNodeExpr(declaration.type), + typeArgumentCount: 0, + pipeName: declaration.name, + deps: null, + pure: declaration.pure ?? true, + }; +} + +function convertDeclareInjectorFacadeToMetadata(declaration: R3DeclareInjectorFacade): + R3InjectorMetadata { + return { + name: declaration.type.name, + type: wrapReference(declaration.type), + internalType: new WrappedNodeExpr(declaration.type), + providers: declaration.providers !== undefined ? new WrappedNodeExpr(declaration.providers) : + null, + imports: declaration.imports !== undefined ? + declaration.imports.map(i => new WrappedNodeExpr(i)) : + [], + }; +} + export function publishFacade(global: any) { const ng: ExportedCompilerFacade = global.ng || (global.ng = {}); ng.ɵcompilerFacade = new CompilerFacadeImpl(); diff --git a/packages/compiler/src/metadata_resolver.ts b/packages/compiler/src/metadata_resolver.ts index 17a247b4bd..c1d1e51314 100644 --- a/packages/compiler/src/metadata_resolver.ts +++ b/packages/compiler/src/metadata_resolver.ts @@ -1196,6 +1196,7 @@ export class CompileMetadataResolver { selectors, first: q.first, descendants: q.descendants, + emitDistinctChangesOnly: q.emitDistinctChangesOnly, propertyName, read: q.read ? this._getTokenMetadata(q.read) : null!, static: q.static diff --git a/packages/compiler/src/ml_parser/html_tags.ts b/packages/compiler/src/ml_parser/html_tags.ts index 9fbffd45e2..ffde2b7679 100644 --- a/packages/compiler/src/ml_parser/html_tags.ts +++ b/packages/compiler/src/ml_parser/html_tags.ts @@ -10,10 +10,11 @@ import {TagContentType, TagDefinition} from './tags'; export class HtmlTagDefinition implements TagDefinition { private closedByChildren: {[key: string]: boolean} = {}; + private contentType: TagContentType| + {default: TagContentType, [namespace: string]: TagContentType}; closedByParent: boolean = false; implicitNamespacePrefix: string|null; - contentType: TagContentType; isVoid: boolean; ignoreFirstLf: boolean; canSelfClose: boolean = false; @@ -31,7 +32,7 @@ export class HtmlTagDefinition implements TagDefinition { closedByChildren?: string[], closedByParent?: boolean, implicitNamespacePrefix?: string, - contentType?: TagContentType, + contentType?: TagContentType|{default: TagContentType, [namespace: string]: TagContentType}, isVoid?: boolean, ignoreFirstLf?: boolean, preventNamespaceInheritance?: boolean @@ -50,6 +51,14 @@ export class HtmlTagDefinition implements TagDefinition { isClosedByChild(name: string): boolean { return this.isVoid || name.toLowerCase() in this.closedByChildren; } + + getContentType(prefix?: string): TagContentType { + if (typeof this.contentType === 'object') { + const overrideType = prefix == null ? undefined : this.contentType[prefix]; + return overrideType ?? this.contentType.default; + } + return this.contentType; + } } let _DEFAULT_TAG_DEFINITION!: HtmlTagDefinition; @@ -121,7 +130,11 @@ export function getHtmlTagDefinition(tagName: string): HtmlTagDefinition { 'listing': new HtmlTagDefinition({ignoreFirstLf: true}), 'style': new HtmlTagDefinition({contentType: TagContentType.RAW_TEXT}), 'script': new HtmlTagDefinition({contentType: TagContentType.RAW_TEXT}), - 'title': new HtmlTagDefinition({contentType: TagContentType.ESCAPABLE_RAW_TEXT}), + 'title': new HtmlTagDefinition({ + // The browser supports two separate `title` tags which have to use + // a different content type: `HTMLTitleElement` and `SVGTitleElement` + contentType: {default: TagContentType.ESCAPABLE_RAW_TEXT, svg: TagContentType.PARSABLE_DATA} + }), 'textarea': new HtmlTagDefinition( {contentType: TagContentType.ESCAPABLE_RAW_TEXT, ignoreFirstLf: true}), }; diff --git a/packages/compiler/src/ml_parser/lexer.ts b/packages/compiler/src/ml_parser/lexer.ts index 1b4ce13929..6864d5a550 100644 --- a/packages/compiler/src/ml_parser/lexer.ts +++ b/packages/compiler/src/ml_parser/lexer.ts @@ -523,7 +523,7 @@ class _Tokenizer { tagName = openTagToken.parts[1]; this._attemptCharCodeUntilFn(isNotWhitespace); while (this._cursor.peek() !== chars.$SLASH && this._cursor.peek() !== chars.$GT && - this._cursor.peek() !== chars.$LT) { + this._cursor.peek() !== chars.$LT && this._cursor.peek() !== chars.$EOF) { this._consumeAttributeName(); this._attemptCharCodeUntilFn(isNotWhitespace); if (this._attemptCharCode(chars.$EQ)) { @@ -550,7 +550,7 @@ class _Tokenizer { throw e; } - const contentTokenType = this._getTagDefinition(tagName).contentType; + const contentTokenType = this._getTagDefinition(tagName).getContentType(prefix); if (contentTokenType === TagContentType.RAW_TEXT) { this._consumeRawTextWithTagClose(prefix, tagName, false); @@ -560,7 +560,7 @@ class _Tokenizer { } private _consumeRawTextWithTagClose(prefix: string, tagName: string, decodeEntities: boolean) { - const textToken = this._consumeRawText(decodeEntities, () => { + this._consumeRawText(decodeEntities, () => { if (!this._attemptCharCode(chars.$LT)) return false; if (!this._attemptCharCode(chars.$SLASH)) return false; this._attemptCharCodeUntilFn(isNotWhitespace); @@ -774,7 +774,8 @@ function isNotWhitespace(code: number): boolean { function isNameEnd(code: number): boolean { return chars.isWhitespace(code) || code === chars.$GT || code === chars.$LT || - code === chars.$SLASH || code === chars.$SQ || code === chars.$DQ || code === chars.$EQ; + code === chars.$SLASH || code === chars.$SQ || code === chars.$DQ || code === chars.$EQ || + code === chars.$EOF; } function isPrefixEnd(code: number): boolean { diff --git a/packages/compiler/src/ml_parser/parser.ts b/packages/compiler/src/ml_parser/parser.ts index df73b9b6ac..905c25d583 100644 --- a/packages/compiler/src/ml_parser/parser.ts +++ b/packages/compiler/src/ml_parser/parser.ts @@ -259,7 +259,7 @@ class _TreeBuilder { this._advance(); selfClosing = false; } - const end = this._peek.sourceSpan.start; + const end = this._peek.sourceSpan.fullStart; const span = new ParseSourceSpan( startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart); // Create a separate `startSpan` because `span` will be modified when there is an `end` span. diff --git a/packages/compiler/src/ml_parser/tags.ts b/packages/compiler/src/ml_parser/tags.ts index 4d061e9f68..8c2f4a65fc 100644 --- a/packages/compiler/src/ml_parser/tags.ts +++ b/packages/compiler/src/ml_parser/tags.ts @@ -15,13 +15,13 @@ export enum TagContentType { export interface TagDefinition { closedByParent: boolean; implicitNamespacePrefix: string|null; - contentType: TagContentType; isVoid: boolean; ignoreFirstLf: boolean; canSelfClose: boolean; preventNamespaceInheritance: boolean; isClosedByChild(name: string): boolean; + getContentType(prefix?: string): TagContentType; } export function splitNsName(elementName: string): [string|null, string] { diff --git a/packages/compiler/src/ml_parser/xml_tags.ts b/packages/compiler/src/ml_parser/xml_tags.ts index e4a76ed245..d36bf684d6 100644 --- a/packages/compiler/src/ml_parser/xml_tags.ts +++ b/packages/compiler/src/ml_parser/xml_tags.ts @@ -16,7 +16,6 @@ export class XmlTagDefinition implements TagDefinition { parentToAdd!: string; // TODO(issue/24571): remove '!'. implicitNamespacePrefix!: string; - contentType: TagContentType = TagContentType.PARSABLE_DATA; isVoid: boolean = false; ignoreFirstLf: boolean = false; canSelfClose: boolean = true; @@ -29,6 +28,10 @@ export class XmlTagDefinition implements TagDefinition { isClosedByChild(name: string): boolean { return false; } + + getContentType(): TagContentType { + return TagContentType.PARSABLE_DATA; + } } const _TAG_DEFINITION = new XmlTagDefinition(); diff --git a/packages/compiler/src/output/abstract_emitter.ts b/packages/compiler/src/output/abstract_emitter.ts index bbb561bacc..388cb3e8aa 100644 --- a/packages/compiler/src/output/abstract_emitter.ts +++ b/packages/compiler/src/output/abstract_emitter.ts @@ -344,6 +344,17 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex ctx.print(expr, `)`); return null; } + visitTaggedTemplateExpr(expr: o.TaggedTemplateExpr, ctx: EmitterVisitorContext): any { + expr.tag.visitExpression(this, ctx); + ctx.print(expr, '`' + expr.template.elements[0].rawText); + for (let i = 1; i < expr.template.elements.length; i++) { + ctx.print(expr, '${'); + expr.template.expressions[i - 1].visitExpression(this, ctx); + ctx.print(expr, `}${expr.template.elements[i].rawText}`); + } + ctx.print(expr, '`'); + return null; + } visitWrappedNodeExpr(ast: o.WrappedNodeExpr, ctx: EmitterVisitorContext): any { throw new Error('Abstract emitter cannot visit WrappedNodeExpr.'); } diff --git a/packages/compiler/src/output/abstract_js_emitter.ts b/packages/compiler/src/output/abstract_js_emitter.ts index 7afd8159a0..b0054f02db 100644 --- a/packages/compiler/src/output/abstract_js_emitter.ts +++ b/packages/compiler/src/output/abstract_js_emitter.ts @@ -10,6 +10,21 @@ import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext, escapeIdentifier} from './abstract_emitter'; import * as o from './output_ast'; +/** + * In TypeScript, tagged template functions expect a "template object", which is an array of + * "cooked" strings plus a `raw` property that contains an array of "raw" strings. This is + * typically constructed with a function called `__makeTemplateObject(cooked, raw)`, but it may not + * be available in all environments. + * + * This is a JavaScript polyfill that uses __makeTemplateObject when it's available, but otherwise + * creates an inline helper with the same functionality. + * + * In the inline function, if `Object.defineProperty` is available we use that to attach the `raw` + * array. + */ +const makeTemplateObjectPolyfill = + '(this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})'; + export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor { constructor() { super(false); @@ -115,6 +130,27 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor { } return null; } + visitTaggedTemplateExpr(ast: o.TaggedTemplateExpr, ctx: EmitterVisitorContext): any { + // The following convoluted piece of code is effectively the downlevelled equivalent of + // ``` + // tag`...` + // ``` + // which is effectively like: + // ``` + // tag(__makeTemplateObject(cooked, raw), expression1, expression2, ...); + // ``` + const elements = ast.template.elements; + ast.tag.visitExpression(this, ctx); + ctx.print(ast, `(${makeTemplateObjectPolyfill}(`); + ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.text, false)).join(', ')}], `); + ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.rawText, false)).join(', ')}])`); + ast.template.expressions.forEach(expression => { + ctx.print(ast, ', '); + expression.visitExpression(this, ctx); + }); + ctx.print(ast, ')'); + return null; + } visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any { ctx.print(ast, `function${ast.name ? ' ' + ast.name : ''}(`); this._visitParams(ast.params, ctx); @@ -161,19 +197,7 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor { // ``` // $localize(__makeTemplateObject(cooked, raw), expression1, expression2, ...); // ``` - // - // The `$localize` function expects a "template object", which is an array of "cooked" strings - // plus a `raw` property that contains an array of "raw" strings. - // - // In some environments a helper function called `__makeTemplateObject(cooked, raw)` might be - // available, in which case we use that. Otherwise we must create our own helper function - // inline. - // - // In the inline function, if `Object.defineProperty` is available we use that to attach the - // `raw` array. - ctx.print( - ast, - '$localize((this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})('); + ctx.print(ast, `$localize(${makeTemplateObjectPolyfill}(`); const parts = [ast.serializeI18nHead()]; for (let i = 1; i < ast.messageParts.length; i++) { parts.push(ast.serializeI18nTemplatePart(i)); diff --git a/packages/compiler/src/output/output_ast.ts b/packages/compiler/src/output/output_ast.ts index d74bff4389..bf14072310 100644 --- a/packages/compiler/src/output/output_ast.ts +++ b/packages/compiler/src/output/output_ast.ts @@ -126,20 +126,26 @@ export function nullSafeIsEquivalent( - base: T[], other: T[]) { +function areAllEquivalentPredicate( + base: T[], other: T[], equivalentPredicate: (baseElement: T, otherElement: T) => boolean) { const len = base.length; if (len !== other.length) { return false; } for (let i = 0; i < len; i++) { - if (!base[i].isEquivalent(other[i])) { + if (!equivalentPredicate(base[i], other[i])) { return false; } } return true; } +export function areAllEquivalent( + base: T[], other: T[]) { + return areAllEquivalentPredicate( + base, other, (baseElement: T, otherElement: T) => baseElement.isEquivalent(otherElement)); +} + export abstract class Expression { public type: Type|null; public sourceSpan: ParseSourceSpan|null; @@ -468,6 +474,30 @@ export class InvokeFunctionExpr extends Expression { } +export class TaggedTemplateExpr extends Expression { + constructor( + public tag: Expression, public template: TemplateLiteral, type?: Type|null, + sourceSpan?: ParseSourceSpan|null) { + super(type, sourceSpan); + } + + isEquivalent(e: Expression): boolean { + return e instanceof TaggedTemplateExpr && this.tag.isEquivalent(e.tag) && + areAllEquivalentPredicate( + this.template.elements, e.template.elements, (a, b) => a.text === b.text) && + areAllEquivalent(this.template.expressions, e.template.expressions); + } + + isConstant() { + return false; + } + + visitExpression(visitor: ExpressionVisitor, context: any): any { + return visitor.visitTaggedTemplateExpr(this, context); + } +} + + export class InstantiateExpr extends Expression { constructor( public classExpr: Expression, public args: Expression[], type?: Type|null, @@ -510,6 +540,23 @@ export class LiteralExpr extends Expression { } } +export class TemplateLiteral { + constructor(public elements: TemplateLiteralElement[], public expressions: Expression[]) {} +} +export class TemplateLiteralElement { + rawText: string; + constructor(public text: string, public sourceSpan?: ParseSourceSpan, rawText?: string) { + // If `rawText` is not provided, try to extract the raw string from its + // associated `sourceSpan`. If that is also not available, "fake" the raw + // string instead by escaping the following control sequences: + // - "\" would otherwise indicate that the next character is a control character. + // - "`" and "${" are template string control sequences that would otherwise prematurely + // indicate the end of the template literal element. + this.rawText = + rawText ?? sourceSpan?.toString() ?? escapeForTemplateLiteral(escapeSlashes(text)); + } +} + export abstract class MessagePiece { constructor(public text: string, public sourceSpan: ParseSourceSpan) {} } @@ -603,7 +650,7 @@ export interface CookedRawString { const escapeSlashes = (str: string): string => str.replace(/\\/g, '\\\\'); const escapeStartingColon = (str: string): string => str.replace(/^:/, '\\:'); const escapeColons = (str: string): string => str.replace(/:/g, '\\:'); -const escapeForMessagePart = (str: string): string => +const escapeForTemplateLiteral = (str: string): string => str.replace(/`/g, '\\`').replace(/\${/g, '$\\{'); /** @@ -625,13 +672,13 @@ function createCookedRawString( if (metaBlock === '') { return { cooked: messagePart, - raw: escapeForMessagePart(escapeStartingColon(escapeSlashes(messagePart))), + raw: escapeForTemplateLiteral(escapeStartingColon(escapeSlashes(messagePart))), range, }; } else { return { cooked: `:${metaBlock}:${messagePart}`, - raw: escapeForMessagePart( + raw: escapeForTemplateLiteral( `:${escapeColons(escapeSlashes(metaBlock))}:${escapeSlashes(messagePart)}`), range, }; @@ -953,6 +1000,7 @@ export interface ExpressionVisitor { visitWritePropExpr(expr: WritePropExpr, context: any): any; visitInvokeMethodExpr(ast: InvokeMethodExpr, context: any): any; visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: any): any; + visitTaggedTemplateExpr(ast: TaggedTemplateExpr, context: any): any; visitInstantiateExpr(ast: InstantiateExpr, context: any): any; visitLiteralExpr(ast: LiteralExpr, context: any): any; visitLocalizedString(ast: LocalizedString, context: any): any; @@ -1275,6 +1323,17 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor { context); } + visitTaggedTemplateExpr(ast: TaggedTemplateExpr, context: any): any { + return this.transformExpr( + new TaggedTemplateExpr( + ast.tag.visitExpression(this, context), + new TemplateLiteral( + ast.template.elements, + ast.template.expressions.map((e) => e.visitExpression(this, context))), + ast.type, ast.sourceSpan), + context); + } + visitInstantiateExpr(ast: InstantiateExpr, context: any): any { return this.transformExpr( new InstantiateExpr( @@ -1378,7 +1437,7 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor { return this.transformExpr( new CommaExpr(this.visitAllExpressions(ast.parts, context), ast.sourceSpan), context); } - visitAllExpressions(exprs: Expression[], context: any): Expression[] { + visitAllExpressions(exprs: T[], context: any): T[] { return exprs.map(expr => expr.visitExpression(this, context)); } @@ -1524,6 +1583,11 @@ export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor this.visitAllExpressions(ast.args, context); return this.visitExpression(ast, context); } + visitTaggedTemplateExpr(ast: TaggedTemplateExpr, context: any): any { + ast.tag.visitExpression(this, context); + this.visitAllExpressions(ast.template.expressions, context); + return this.visitExpression(ast, context); + } visitInstantiateExpr(ast: InstantiateExpr, context: any): any { ast.classExpr.visitExpression(this, context); this.visitAllExpressions(ast.args, context); @@ -1808,6 +1872,12 @@ export function ifStmt( return new IfStmt(condition, thenClause, elseClause, sourceSpan, leadingComments); } +export function taggedTemplate( + tag: Expression, template: TemplateLiteral, type?: Type|null, + sourceSpan?: ParseSourceSpan|null): TaggedTemplateExpr { + return new TaggedTemplateExpr(tag, template, type, sourceSpan); +} + export function literal( value: any, type?: Type|null, sourceSpan?: ParseSourceSpan|null): LiteralExpr { return new LiteralExpr(value, type, sourceSpan); diff --git a/packages/compiler/src/output/output_interpreter.ts b/packages/compiler/src/output/output_interpreter.ts index 72ae1871ca..22d7dbec83 100644 --- a/packages/compiler/src/output/output_interpreter.ts +++ b/packages/compiler/src/output/output_interpreter.ts @@ -197,6 +197,15 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor { return fn.apply(null, args); } } + visitTaggedTemplateExpr(expr: o.TaggedTemplateExpr, ctx: _ExecutionContext): any { + const templateElements = expr.template.elements.map((e) => e.text); + Object.defineProperty( + templateElements, 'raw', {value: expr.template.elements.map((e) => e.rawText)}); + const args = this.visitAllExpressions(expr.template.expressions, ctx); + args.unshift(templateElements); + const tag = expr.tag.visitExpression(this, ctx); + return tag.apply(null, args); + } visitReturnStmt(stmt: o.ReturnStatement, ctx: _ExecutionContext): any { return new ReturnValue(stmt.value.visitExpression(this, ctx)); } diff --git a/packages/compiler/src/output/output_jit_trusted_types.ts b/packages/compiler/src/output/output_jit_trusted_types.ts index 8d831e592a..b7e0fea059 100644 --- a/packages/compiler/src/output/output_jit_trusted_types.ts +++ b/packages/compiler/src/output/output_jit_trusted_types.ts @@ -93,9 +93,7 @@ function trustedScriptFromString(script: string): TrustedScript|string { } /** - * Unsafely call the Function constructor with the given string arguments. It - * is only available in development mode, and should be stripped out of - * production code. + * Unsafely call the Function constructor with the given string arguments. * @security This is a security-sensitive function; any use of this function * must go through security review. In particular, it must be assured that it * is only called from the JIT compiler, as use in other code can lead to XSS @@ -113,7 +111,7 @@ export function newTrustedFunctionForJIT(...args: string[]): Function { // below, where the Chromium bug is also referenced: // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor const fnArgs = args.slice(0, -1).join(','); - const fnBody = args.pop()!.toString(); + const fnBody = args[args.length - 1]; const body = `(function anonymous(${fnArgs} ) { ${fnBody} })`; @@ -122,6 +120,13 @@ export function newTrustedFunctionForJIT(...args: string[]): Function { // being stripped out of JS binaries even if not used. The global['eval'] // indirection fixes that. const fn = global['eval'](trustedScriptFromString(body) as string) as Function; + if (fn.bind === undefined) { + // Workaround for a browser bug that only exists in Chrome 83, where passing + // a TrustedScript to eval just returns the TrustedScript back without + // evaluating it. In that case, fall back to the most straightforward + // implementation: + return new Function(...args); + } // To completely mimic the behavior of calling "new Function", two more // things need to happen: diff --git a/packages/compiler/src/render3/partial/api.ts b/packages/compiler/src/render3/partial/api.ts index bb4abd04e7..124e953291 100644 --- a/packages/compiler/src/render3/partial/api.ts +++ b/packages/compiler/src/render3/partial/api.ts @@ -6,33 +6,36 @@ * found in the LICENSE file at https://angular.io/license */ import {ChangeDetectionStrategy, ViewEncapsulation} from '../../core'; -import {InterpolationConfig} from '../../ml_parser/interpolation_config'; import * as o from '../../output/output_ast'; -/** - * This interface describes the shape of the object that partial directive declarations are compiled - * into. This serves only as documentation, as conformance of this interface is not enforced during - * the generation of the partial declaration, nor when the linker applies full compilation from the - * partial declaration. - */ -export interface R3DeclareDirectiveMetadata { +export interface R3PartialDeclaration { /** - * Version number of the metadata format. This is used to evolve the metadata - * interface later - the linker will be able to detect which version a library - * is using and interpret its metadata accordingly. + * Version number of the Angular compiler that was used to compile this declaration. The linker + * will be able to detect which version a library is using and interpret its metadata accordingly. */ - version: 1; + version: string; + /** + * A reference to the `@angular/core` ES module, which allows access + * to all Angular exports, including Ivy instructions. + */ + ngImport: o.Expression; + + /** + * Reference to the decorated class, which is subject to this partial declaration. + */ + type: o.Expression; +} + +/** + * Describes the shape of the object that the `ɵɵngDeclareDirective()` function accepts. + */ +export interface R3DeclareDirectiveMetadata extends R3PartialDeclaration { /** * Unparsed selector of the directive. */ selector?: string; - /** - * Reference to the directive class itself. - */ - type: o.Expression; - /** * A mapping of inputs from class property names to binding property names, or to a tuple of * binding property name and class property name if the names are different. @@ -40,8 +43,7 @@ export interface R3DeclareDirectiveMetadata { inputs?: {[classPropertyName: string]: string|[string, string]}; /** - * A mapping of outputs from class property names to binding property names, or to a tuple of - * binding property name and class property name if the names are different. + * A mapping of outputs from class property names to binding property names. */ outputs?: {[classPropertyName: string]: string}; @@ -106,78 +108,50 @@ export interface R3DeclareDirectiveMetadata { * Whether the directive implements the `ngOnChanges` hook. Defaults to false. */ usesOnChanges?: boolean; - - /** - * A reference to the `@angular/core` ES module, which allows access - * to all Angular exports, including Ivy instructions. - */ - ngImport: o.Expression; } /** - * An extension of `R3DeclareDirectiveMetadata` that declares the shape of a partial declaration of - * a component. + * Describes the shape of the object that the `ɵɵngDeclareComponent()` function accepts. */ export interface R3DeclareComponentMetadata extends R3DeclareDirectiveMetadata { /** - * Information about the component's template. + * The component's unparsed template string as opaque expression. The template is represented + * using either a string literal or template literal without substitutions, but its value is + * not read directly. Instead, the template parser is given the full source file's text and + * the range of this expression to parse directly from source. */ - template: { - /** - * The component's unparsed template string as opaque expression. The template is represented - * using either a string literal or template literal without substitutions, but its value is - * not read directly. Instead, the template parser is given the full source file's text and - * the range of this expression to parse directly from source. - */ - source: o.Expression; + template: o.Expression; - /** - * Whether the template was inline (using `template`) or external (using `templateUrl`). - */ - isInline: boolean; - }; + /** + * Whether the template was inline (using `template`) or external (using `templateUrl`). + * Defaults to false. + */ + isInline?: boolean; /** * CSS from inline styles and included styleUrls. */ styles?: string[]; + /** + * List of components which matched in the template, including sufficient + * metadata for each directive to attribute bindings and references within + * the template to each directive specifically, if the runtime instructions + * support this. + */ + components?: R3DeclareUsedDirectiveMetadata[]; + /** * List of directives which matched in the template, including sufficient * metadata for each directive to attribute bindings and references within * the template to each directive specifically, if the runtime instructions * support this. */ - directives?: { - /** - * Selector of the directive. - */ - selector: string; - - /** - * Reference to the directive class (possibly a forward reference). - */ - type: o.Expression | (() => o.Expression); - - /** - * Property names of the directive's inputs. - */ - inputs?: string[]; - - /** - * Event names of the directive's outputs. - */ - outputs?: string[]; - - /** - * Names by which this directive exports itself for references. - */ - exportAs?: string[]; - }[]; + directives?: R3DeclareUsedDirectiveMetadata[]; /** - * A map of pipe names to an expression referencing the pipe type (possibly a forward reference) - * which are used in the template. + * A map of pipe names to an expression referencing the pipe type (possibly a forward reference + * wrapped in a `forwardRef` invocation) which are used in the template. */ pipes?: {[pipeName: string]: o.Expression|(() => o.Expression)}; @@ -206,7 +180,7 @@ export interface R3DeclareComponentMetadata extends R3DeclareDirectiveMetadata { /** * Overrides the default interpolation start and end delimiters. Defaults to {{ and }}. */ - interpolation?: InterpolationConfig; + interpolation?: [string, string]; /** * Whether whitespace in the template should be preserved. Defaults to false. @@ -214,6 +188,34 @@ export interface R3DeclareComponentMetadata extends R3DeclareDirectiveMetadata { preserveWhitespaces?: boolean; } +export interface R3DeclareUsedDirectiveMetadata { + /** + * Selector of the directive. + */ + selector: string; + + /** + * Reference to the directive class (possibly a forward reference wrapped in a `forwardRef` + * invocation). + */ + type: o.Expression|(() => o.Expression); + + /** + * Property names of the directive's inputs. + */ + inputs?: string[]; + + /** + * Event names of the directive's outputs. + */ + outputs?: string[]; + + /** + * Names by which this directive exports itself for references. + */ + exportAs?: string[]; +} + export interface R3DeclareQueryMetadata { /** * Name of the property on the class to update with query results. @@ -236,6 +238,11 @@ export interface R3DeclareQueryMetadata { */ descendants?: boolean; + /** + * True to only fire changes if there are underlying changes to the query. + */ + emitDistinctChangesOnly?: boolean; + /** * An expression representing a type to read from each matched node, or null if the default value * for a given node is to be returned. @@ -257,3 +264,149 @@ export interface R3DeclareQueryMetadata { */ static?: boolean; } + +/** + * Describes the shape of the objects that the `ɵɵngDeclareNgModule()` accepts. + */ +export interface R3DeclareNgModuleMetadata extends R3PartialDeclaration { + /** + * An array of expressions representing the bootstrap components specified by the module. + */ + bootstrap?: o.Expression[]; + + /** + * An array of expressions representing the directives and pipes declared by the module. + */ + declarations?: o.Expression[]; + + /** + * An array of expressions representing the imports of the module. + */ + imports?: o.Expression[]; + + /** + * An array of expressions representing the exports of the module. + */ + exports?: o.Expression[]; + + /** + * The set of schemas that declare elements to be allowed in the NgModule. + */ + schemas?: o.Expression[]; + + /** Unique ID or expression representing the unique ID of an NgModule. */ + id?: o.Expression; +} + +/** + * Describes the shape of the objects that the `ɵɵngDeclareInjector()` accepts. + */ +export interface R3DeclareInjectorMetadata extends R3PartialDeclaration { + /** + * The list of providers provided by the injector. + */ + providers?: o.Expression; + /** + * The list of imports into the injector. + */ + imports?: o.Expression[]; +} + +/** + * Describes the shape of the object that the `ɵɵngDeclarePipe()` function accepts. + * + * This interface serves primarily as documentation, as conformance to this interface is not + * enforced during linking. + */ +export interface R3DeclarePipeMetadata extends R3PartialDeclaration { + /** + * The name to use in templates to refer to this pipe. + */ + name: string; + + /** + * Whether this pipe is "pure". + * + * A pure pipe's `transform()` method is only invoked when its input arguments change. + * + * Default: true. + */ + pure?: boolean; +} + + +/** + * Describes the shape of the object that the `ɵɵngDeclareFactory()` function accepts. + * + * This interface serves primarily as documentation, as conformance to this interface is not + * enforced during linking. + */ +export interface R3DeclareFactoryMetadata extends R3PartialDeclaration { + /** + * A collection of dependencies that this factory relies upon. + * + * If this is `null`, then the type's constructor is nonexistent and will be inherited from an + * ancestor of the type. + * + * If this is `'invalid'`, then one or more of the parameters wasn't resolvable and any attempt to + * use these deps will result in a runtime error. + */ + deps: R3DeclareDependencyMetadata[]|'invalid'|null; + + /** + * Type of the target being created by the factory. + */ + target: FactoryTarget; +} + +export enum FactoryTarget { + Directive = 0, + Component = 1, + Injectable = 2, + Pipe = 3, + NgModule = 4, +} + +/** + * Metadata indicating how a dependency should be injected into a factory. + */ +export interface R3DeclareDependencyMetadata { + /** + * An expression representing the token or value to be injected, or `null` if the dependency is + * not valid. + * + * If this dependency is due to the `@Attribute()` decorator, then this is an expression + * evaluating to the name of the attribute. + */ + token: o.Expression|null; + + /** + * Whether the dependency is injecting an attribute value. + * Default: false. + */ + attribute?: boolean; + + /** + * Whether the dependency has an @Host qualifier. + * Default: false, + */ + host?: boolean; + + /** + * Whether the dependency has an @Optional qualifier. + * Default: false, + */ + optional?: boolean; + + /** + * Whether the dependency has an @Self qualifier. + * Default: false, + */ + self?: boolean; + + /** + * Whether the dependency has an @SkipSelf qualifier. + * Default: false, + */ + skipSelf?: boolean; +} diff --git a/packages/compiler/src/render3/partial/component.ts b/packages/compiler/src/render3/partial/component.ts index 050207051b..d54f1e7b84 100644 --- a/packages/compiler/src/render3/partial/component.ts +++ b/packages/compiler/src/render3/partial/component.ts @@ -8,12 +8,15 @@ import * as core from '../../core'; import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config'; import * as o from '../../output/output_ast'; +import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../parse_util'; import {Identifiers as R3} from '../r3_identifiers'; -import {R3ComponentDef, R3ComponentMetadata} from '../view/api'; +import {R3CompiledExpression} from '../util'; +import {DeclarationListEmitMode, R3ComponentMetadata, R3UsedDirectiveMetadata} from '../view/api'; import {createComponentType} from '../view/compiler'; import {ParsedTemplate} from '../view/template'; import {DefinitionMap} from '../view/util'; +import {R3DeclareComponentMetadata, R3DeclareUsedDirectiveMetadata} from './api'; import {createDirectiveDefinitionMap} from './directive'; import {toOptionalLiteralArray} from './util'; @@ -22,28 +25,35 @@ import {toOptionalLiteralArray} from './util'; * Compile a component declaration defined by the `R3ComponentMetadata`. */ export function compileDeclareComponentFromMetadata( - meta: R3ComponentMetadata, template: ParsedTemplate): R3ComponentDef { + meta: R3ComponentMetadata, template: ParsedTemplate): R3CompiledExpression { const definitionMap = createComponentDefinitionMap(meta, template); const expression = o.importExpr(R3.declareComponent).callFn([definitionMap.toLiteralMap()]); const type = createComponentType(meta); - return {expression, type}; + return {expression, type, statements: []}; } /** * Gathers the declaration fields for a component into a `DefinitionMap`. */ -export function createComponentDefinitionMap( - meta: R3ComponentMetadata, template: ParsedTemplate): DefinitionMap { - const definitionMap = createDirectiveDefinitionMap(meta); +export function createComponentDefinitionMap(meta: R3ComponentMetadata, template: ParsedTemplate): + DefinitionMap { + const definitionMap: DefinitionMap = + createDirectiveDefinitionMap(meta); - const templateMap = compileTemplateDefinition(template); - - definitionMap.set('template', templateMap); + definitionMap.set('template', getTemplateExpression(template)); + if (template.isInline) { + definitionMap.set('isInline', o.literal(true)); + } definitionMap.set('styles', toOptionalLiteralArray(meta.styles, o.literal)); - definitionMap.set('directives', compileUsedDirectiveMetadata(meta)); + definitionMap.set( + 'components', + compileUsedDirectiveMetadata(meta, directive => directive.isComponent === true)); + definitionMap.set( + 'directives', + compileUsedDirectiveMetadata(meta, directive => directive.isComponent !== true)); definitionMap.set('pipes', compileUsedPipeMetadata(meta)); definitionMap.set('viewProviders', meta.viewProviders); definitionMap.set('animations', meta.animations); @@ -72,29 +82,58 @@ export function createComponentDefinitionMap( return definitionMap; } -/** - * Compiles the provided template into its partial definition. - */ -function compileTemplateDefinition(template: ParsedTemplate): o.LiteralMapExpr { - const templateMap = new DefinitionMap(); - const templateExpr = - typeof template.template === 'string' ? o.literal(template.template) : template.template; - templateMap.set('source', templateExpr); - templateMap.set('isInline', o.literal(template.isInline)); - return templateMap.toLiteralMap(); +function getTemplateExpression(template: ParsedTemplate): o.Expression { + if (typeof template.template === 'string') { + if (template.isInline) { + // The template is inline but not a simple literal string, so give up with trying to + // source-map it and just return a simple literal here. + return o.literal(template.template); + } else { + // The template is external so we must synthesize an expression node with the appropriate + // source-span. + const contents = template.template; + const file = new ParseSourceFile(contents, template.templateUrl); + const start = new ParseLocation(file, 0, 0, 0); + const end = computeEndLocation(file, contents); + const span = new ParseSourceSpan(start, end); + return o.literal(contents, null, span); + } + } else { + // The template is inline so we can just reuse the current expression node. + return template.template; + } +} + +function computeEndLocation(file: ParseSourceFile, contents: string): ParseLocation { + const length = contents.length; + let lineStart = 0; + let lastLineStart = 0; + let line = 0; + do { + lineStart = contents.indexOf('\n', lastLineStart); + if (lineStart !== -1) { + lastLineStart = lineStart + 1; + line++; + } + } while (lineStart !== -1); + + return new ParseLocation(file, length, line, length - lastLineStart); } /** * Compiles the directives as registered in the component metadata into an array literal of the * individual directives. If the component does not use any directives, then null is returned. */ -function compileUsedDirectiveMetadata(meta: R3ComponentMetadata): o.LiteralArrayExpr|null { - const wrapType = meta.wrapDirectivesAndPipesInClosure ? - (expr: o.Expression) => o.fn([], [new o.ReturnStatement(expr)]) : +function compileUsedDirectiveMetadata( + meta: R3ComponentMetadata, + predicate: (directive: R3UsedDirectiveMetadata) => boolean): o.LiteralArrayExpr|null { + const wrapType = meta.declarationListEmitMode !== DeclarationListEmitMode.Direct ? + generateForwardRef : (expr: o.Expression) => expr; - return toOptionalLiteralArray(meta.directives, directive => { - const dirMeta = new DefinitionMap(); + const directives = meta.directives.filter(predicate); + return toOptionalLiteralArray(directives, directive => { + const dirMeta = new DefinitionMap(); dirMeta.set('type', wrapType(directive.type)); dirMeta.set('selector', o.literal(directive.selector)); dirMeta.set('inputs', toOptionalLiteralArray(directive.inputs, o.literal)); @@ -114,8 +153,8 @@ function compileUsedPipeMetadata(meta: R3ComponentMetadata): o.LiteralMapExpr|nu return null; } - const wrapType = meta.wrapDirectivesAndPipesInClosure ? - (expr: o.Expression) => o.fn([], [new o.ReturnStatement(expr)]) : + const wrapType = meta.declarationListEmitMode !== DeclarationListEmitMode.Direct ? + generateForwardRef : (expr: o.Expression) => expr; const entries = []; @@ -124,3 +163,7 @@ function compileUsedPipeMetadata(meta: R3ComponentMetadata): o.LiteralMapExpr|nu } return o.literalMap(entries); } + +function generateForwardRef(expr: o.Expression): o.Expression { + return o.importExpr(R3.forwardRef).callFn([o.fn([], [new o.ReturnStatement(expr)])]); +} diff --git a/packages/compiler/src/render3/partial/directive.ts b/packages/compiler/src/render3/partial/directive.ts index 06879c7608..a14c3c5ef6 100644 --- a/packages/compiler/src/render3/partial/directive.ts +++ b/packages/compiler/src/render3/partial/directive.ts @@ -7,32 +7,36 @@ */ import * as o from '../../output/output_ast'; import {Identifiers as R3} from '../r3_identifiers'; -import {R3DirectiveDef, R3DirectiveMetadata, R3HostMetadata, R3QueryMetadata} from '../view/api'; +import {R3CompiledExpression} from '../util'; +import {R3DirectiveMetadata, R3HostMetadata, R3QueryMetadata} from '../view/api'; import {createDirectiveType} from '../view/compiler'; import {asLiteral, conditionallyCreateMapObjectLiteral, DefinitionMap} from '../view/util'; +import {R3DeclareDirectiveMetadata, R3DeclareQueryMetadata} from './api'; import {toOptionalLiteralMap} from './util'; /** * Compile a directive declaration defined by the `R3DirectiveMetadata`. */ -export function compileDeclareDirectiveFromMetadata(meta: R3DirectiveMetadata): R3DirectiveDef { +export function compileDeclareDirectiveFromMetadata(meta: R3DirectiveMetadata): + R3CompiledExpression { const definitionMap = createDirectiveDefinitionMap(meta); const expression = o.importExpr(R3.declareDirective).callFn([definitionMap.toLiteralMap()]); const type = createDirectiveType(meta); - return {expression, type}; + return {expression, type, statements: []}; } /** * Gathers the declaration fields for a directive into a `DefinitionMap`. This allows for reusing * this logic for components, as they extend the directive metadata. */ -export function createDirectiveDefinitionMap(meta: R3DirectiveMetadata): DefinitionMap { - const definitionMap = new DefinitionMap(); +export function createDirectiveDefinitionMap(meta: R3DirectiveMetadata): + DefinitionMap { + const definitionMap = new DefinitionMap(); - definitionMap.set('version', o.literal(1)); + definitionMap.set('version', o.literal('0.0.0-PLACEHOLDER')); // e.g. `type: MyDirective` definitionMap.set('type', meta.internalType); @@ -77,13 +81,20 @@ export function createDirectiveDefinitionMap(meta: R3DirectiveMetadata): Definit * by `R3DeclareQueryMetadata`. */ function compileQuery(query: R3QueryMetadata): o.LiteralMapExpr { - const meta = new DefinitionMap(); + const meta = new DefinitionMap(); meta.set('propertyName', o.literal(query.propertyName)); if (query.first) { meta.set('first', o.literal(true)); } meta.set( 'predicate', Array.isArray(query.predicate) ? asLiteral(query.predicate) : query.predicate); + if (!query.emitDistinctChangesOnly) { + // `emitDistinctChangesOnly` is special because we expect it to be `true`. + // Therefore we explicitly emit the field, and explicitly place it only when it's `false`. + meta.set('emitDistinctChangesOnly', o.literal(false)); + } else { + // The linker will assume that an absent `emitDistinctChangesOnly` flag is by default `true`. + } if (query.descendants) { meta.set('descendants', o.literal(true)); } @@ -99,7 +110,7 @@ function compileQuery(query: R3QueryMetadata): o.LiteralMapExpr { * in `R3DeclareDirectiveMetadata['host']` */ function compileHostMetadata(meta: R3HostMetadata): o.LiteralMapExpr|null { - const hostMetadata = new DefinitionMap(); + const hostMetadata = new DefinitionMap>(); hostMetadata.set('attributes', toOptionalLiteralMap(meta.attributes, expression => expression)); hostMetadata.set('listeners', toOptionalLiteralMap(meta.listeners, o.literal)); hostMetadata.set('properties', toOptionalLiteralMap(meta.properties, o.literal)); diff --git a/packages/compiler/src/render3/r3_ast.ts b/packages/compiler/src/render3/r3_ast.ts index dc121bbc05..dc7e452a11 100644 --- a/packages/compiler/src/render3/r3_ast.ts +++ b/packages/compiler/src/render3/r3_ast.ts @@ -16,6 +16,19 @@ export interface Node { visit(visitor: Visitor): Result; } +/** + * This is an R3 `Node`-like wrapper for a raw `html.Comment` node. We do not currently + * require the implementation of a visitor for Comments as they are only collected at + * the top-level of the R3 AST, and only if `Render3ParseOptions['collectCommentNodes']` + * is true. + */ +export class Comment implements Node { + constructor(public value: string, public sourceSpan: ParseSourceSpan) {} + visit(_visitor: Visitor): Result { + throw new Error('visit() not implemented for Comment'); + } +} + export class Text implements Node { constructor(public value: string, public sourceSpan: ParseSourceSpan) {} visit(visitor: Visitor): Result { diff --git a/packages/compiler/src/render3/r3_factory.ts b/packages/compiler/src/render3/r3_factory.ts index c1cb7c923b..8cc4dbfc06 100644 --- a/packages/compiler/src/render3/r3_factory.ts +++ b/packages/compiler/src/render3/r3_factory.ts @@ -8,14 +8,12 @@ import {StaticSymbol} from '../aot/static_symbol'; import {CompileTypeMetadata, tokenReference} from '../compile_metadata'; -import {CompileReflector} from '../compile_reflector'; import {InjectFlags} from '../core'; -import {Identifiers} from '../identifiers'; import * as o from '../output/output_ast'; import {Identifiers as R3} from '../render3/r3_identifiers'; import {OutputContext} from '../util'; -import {R3Reference, typeWithParameters} from './util'; +import {R3CompiledExpression, R3Reference, typeWithParameters} from './util'; import {unsupported} from './view/util'; @@ -56,32 +54,20 @@ export interface R3ConstructorFactoryMetadata { */ deps: R3DependencyMetadata[]|'invalid'|null; - /** - * An expression for the function which will be used to inject dependencies. The API of this - * function could be different, and other options control how it will be invoked. - */ - injectFn: o.ExternalReference; - /** * Type of the target being created by the factory. */ - target: R3FactoryTarget; + target: FactoryTarget; } export enum R3FactoryDelegateType { - Class, - Function, - Factory, -} - -export interface R3DelegatedFactoryMetadata extends R3ConstructorFactoryMetadata { - delegate: o.Expression; - delegateType: R3FactoryDelegateType.Factory; + Class = 0, + Function = 1, } export interface R3DelegatedFnOrClassMetadata extends R3ConstructorFactoryMetadata { delegate: o.Expression; - delegateType: R3FactoryDelegateType.Class|R3FactoryDelegateType.Function; + delegateType: R3FactoryDelegateType; delegateDeps: R3DependencyMetadata[]; } @@ -89,10 +75,10 @@ export interface R3ExpressionFactoryMetadata extends R3ConstructorFactoryMetadat expression: o.Expression; } -export type R3FactoryMetadata = R3ConstructorFactoryMetadata|R3DelegatedFactoryMetadata| - R3DelegatedFnOrClassMetadata|R3ExpressionFactoryMetadata; +export type R3FactoryMetadata = + R3ConstructorFactoryMetadata|R3DelegatedFnOrClassMetadata|R3ExpressionFactoryMetadata; -export enum R3FactoryTarget { +export enum FactoryTarget { Directive = 0, Component = 1, Injectable = 2, @@ -100,59 +86,19 @@ export enum R3FactoryTarget { NgModule = 4, } -/** - * Resolved type of a dependency. - * - * Occasionally, dependencies will have special significance which is known statically. In that - * case the `R3ResolvedDependencyType` informs the factory generator that a particular dependency - * should be generated specially (usually by calling a special injection function instead of the - * standard one). - */ -export enum R3ResolvedDependencyType { - /** - * A normal token dependency. - */ - Token = 0, - - /** - * The dependency is for an attribute. - * - * The token expression is a string representing the attribute name. - */ - Attribute = 1, - - /** - * Injecting the `ChangeDetectorRef` token. Needs special handling when injected into a pipe. - */ - ChangeDetectorRef = 2, - - /** - * An invalid dependency (no token could be determined). An error should be thrown at runtime. - */ - Invalid = 3, -} - -/** - * Metadata representing a single dependency to be injected into a constructor or function call. - */ export interface R3DependencyMetadata { /** * An expression representing the token or value to be injected. + * Or `null` if the dependency could not be resolved - making it invalid. */ - token: o.Expression; + token: o.Expression|null; /** * 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. + * Otherwise it is null; */ - attribute: o.Expression|null; - - /** - * An enum indicating whether this dependency has special meaning to Angular and needs to be - * injected specially. - */ - resolved: R3ResolvedDependencyType; + attributeNameType: o.Expression|null; /** * Whether the dependency has an @Host qualifier. @@ -175,26 +121,19 @@ export interface R3DependencyMetadata { skipSelf: boolean; } -export interface R3FactoryFn { - factory: o.Expression; - statements: o.Statement[]; - type: o.ExpressionType; -} - /** * Construct a factory function expression for the given `R3FactoryMetadata`. */ -export function compileFactoryFunction(meta: R3FactoryMetadata): R3FactoryFn { +export function compileFactoryFunction(meta: R3FactoryMetadata): R3CompiledExpression { const t = o.variable('t'); - const statements: o.Statement[] = []; - let ctorDepsType: o.Type = o.NONE_TYPE; + let baseFactoryVar: o.ReadVarExpr|null = null; // 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 // parameter provided by the user (t) if specified, or the current type if not. If there is a // delegated factory (which is used to create the current type) then this is only the type-to- // create parameter (t). - const typeForCtor = !isDelegatedMetadata(meta) ? + const typeForCtor = !isDelegatedFactoryMetadata(meta) ? new o.BinaryOperatorExpr(o.BinaryOperator.Or, t, meta.internalType) : t; @@ -202,26 +141,13 @@ export function compileFactoryFunction(meta: R3FactoryMetadata): R3FactoryFn { if (meta.deps !== null) { // There is a constructor (either explicitly or implicitly defined). if (meta.deps !== 'invalid') { - ctorExpr = new o.InstantiateExpr( - typeForCtor, - injectDependencies(meta.deps, meta.injectFn, meta.target === R3FactoryTarget.Pipe)); - - ctorDepsType = createCtorDepsType(meta.deps); + ctorExpr = new o.InstantiateExpr(typeForCtor, injectDependencies(meta.deps, meta.target)); } } else { - const baseFactory = o.variable(`ɵ${meta.name}_BaseFactory`); - const getInheritedFactory = o.importExpr(R3.getInheritedFactory); - const baseFactoryStmt = - baseFactory - .set(getInheritedFactory.callFn( - [meta.internalType], /* sourceSpan */ undefined, /* pure */ true)) - .toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Exported, o.StmtModifier.Final]); - statements.push(baseFactoryStmt); - // There is no constructor, use the base class' factory to construct typeForCtor. - ctorExpr = baseFactory.callFn([typeForCtor]); + baseFactoryVar = o.variable(`ɵ${meta.name}_BaseFactory`); + ctorExpr = baseFactoryVar.callFn([typeForCtor]); } - const ctorExprFinal = ctorExpr; const body: o.Statement[] = []; let retExpr: o.Expression|null = null; @@ -229,34 +155,16 @@ export function compileFactoryFunction(meta: R3FactoryMetadata): R3FactoryFn { function makeConditionalFactory(nonCtorExpr: o.Expression): o.ReadVarExpr { const r = o.variable('r'); body.push(r.set(o.NULL_EXPR).toDeclStmt()); - let ctorStmt: o.Statement|null = null; - if (ctorExprFinal !== null) { - ctorStmt = r.set(ctorExprFinal).toStmt(); - } else { - ctorStmt = o.importExpr(R3.invalidFactory).callFn([]).toStmt(); - } + const ctorStmt = ctorExpr !== null ? r.set(ctorExpr).toStmt() : + o.importExpr(R3.invalidFactory).callFn([]).toStmt(); body.push(o.ifStmt(t, [ctorStmt], [r.set(nonCtorExpr).toStmt()])); return r; } - if (isDelegatedMetadata(meta) && meta.delegateType === R3FactoryDelegateType.Factory) { - const delegateFactory = o.variable(`ɵ${meta.name}_BaseFactory`); - const getFactoryOf = o.importExpr(R3.getFactoryOf); - if (meta.delegate.isEquivalent(meta.internalType)) { - throw new Error(`Illegal state: compiling factory that delegates to itself`); - } - const delegateFactoryStmt = - delegateFactory.set(getFactoryOf.callFn([meta.delegate])).toDeclStmt(o.INFERRED_TYPE, [ - o.StmtModifier.Exported, o.StmtModifier.Final - ]); - - statements.push(delegateFactoryStmt); - retExpr = makeConditionalFactory(delegateFactory.callFn([])); - } else if (isDelegatedMetadata(meta)) { + if (isDelegatedFactoryMetadata(meta)) { // This type is created with a delegated factory. If a type parameter is not specified, call // the factory instead. - const delegateArgs = - injectDependencies(meta.delegateDeps, meta.injectFn, meta.target === R3FactoryTarget.Pipe); + const delegateArgs = injectDependencies(meta.delegateDeps, meta.target); // Either call `new delegate(...)` or `delegate(...)` depending on meta.delegateType. const factoryExpr = new ( meta.delegateType === R3FactoryDelegateType.Class ? @@ -270,64 +178,88 @@ export function compileFactoryFunction(meta: R3FactoryMetadata): R3FactoryFn { retExpr = ctorExpr; } - if (retExpr !== null) { - body.push(new o.ReturnStatement(retExpr)); - } else { + + if (retExpr === null) { + // The expression cannot be formed so render an `ɵɵinvalidFactory()` call. body.push(o.importExpr(R3.invalidFactory).callFn([]).toStmt()); + } else if (baseFactoryVar !== null) { + // This factory uses a base factory, so call `ɵɵgetInheritedFactory()` to compute it. + const getInheritedFactoryCall = + o.importExpr(R3.getInheritedFactory).callFn([meta.internalType]); + // Memoize the base factoryFn: `baseFactory || (baseFactory = ɵɵgetInheritedFactory(...))` + const baseFactory = new o.BinaryOperatorExpr( + o.BinaryOperator.Or, baseFactoryVar, baseFactoryVar.set(getInheritedFactoryCall)); + body.push(new o.ReturnStatement(baseFactory.callFn([typeForCtor]))); + } else { + // This is straightforward factory, just return it. + body.push(new o.ReturnStatement(retExpr)); + } + + let factoryFn: o.Expression = o.fn( + [new o.FnParam('t', o.DYNAMIC_TYPE)], body, o.INFERRED_TYPE, undefined, + `${meta.name}_Factory`); + + if (baseFactoryVar !== null) { + // There is a base factory variable so wrap its declaration along with the factory function into + // an IIFE. + factoryFn = o.fn([], [ + new o.DeclareVarStmt(baseFactoryVar.name!), new o.ReturnStatement(factoryFn) + ]).callFn([], /* sourceSpan */ undefined, /* pure */ true); } return { - factory: o.fn( - [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), ctorDepsType])) + expression: factoryFn, + statements: [], + type: createFactoryType(meta), }; } -function injectDependencies( - deps: R3DependencyMetadata[], injectFn: o.ExternalReference, isPipe: boolean): o.Expression[] { - return deps.map((dep, index) => compileInjectDependency(dep, injectFn, isPipe, index)); +export function createFactoryType(meta: R3FactoryMetadata) { + const ctorDepsType = + meta.deps !== null && meta.deps !== 'invalid' ? createCtorDepsType(meta.deps) : o.NONE_TYPE; + return o.expressionType(o.importExpr( + R3.FactoryDeclaration, + [typeWithParameters(meta.type.type, meta.typeArgumentCount), ctorDepsType])); +} + +function injectDependencies(deps: R3DependencyMetadata[], target: FactoryTarget): o.Expression[] { + return deps.map((dep, index) => compileInjectDependency(dep, target, index)); } function compileInjectDependency( - dep: R3DependencyMetadata, injectFn: o.ExternalReference, isPipe: boolean, - index: number): o.Expression { + dep: R3DependencyMetadata, target: FactoryTarget, index: number): o.Expression { // Interpret the dependency according to its resolved type. - switch (dep.resolved) { - case R3ResolvedDependencyType.Token: - case R3ResolvedDependencyType.ChangeDetectorRef: - // Build up the injection flags according to the metadata. - const flags = InjectFlags.Default | (dep.self ? InjectFlags.Self : 0) | - (dep.skipSelf ? InjectFlags.SkipSelf : 0) | (dep.host ? InjectFlags.Host : 0) | - (dep.optional ? InjectFlags.Optional : 0); + if (dep.token === null) { + return o.importExpr(R3.invalidFactoryDep).callFn([o.literal(index)]); + } else if (dep.attributeNameType === null) { + // Build up the injection flags according to the metadata. + const flags = InjectFlags.Default | (dep.self ? InjectFlags.Self : 0) | + (dep.skipSelf ? InjectFlags.SkipSelf : 0) | (dep.host ? InjectFlags.Host : 0) | + (dep.optional ? InjectFlags.Optional : 0) | + (target === FactoryTarget.Pipe ? InjectFlags.ForPipe : 0); - // If this dependency is optional or otherwise has non-default flags, then additional - // parameters describing how to inject the dependency must be passed to the inject function - // that's being used. - let flagsParam: o.LiteralExpr|null = - (flags !== InjectFlags.Default || dep.optional) ? o.literal(flags) : null; + // If this dependency is optional or otherwise has non-default flags, then additional + // parameters describing how to inject the dependency must be passed to the inject function + // that's being used. + let flagsParam: o.LiteralExpr|null = + (flags !== InjectFlags.Default || dep.optional) ? o.literal(flags) : null; - // We have a separate instruction for injecting ChangeDetectorRef into a pipe. - if (isPipe && dep.resolved === R3ResolvedDependencyType.ChangeDetectorRef) { - return o.importExpr(R3.injectPipeChangeDetectorRef).callFn(flagsParam ? [flagsParam] : []); - } - - // Build up the arguments to the injectFn call. - const injectArgs = [dep.token]; - if (flagsParam) { - injectArgs.push(flagsParam); - } - return o.importExpr(injectFn).callFn(injectArgs); - case R3ResolvedDependencyType.Attribute: - // In the case of attributes, the attribute name in question is given as the token. - return o.importExpr(R3.injectAttribute).callFn([dep.token]); - case R3ResolvedDependencyType.Invalid: - return o.importExpr(R3.invalidFactoryDep).callFn([o.literal(index)]); - default: - return unsupported( - `Unknown R3ResolvedDependencyType: ${R3ResolvedDependencyType[dep.resolved]}`); + // Build up the arguments to the injectFn call. + const injectArgs = [dep.token]; + if (flagsParam) { + injectArgs.push(flagsParam); + } + const injectFn = getInjectFn(target); + return o.importExpr(injectFn).callFn(injectArgs); + } else { + // The `dep.attributeTypeName` value is defined, which indicates that this is an `@Attribute()` + // type dependency. For the generated JS we still want to use the `dep.token` value in case the + // name given for the attribute is not a string literal. For example given `@Attribute(foo())`, + // we want to generate `ɵɵinjectAttribute(foo())`. + // + // The `dep.attributeTypeName` is only actually used (in `createCtorDepType()`) to generate + // typings. + return o.importExpr(R3.injectAttribute).callFn([dep.token]); } } @@ -353,10 +285,8 @@ function createCtorDepsType(deps: R3DependencyMetadata[]): o.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.attributeNameType !== null) { + entries.push({key: 'attribute', value: dep.attributeNameType, quoted: false}); } if (dep.optional) { entries.push({key: 'optional', value: o.literal(true), quoted: false}); @@ -374,55 +304,25 @@ function createCtorDepType(dep: R3DependencyMetadata): o.LiteralMapExpr|null { return entries.length > 0 ? o.literalMap(entries) : null; } -/** - * A helper function useful for extracting `R3DependencyMetadata` from a Render2 - * `CompileTypeMetadata` instance. - */ -export function dependenciesFromGlobalMetadata( - type: CompileTypeMetadata, outputCtx: OutputContext, - reflector: CompileReflector): R3DependencyMetadata[] { - // Use the `CompileReflector` to look up references to some well-known Angular types. These will - // be compared with the token to statically determine whether the token has significance to - // Angular, and set the correct `R3ResolvedDependencyType` as a result. - const injectorRef = reflector.resolveExternalReference(Identifiers.Injector); - - // Iterate through the type's DI dependencies and produce `R3DependencyMetadata` for each of them. - const deps: R3DependencyMetadata[] = []; - for (let dependency of type.diDeps) { - if (dependency.token) { - const tokenRef = tokenReference(dependency.token); - let resolved: R3ResolvedDependencyType = dependency.isAttribute ? - R3ResolvedDependencyType.Attribute : - R3ResolvedDependencyType.Token; - - // In the case of most dependencies, the token will be a reference to a type. Sometimes, - // however, it can be a string, in the case of older Angular code or @Attribute injection. - const token = - tokenRef instanceof StaticSymbol ? outputCtx.importExpr(tokenRef) : o.literal(tokenRef); - - // Construct the dependency. - deps.push({ - token, - attribute: null, - resolved, - host: !!dependency.isHost, - optional: !!dependency.isOptional, - self: !!dependency.isSelf, - skipSelf: !!dependency.isSkipSelf, - }); - } else { - unsupported('dependency without a token'); - } - } - - return deps; -} - -function isDelegatedMetadata(meta: R3FactoryMetadata): meta is R3DelegatedFactoryMetadata| - R3DelegatedFnOrClassMetadata { +export function isDelegatedFactoryMetadata(meta: R3FactoryMetadata): + meta is R3DelegatedFnOrClassMetadata { return (meta as any).delegateType !== undefined; } -function isExpressionFactoryMetadata(meta: R3FactoryMetadata): meta is R3ExpressionFactoryMetadata { +export function isExpressionFactoryMetadata(meta: R3FactoryMetadata): + meta is R3ExpressionFactoryMetadata { return (meta as any).expression !== undefined; } + +function getInjectFn(target: FactoryTarget): o.ExternalReference { + switch (target) { + case FactoryTarget.Component: + case FactoryTarget.Directive: + case FactoryTarget.Pipe: + return R3.directiveInject; + case FactoryTarget.NgModule: + case FactoryTarget.Injectable: + default: + return R3.inject; + } +} diff --git a/packages/compiler/src/render3/r3_identifiers.ts b/packages/compiler/src/render3/r3_identifiers.ts index 7f757c13dd..89f26c9813 100644 --- a/packages/compiler/src/render3/r3_identifiers.ts +++ b/packages/compiler/src/render3/r3_identifiers.ts @@ -219,9 +219,6 @@ export class Identifiers { static injectAttribute: o.ExternalReference = {name: 'ɵɵinjectAttribute', moduleName: CORE}; - static injectPipeChangeDetectorRef: - o.ExternalReference = {name: 'ɵɵinjectPipeChangeDetectorRef', moduleName: CORE}; - static directiveInject: o.ExternalReference = {name: 'ɵɵdirectiveInject', moduleName: CORE}; static invalidFactory: o.ExternalReference = {name: 'ɵɵinvalidFactory', moduleName: CORE}; static invalidFactoryDep: o.ExternalReference = {name: 'ɵɵinvalidFactoryDep', moduleName: CORE}; @@ -229,6 +226,9 @@ export class Identifiers { static templateRefExtractor: o.ExternalReference = {name: 'ɵɵtemplateRefExtractor', moduleName: CORE}; + static forwardRef: o.ExternalReference = {name: 'forwardRef', moduleName: CORE}; + static resolveForwardRef: o.ExternalReference = {name: 'resolveForwardRef', moduleName: CORE}; + static resolveWindow: o.ExternalReference = {name: 'ɵɵresolveWindow', moduleName: CORE}; static resolveDocument: o.ExternalReference = {name: 'ɵɵresolveDocument', moduleName: CORE}; static resolveBody: o.ExternalReference = {name: 'ɵɵresolveBody', moduleName: CORE}; @@ -247,36 +247,35 @@ export class Identifiers { moduleName: CORE, }; - static ComponentDefWithMeta: o.ExternalReference = { - name: 'ɵɵComponentDefWithMeta', + static ComponentDeclaration: o.ExternalReference = { + name: 'ɵɵComponentDeclaration', moduleName: CORE, }; - static FactoryDef: o.ExternalReference = { - name: 'ɵɵFactoryDef', + static FactoryDeclaration: o.ExternalReference = { + name: 'ɵɵFactoryDeclaration', moduleName: CORE, }; + static declareFactory: o.ExternalReference = {name: 'ɵɵngDeclareFactory', moduleName: CORE}; + static FactoryTarget: o.ExternalReference = {name: 'ɵɵFactoryTarget', moduleName: CORE}; static defineDirective: o.ExternalReference = {name: 'ɵɵdefineDirective', moduleName: CORE}; static declareDirective: o.ExternalReference = {name: 'ɵɵngDeclareDirective', moduleName: CORE}; - static DirectiveDefWithMeta: o.ExternalReference = { - name: 'ɵɵDirectiveDefWithMeta', + static DirectiveDeclaration: o.ExternalReference = { + name: 'ɵɵDirectiveDeclaration', moduleName: CORE, }; - static InjectorDef: o.ExternalReference = { - name: 'ɵɵInjectorDef', - moduleName: CORE, - }; + static InjectorDef: o.ExternalReference = {name: 'ɵɵInjectorDef', moduleName: CORE}; + static InjectorDeclaration: + o.ExternalReference = {name: 'ɵɵInjectorDeclaration', moduleName: CORE}; - static defineInjector: o.ExternalReference = { - name: 'ɵɵdefineInjector', - moduleName: CORE, - }; + static defineInjector: o.ExternalReference = {name: 'ɵɵdefineInjector', moduleName: CORE}; + static declareInjector: o.ExternalReference = {name: 'ɵɵngDeclareInjector', moduleName: CORE}; - static NgModuleDefWithMeta: o.ExternalReference = { - name: 'ɵɵNgModuleDefWithMeta', + static NgModuleDeclaration: o.ExternalReference = { + name: 'ɵɵNgModuleDeclaration', moduleName: CORE, }; @@ -286,16 +285,16 @@ export class Identifiers { }; static defineNgModule: o.ExternalReference = {name: 'ɵɵdefineNgModule', moduleName: CORE}; + static declareNgModule: o.ExternalReference = {name: 'ɵɵngDeclareNgModule', moduleName: CORE}; static setNgModuleScope: o.ExternalReference = {name: 'ɵɵsetNgModuleScope', moduleName: CORE}; - static PipeDefWithMeta: o.ExternalReference = {name: 'ɵɵPipeDefWithMeta', moduleName: CORE}; + static PipeDeclaration: o.ExternalReference = {name: 'ɵɵPipeDeclaration', moduleName: CORE}; static definePipe: o.ExternalReference = {name: 'ɵɵdefinePipe', moduleName: CORE}; + static declarePipe: o.ExternalReference = {name: 'ɵɵngDeclarePipe', moduleName: CORE}; static queryRefresh: o.ExternalReference = {name: 'ɵɵqueryRefresh', moduleName: CORE}; static viewQuery: o.ExternalReference = {name: 'ɵɵviewQuery', moduleName: CORE}; - static staticViewQuery: o.ExternalReference = {name: 'ɵɵstaticViewQuery', moduleName: CORE}; - static staticContentQuery: o.ExternalReference = {name: 'ɵɵstaticContentQuery', moduleName: CORE}; static loadQuery: o.ExternalReference = {name: 'ɵɵloadQuery', moduleName: CORE}; static contentQuery: o.ExternalReference = {name: 'ɵɵcontentQuery', moduleName: CORE}; @@ -311,11 +310,6 @@ export class Identifiers { static listener: o.ExternalReference = {name: 'ɵɵlistener', moduleName: CORE}; - static getFactoryOf: o.ExternalReference = { - name: 'ɵɵgetFactoryOf', - moduleName: CORE, - }; - static getInheritedFactory: o.ExternalReference = { name: 'ɵɵgetInheritedFactory', moduleName: CORE, diff --git a/packages/compiler/src/render3/r3_module_compiler.ts b/packages/compiler/src/render3/r3_module_compiler.ts index d89379a91a..4196771242 100644 --- a/packages/compiler/src/render3/r3_module_compiler.ts +++ b/packages/compiler/src/render3/r3_module_compiler.ts @@ -6,21 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileShallowModuleMetadata, identifierName} from '../compile_metadata'; -import {InjectableCompiler} from '../injectable_compiler'; -import {mapLiteral} from '../output/map_util'; +import {R3DeclareNgModuleFacade} from '../compiler_facade_interface'; import * as o from '../output/output_ast'; -import {OutputContext} from '../util'; -import {compileFactoryFunction, R3DependencyMetadata, R3FactoryTarget} from './r3_factory'; import {Identifiers as R3} from './r3_identifiers'; -import {convertMetaToOutput, jitOnlyGuardedExpression, mapToMapExpression, R3Reference} from './util'; - -export interface R3NgModuleDef { - expression: o.Expression; - type: o.Type; - additionalStatements: o.Statement[]; -} +import {jitOnlyGuardedExpression, R3CompiledExpression, R3Reference, refsToArray} from './util'; +import {DefinitionMap} from './view/util'; /** * Metadata required by the module compiler to generate a module def (`ɵmod`) for a type. @@ -90,13 +81,49 @@ export interface R3NgModuleMetadata { id: o.Expression|null; } +/** + * The shape of the object literal that is passed to the `ɵɵdefineNgModule()` call. + */ +interface R3NgModuleDefMap { + /** + * An expression representing the module type being compiled. + */ + type: o.Expression; + /** + * An expression evaluating to an array of expressions representing the bootstrap components + * specified by the module. + */ + bootstrap?: o.Expression; + /** + * An expression evaluating to an array of expressions representing the directives and pipes + * declared by the module. + */ + declarations?: o.Expression; + /** + * An expression evaluating to an array of expressions representing the imports of the module. + */ + imports?: o.Expression; + /** + * An expression evaluating to an array of expressions representing the exports of the module. + */ + exports?: o.Expression; + /** + * A literal array expression containing the schemas that declare elements to be allowed in the + * NgModule. + */ + schemas?: o.LiteralArrayExpr; + /** + * An expression evaluating to the unique ID of an NgModule. + * */ + id?: o.Expression; +} + /** * Construct an `R3NgModuleDef` for the given `R3NgModuleMetadata`. */ -export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef { +export function compileNgModule(meta: R3NgModuleMetadata): R3CompiledExpression { const { internalType, - type: moduleType, bootstrap, declarations, imports, @@ -107,35 +134,27 @@ export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef { id } = meta; - const additionalStatements: o.Statement[] = []; - const definitionMap = {type: internalType} as { - type: o.Expression, - bootstrap: o.Expression, - declarations: o.Expression, - imports: o.Expression, - exports: o.Expression, - schemas: o.LiteralArrayExpr, - id: o.Expression - }; + const statements: o.Statement[] = []; + const definitionMap = new DefinitionMap(); + definitionMap.set('type', internalType); - // Only generate the keys in the metadata if the arrays have values. - if (bootstrap.length) { - definitionMap.bootstrap = refsToArray(bootstrap, containsForwardDecls); + if (bootstrap.length > 0) { + definitionMap.set('bootstrap', refsToArray(bootstrap, containsForwardDecls)); } - // If requested to emit scope information inline, pass the declarations, imports and exports to - // the `ɵɵdefineNgModule` call. The JIT compilation uses this. + // If requested to emit scope information inline, pass the `declarations`, `imports` and `exports` + // to the `ɵɵdefineNgModule()` call. The JIT compilation uses this. if (emitInline) { - if (declarations.length) { - definitionMap.declarations = refsToArray(declarations, containsForwardDecls); + if (declarations.length > 0) { + definitionMap.set('declarations', refsToArray(declarations, containsForwardDecls)); } - if (imports.length) { - definitionMap.imports = refsToArray(imports, containsForwardDecls); + if (imports.length > 0) { + definitionMap.set('imports', refsToArray(imports, containsForwardDecls)); } - if (exports.length) { - definitionMap.exports = refsToArray(exports, containsForwardDecls); + if (exports.length > 0) { + definitionMap.set('exports', refsToArray(exports, containsForwardDecls)); } } @@ -144,26 +163,59 @@ export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef { else { const setNgModuleScopeCall = generateSetNgModuleScopeCall(meta); if (setNgModuleScopeCall !== null) { - additionalStatements.push(setNgModuleScopeCall); + statements.push(setNgModuleScopeCall); } } - if (schemas && schemas.length) { - definitionMap.schemas = o.literalArr(schemas.map(ref => ref.value)); + if (schemas !== null && schemas.length > 0) { + definitionMap.set('schemas', o.literalArr(schemas.map(ref => ref.value))); } - if (id) { - definitionMap.id = id; + if (id !== null) { + definitionMap.set('id', id); } - const expression = o.importExpr(R3.defineNgModule).callFn([mapToMapExpression(definitionMap)]); - const type = new o.ExpressionType(o.importExpr(R3.NgModuleDefWithMeta, [ + const expression = + o.importExpr(R3.defineNgModule).callFn([definitionMap.toLiteralMap()], undefined, true); + const type = createNgModuleType(meta); + + return {expression, type, statements}; +} + +/** + * This function is used in JIT mode to generate the call to `ɵɵdefineNgModule()` from a call to + * `ɵɵngDeclareNgModule()`. + */ +export function compileNgModuleDeclarationExpression(meta: R3DeclareNgModuleFacade): o.Expression { + const definitionMap = new DefinitionMap(); + definitionMap.set('type', new o.WrappedNodeExpr(meta.type)); + if (meta.bootstrap !== undefined) { + definitionMap.set('bootstrap', new o.WrappedNodeExpr(meta.bootstrap)); + } + if (meta.declarations !== undefined) { + definitionMap.set('declarations', new o.WrappedNodeExpr(meta.declarations)); + } + if (meta.imports !== undefined) { + definitionMap.set('imports', new o.WrappedNodeExpr(meta.imports)); + } + if (meta.exports !== undefined) { + definitionMap.set('exports', new o.WrappedNodeExpr(meta.exports)); + } + if (meta.schemas !== undefined) { + definitionMap.set('schemas', new o.WrappedNodeExpr(meta.schemas)); + } + if (meta.id !== undefined) { + definitionMap.set('id', new o.WrappedNodeExpr(meta.id)); + } + return o.importExpr(R3.defineNgModule).callFn([definitionMap.toLiteralMap()]); +} + +export function createNgModuleType( + {type: moduleType, declarations, imports, exports}: R3NgModuleMetadata): o.ExpressionType { + return new o.ExpressionType(o.importExpr(R3.NgModuleDeclaration, [ new o.ExpressionType(moduleType.type), tupleTypeOf(declarations), tupleTypeOf(imports), tupleTypeOf(exports) ])); - - - return {expression, type, additionalStatements}; } /** @@ -175,32 +227,29 @@ export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef { function generateSetNgModuleScopeCall(meta: R3NgModuleMetadata): o.Statement|null { const {adjacentType: moduleType, declarations, imports, exports, containsForwardDecls} = meta; - const scopeMap = {} as { - declarations: o.Expression, - imports: o.Expression, - exports: o.Expression, - }; + const scopeMap = new DefinitionMap< + {declarations: o.Expression, imports: o.Expression, exports: o.Expression}>(); - if (declarations.length) { - scopeMap.declarations = refsToArray(declarations, containsForwardDecls); + if (declarations.length > 0) { + scopeMap.set('declarations', refsToArray(declarations, containsForwardDecls)); } - if (imports.length) { - scopeMap.imports = refsToArray(imports, containsForwardDecls); + if (imports.length > 0) { + scopeMap.set('imports', refsToArray(imports, containsForwardDecls)); } - if (exports.length) { - scopeMap.exports = refsToArray(exports, containsForwardDecls); + if (exports.length > 0) { + scopeMap.set('exports', refsToArray(exports, containsForwardDecls)); } - if (Object.keys(scopeMap).length === 0) { + if (Object.keys(scopeMap.values).length === 0) { return null; } // setNgModuleScope(...) const fnCall = new o.InvokeFunctionExpr( /* fn */ o.importExpr(R3.setNgModuleScope), - /* args */[moduleType, mapToMapExpression(scopeMap)]); + /* args */[moduleType, scopeMap.toLiteralMap()]); // (ngJitMode guard) && setNgModuleScope(...) const guardedCall = jitOnlyGuardedExpression(fnCall); @@ -218,92 +267,7 @@ function generateSetNgModuleScopeCall(meta: R3NgModuleMetadata): o.Statement|nul return iifeCall.toStmt(); } -export interface R3InjectorDef { - expression: o.Expression; - type: o.Type; - statements: o.Statement[]; -} - -export interface R3InjectorMetadata { - name: string; - type: R3Reference; - internalType: o.Expression; - deps: R3DependencyMetadata[]|null; - providers: o.Expression|null; - imports: o.Expression[]; -} - -export function compileInjector(meta: R3InjectorMetadata): R3InjectorDef { - const result = compileFactoryFunction({ - name: meta.name, - type: meta.type, - internalType: meta.internalType, - typeArgumentCount: 0, - deps: meta.deps, - injectFn: R3.inject, - target: R3FactoryTarget.NgModule, - }); - const definitionMap = { - factory: result.factory, - } as {factory: o.Expression, providers: o.Expression, imports: o.Expression}; - - if (meta.providers !== null) { - definitionMap.providers = meta.providers; - } - - if (meta.imports.length > 0) { - definitionMap.imports = o.literalArr(meta.imports); - } - - const expression = o.importExpr(R3.defineInjector).callFn([mapToMapExpression(definitionMap)]); - const type = - new o.ExpressionType(o.importExpr(R3.InjectorDef, [new o.ExpressionType(meta.type.type)])); - return {expression, type, statements: result.statements}; -} - -// TODO(alxhub): integrate this with `compileNgModule`. Currently the two are separate operations. -export function compileNgModuleFromRender2( - ctx: OutputContext, ngModule: CompileShallowModuleMetadata, - injectableCompiler: InjectableCompiler): void { - const className = identifierName(ngModule.type)!; - - const rawImports = ngModule.rawImports ? [ngModule.rawImports] : []; - const rawExports = ngModule.rawExports ? [ngModule.rawExports] : []; - - const injectorDefArg = mapLiteral({ - 'factory': - injectableCompiler.factoryFor({type: ngModule.type, symbol: ngModule.type.reference}, ctx), - 'providers': convertMetaToOutput(ngModule.rawProviders, ctx), - 'imports': convertMetaToOutput([...rawImports, ...rawExports], ctx), - }); - - const injectorDef = o.importExpr(R3.defineInjector).callFn([injectorDefArg]); - - ctx.statements.push(new o.ClassStmt( - /* name */ className, - /* parent */ null, - /* fields */[new o.ClassField( - /* name */ 'ɵinj', - /* type */ o.INFERRED_TYPE, - /* modifiers */[o.StmtModifier.Static], - /* initializer */ injectorDef, - )], - /* getters */[], - /* constructorMethod */ new o.ClassMethod(null, [], []), - /* methods */[])); -} - -function accessExportScope(module: o.Expression): o.Expression { - const selectorScope = new o.ReadPropExpr(module, 'ɵmod'); - return new o.ReadPropExpr(selectorScope, 'exported'); -} - function tupleTypeOf(exp: R3Reference[]): o.Type { const types = exp.map(ref => o.typeofExpr(ref.type)); return exp.length > 0 ? o.expressionType(o.literalArr(types)) : o.NONE_TYPE; } - -function refsToArray(refs: R3Reference[], shouldForwardDeclare: boolean): o.Expression { - const values = o.literalArr(refs.map(ref => ref.value)); - return shouldForwardDeclare ? o.fn([], [new o.ReturnStatement(values)]) : values; -} diff --git a/packages/compiler/src/render3/r3_module_factory_compiler.ts b/packages/compiler/src/render3/r3_module_factory_compiler.ts deleted file mode 100644 index a1a68417a6..0000000000 --- a/packages/compiler/src/render3/r3_module_factory_compiler.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {CompileNgModuleMetadata, CompileTypeMetadata, identifierName} from '../compile_metadata'; -import {CompileMetadataResolver} from '../metadata_resolver'; -import * as o from '../output/output_ast'; -import {OutputContext} from '../util'; - -import {Identifiers as R3} from './r3_identifiers'; - -/** - * Write a Renderer2 compatibility module factory to the output context. - */ -export function compileModuleFactory( - outputCtx: OutputContext, module: CompileNgModuleMetadata, - backPatchReferenceOf: (module: CompileTypeMetadata) => o.Expression, - resolver: CompileMetadataResolver) { - const ngModuleFactoryVar = `${identifierName(module.type)}NgFactory`; - - const parentInjector = 'parentInjector'; - const createFunction = o.fn( - [new o.FnParam(parentInjector, o.DYNAMIC_TYPE)], - [new o.IfStmt( - o.THIS_EXPR.prop(R3.PATCH_DEPS).notIdentical(o.literal(true, o.INFERRED_TYPE)), - [ - o.THIS_EXPR.prop(R3.PATCH_DEPS).set(o.literal(true, o.INFERRED_TYPE)).toStmt(), - backPatchReferenceOf(module.type).callFn([]).toStmt() - ])], - o.INFERRED_TYPE, null, `${ngModuleFactoryVar}_Create`); - - const moduleFactoryLiteral = o.literalMap([ - {key: 'moduleType', value: outputCtx.importExpr(module.type.reference), quoted: false}, - {key: 'create', value: createFunction, quoted: false} - ]); - - outputCtx.statements.push( - o.variable(ngModuleFactoryVar).set(moduleFactoryLiteral).toDeclStmt(o.DYNAMIC_TYPE, [ - o.StmtModifier.Exported, o.StmtModifier.Final - ])); -} diff --git a/packages/compiler/src/render3/r3_pipe_compiler.ts b/packages/compiler/src/render3/r3_pipe_compiler.ts index 1f22d629d3..ec11bfb8dd 100644 --- a/packages/compiler/src/render3/r3_pipe_compiler.ts +++ b/packages/compiler/src/render3/r3_pipe_compiler.ts @@ -5,16 +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 {CompilePipeMetadata, identifierName} from '../compile_metadata'; -import {CompileReflector} from '../compile_reflector'; -import {DefinitionKind} from '../constant_pool'; import * as o from '../output/output_ast'; -import {error, OutputContext} from '../util'; -import {compileFactoryFunction, dependenciesFromGlobalMetadata, R3DependencyMetadata, R3FactoryTarget} from './r3_factory'; +import {R3DependencyMetadata} from './r3_factory'; import {Identifiers as R3} from './r3_identifiers'; -import {R3Reference, typeWithParameters, wrapReference} from './util'; +import {R3CompiledExpression, R3Reference, typeWithParameters} from './util'; export interface R3PipeMetadata { /** @@ -57,7 +52,7 @@ export interface R3PipeMetadata { pure: boolean; } -export function compilePipeFromMetadata(metadata: R3PipeMetadata) { +export function compilePipeFromMetadata(metadata: R3PipeMetadata): R3CompiledExpression { const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = []; // e.g. `name: 'myPipe'` @@ -69,63 +64,16 @@ export function compilePipeFromMetadata(metadata: R3PipeMetadata) { // e.g. `pure: true` definitionMapValues.push({key: 'pure', value: o.literal(metadata.pure), quoted: false}); - const expression = o.importExpr(R3.definePipe).callFn([o.literalMap(definitionMapValues)]); - const type = new o.ExpressionType(o.importExpr(R3.PipeDefWithMeta, [ + const expression = + o.importExpr(R3.definePipe).callFn([o.literalMap(definitionMapValues)], undefined, true); + const type = createPipeType(metadata); + + return {expression, type, statements: []}; +} + +export function createPipeType(metadata: R3PipeMetadata): o.Type { + return new o.ExpressionType(o.importExpr(R3.PipeDeclaration, [ typeWithParameters(metadata.type.type, metadata.typeArgumentCount), new o.ExpressionType(new o.LiteralExpr(metadata.pipeName)), ])); - - return {expression, type}; -} - -/** - * Write a pipe definition to the output context. - */ -export function compilePipeFromRender2( - outputCtx: OutputContext, pipe: CompilePipeMetadata, reflector: CompileReflector) { - const name = identifierName(pipe.type); - - if (!name) { - return error(`Cannot resolve the name of ${pipe.type}`); - } - - const type = outputCtx.importExpr(pipe.type.reference); - const metadata: R3PipeMetadata = { - name, - type: wrapReference(type), - internalType: type, - pipeName: pipe.name, - typeArgumentCount: 0, - deps: dependenciesFromGlobalMetadata(pipe.type, outputCtx, reflector), - pure: pipe.pure, - }; - const res = compilePipeFromMetadata(metadata); - const factoryRes = compileFactoryFunction( - {...metadata, injectFn: R3.directiveInject, target: R3FactoryTarget.Pipe}); - const definitionField = outputCtx.constantPool.propertyNameOf(DefinitionKind.Pipe); - const ngFactoryDefStatement = new o.ClassStmt( - /* name */ name, - /* parent */ null, - /* fields */ - [new o.ClassField( - /* name */ 'ɵfac', - /* type */ o.INFERRED_TYPE, - /* modifiers */[o.StmtModifier.Static], - /* initializer */ factoryRes.factory)], - /* getters */[], - /* constructorMethod */ new o.ClassMethod(null, [], []), - /* methods */[]); - const pipeDefStatement = new o.ClassStmt( - /* name */ name, - /* parent */ null, - /* fields */[new o.ClassField( - /* name */ definitionField, - /* type */ o.INFERRED_TYPE, - /* modifiers */[o.StmtModifier.Static], - /* initializer */ res.expression)], - /* getters */[], - /* constructorMethod */ new o.ClassMethod(null, [], []), - /* methods */[]); - - outputCtx.statements.push(ngFactoryDefStatement, pipeDefStatement); } diff --git a/packages/compiler/src/render3/r3_template_transform.ts b/packages/compiler/src/render3/r3_template_transform.ts index 6a831f4a48..a09a4e0aae 100644 --- a/packages/compiler/src/render3/r3_template_transform.ts +++ b/packages/compiler/src/render3/r3_template_transform.ts @@ -51,23 +51,34 @@ export interface Render3ParseResult { styles: string[]; styleUrls: string[]; ngContentSelectors: string[]; + // Will be defined if `Render3ParseOptions['collectCommentNodes']` is true + commentNodes?: t.Comment[]; +} + +interface Render3ParseOptions { + collectCommentNodes: boolean; } export function htmlAstToRender3Ast( - htmlNodes: html.Node[], bindingParser: BindingParser): Render3ParseResult { - const transformer = new HtmlAstToIvyAst(bindingParser); + htmlNodes: html.Node[], bindingParser: BindingParser, + options: Render3ParseOptions): Render3ParseResult { + const transformer = new HtmlAstToIvyAst(bindingParser, options); const ivyNodes = html.visitAll(transformer, htmlNodes); // Errors might originate in either the binding parser or the html to ivy transformer const allErrors = bindingParser.errors.concat(transformer.errors); - return { + const result: Render3ParseResult = { nodes: ivyNodes, errors: allErrors, styleUrls: transformer.styleUrls, styles: transformer.styles, - ngContentSelectors: transformer.ngContentSelectors, + ngContentSelectors: transformer.ngContentSelectors }; + if (options.collectCommentNodes) { + result.commentNodes = transformer.commentNodes; + } + return result; } class HtmlAstToIvyAst implements html.Visitor { @@ -75,9 +86,11 @@ class HtmlAstToIvyAst implements html.Visitor { styles: string[] = []; styleUrls: string[] = []; ngContentSelectors: string[] = []; + // This array will be populated if `Render3ParseOptions['collectCommentNodes']` is true + commentNodes: t.Comment[] = []; private inI18nBlock: boolean = false; - constructor(private bindingParser: BindingParser) {} + constructor(private bindingParser: BindingParser, private options: Render3ParseOptions) {} // HTML visitor visitElement(element: html.Element): t.Node|null { @@ -287,6 +300,9 @@ class HtmlAstToIvyAst implements html.Visitor { } visitComment(comment: html.Comment): null { + if (this.options.collectCommentNodes) { + this.commentNodes.push(new t.Comment(comment.value || '', comment.sourceSpan)); + } return null; } @@ -458,6 +474,8 @@ class HtmlAstToIvyAst implements html.Visitor { this.reportError(`"-" is not allowed in reference names`, sourceSpan); } else if (identifier.length === 0) { this.reportError(`Reference does not have a name`, sourceSpan); + } else if (references.some(reference => reference.name === identifier)) { + this.reportError(`Reference "#${identifier}" is defined more than once`, sourceSpan); } references.push(new t.Reference(identifier, value, sourceSpan, keySpan, valueSpan)); diff --git a/packages/compiler/src/render3/r3_types.ts b/packages/compiler/src/render3/r3_types.ts deleted file mode 100644 index ff1aee0f59..0000000000 --- a/packages/compiler/src/render3/r3_types.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -/** - * Comment to insert above back-patch - */ -export const BUILD_OPTIMIZER_COLOCATE = '@__BUILD_OPTIMIZER_COLOCATE__'; - -/** - * Comment to mark removable expressions - */ -export const BUILD_OPTIMIZER_REMOVE = '@__BUILD_OPTIMIZER_REMOVE__'; diff --git a/packages/compiler/src/render3/util.ts b/packages/compiler/src/render3/util.ts index 5c1d7bb40a..f558bd1396 100644 --- a/packages/compiler/src/render3/util.ts +++ b/packages/compiler/src/render3/util.ts @@ -6,43 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {StaticSymbol} from '../aot/static_symbol'; +import {escapeIdentifier} from '../output/abstract_emitter'; import * as o from '../output/output_ast'; -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 { - 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]!, - quoted: false, - })); - return o.literalMap(result); -} - -/** - * Convert metadata into an `Expression` in the given `OutputContext`. - * - * This operation will handle arrays, references to symbols, or literal `null` or `undefined`. - */ -export function convertMetaToOutput(meta: any, ctx: OutputContext): o.Expression { - if (Array.isArray(meta)) { - return o.literalArr(meta.map(entry => convertMetaToOutput(entry, ctx))); - } - if (meta instanceof StaticSymbol) { - return ctx.importExpr(meta); - } - if (meta == null) { - return o.literal(meta); - } - - throw new Error(`Internal error: Unsupported or unknown metadata: ${meta}`); -} export function typeWithParameters(type: o.Expression, numParams: number): o.ExpressionType { if (numParams === 0) { @@ -60,6 +25,15 @@ export interface R3Reference { type: o.Expression; } +/** + * Result of compilation of a render3 code unit, e.g. component, directive, pipe, etc. + */ +export interface R3CompiledExpression { + expression: o.Expression; + type: o.Type; + statements: o.Statement[]; +} + const ANIMATE_SYMBOL_PREFIX = '@'; export function prepareSyntheticPropertyName(name: string) { return `${ANIMATE_SYMBOL_PREFIX}${name}`; @@ -69,19 +43,9 @@ export function prepareSyntheticListenerName(name: string, phase: string) { return `${ANIMATE_SYMBOL_PREFIX}${name}.${phase}`; } -export function isSyntheticPropertyOrListener(name: string) { - return name.charAt(0) == ANIMATE_SYMBOL_PREFIX; -} - -export function getSyntheticPropertyName(name: string) { - // this will strip out listener phase values... - // @foo.start => @foo - const i = name.indexOf('.'); - name = i > 0 ? name.substring(0, i) : name; - if (name.charAt(0) !== ANIMATE_SYMBOL_PREFIX) { - name = ANIMATE_SYMBOL_PREFIX + name; - } - return name; +export function getSafePropertyAccessString(accessor: string, name: string): string { + const escapedName = escapeIdentifier(name, false, false); + return escapedName !== name ? `${accessor}[${escapedName}]` : `${accessor}.${name}`; } export function prepareSyntheticListenerFunctionName(name: string, phase: string) { @@ -89,16 +53,29 @@ export function prepareSyntheticListenerFunctionName(name: string, phase: string } export function jitOnlyGuardedExpression(expr: o.Expression): o.Expression { - const ngJitMode = new o.ExternalExpr({name: 'ngJitMode', moduleName: null}); - const jitFlagNotDefined = new o.BinaryOperatorExpr( - o.BinaryOperator.Identical, new o.TypeofExpr(ngJitMode), o.literal('undefined')); - const jitFlagUndefinedOrTrue = new o.BinaryOperatorExpr( - o.BinaryOperator.Or, jitFlagNotDefined, ngJitMode, /* type */ undefined, + return guardedExpression('ngJitMode', expr); +} + +export function devOnlyGuardedExpression(expr: o.Expression): o.Expression { + return guardedExpression('ngDevMode', expr); +} + +export function guardedExpression(guard: string, expr: o.Expression): o.Expression { + const guardExpr = new o.ExternalExpr({name: guard, moduleName: null}); + const guardNotDefined = new o.BinaryOperatorExpr( + o.BinaryOperator.Identical, new o.TypeofExpr(guardExpr), o.literal('undefined')); + const guardUndefinedOrTrue = new o.BinaryOperatorExpr( + o.BinaryOperator.Or, guardNotDefined, guardExpr, /* type */ undefined, /* sourceSpan */ undefined, true); - return new o.BinaryOperatorExpr(o.BinaryOperator.And, jitFlagUndefinedOrTrue, expr); + return new o.BinaryOperatorExpr(o.BinaryOperator.And, guardUndefinedOrTrue, expr); } export function wrapReference(value: any): R3Reference { const wrapped = new o.WrappedNodeExpr(value); return {value: wrapped, type: wrapped}; } + +export function refsToArray(refs: R3Reference[], shouldForwardDeclare: boolean): o.Expression { + const values = o.literalArr(refs.map(ref => ref.value)); + return shouldForwardDeclare ? o.fn([], [new o.ReturnStatement(values)]) : values; +} diff --git a/packages/compiler/src/render3/view/api.ts b/packages/compiler/src/render3/view/api.ts index 51f1b310ef..2e1d67a545 100644 --- a/packages/compiler/src/render3/view/api.ts +++ b/packages/compiler/src/render3/view/api.ts @@ -119,6 +119,50 @@ export interface R3DirectiveMetadata { providers: o.Expression|null; } +/** + * Specifies how a list of declaration type references should be emitted into the generated code. + */ +export const enum DeclarationListEmitMode { + /** + * The list of declarations is emitted into the generated code as is. + * + * ``` + * directives: [MyDir], + * ``` + */ + Direct, + + /** + * The list of declarations is emitted into the generated code wrapped inside a closure, which + * is needed when at least one declaration is a forward reference. + * + * ``` + * directives: function () { return [MyDir, ForwardDir]; }, + * ``` + */ + Closure, + + /** + * Similar to `Closure`, with the addition that the list of declarations can contain individual + * items that are themselves forward references. This is relevant for JIT compilations, as + * unwrapping the forwardRef cannot be done statically so must be deferred. This mode emits + * the declaration list using a mapping transform through `resolveForwardRef` to ensure that + * any forward references within the list are resolved when the outer closure is invoked. + * + * Consider the case where the runtime has captured two declarations in two distinct values: + * ``` + * const dirA = MyDir; + * const dirB = forwardRef(function() { return ForwardRef; }); + * ``` + * + * This mode would emit the declarations captured in `dirA` and `dirB` as follows: + * ``` + * directives: function () { return [dirA, dirB].map(ng.resolveForwardRef); }, + * ``` + */ + ClosureResolved, +} + /** * Information needed to compile a component for the render3 runtime. */ @@ -152,11 +196,9 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata { directives: R3UsedDirectiveMetadata[]; /** - * Whether to wrap the 'directives' and/or `pipes` array, if one is generated, in a closure. - * - * This is done when the directives or pipes contain forward references. + * Specifies how the 'directives' and/or `pipes` array, if generated, need to be emitted. */ - wrapDirectivesAndPipesInClosure: boolean; + declarationListEmitMode: DeclarationListEmitMode; /** * A collection of styling data that will be applied and scoped to the component. @@ -235,6 +277,11 @@ export interface R3UsedDirectiveMetadata { * Name under which the directive is exported, if any (exportAs in Angular). Null otherwise. */ exportAs: string[]|null; + + /** + * If true then this directive is actually a component; otherwise it is not. + */ + isComponent?: boolean; } /** @@ -262,6 +309,13 @@ export interface R3QueryMetadata { */ descendants: boolean; + /** + * If the `QueryList` should fire change event only if actual change to query was computed (vs old + * behavior where the change was fired whenever the query was recomputed, even if the recomputed + * query resulted in the same list.) + */ + emitDistinctChangesOnly: boolean; + /** * An expression representing a type to read from each matched node, or null if the default value * for a given node is to be returned. @@ -284,22 +338,6 @@ export interface R3QueryMetadata { static: boolean; } -/** - * Output of render3 directive compilation. - */ -export interface R3DirectiveDef { - expression: o.Expression; - type: o.Type; -} - -/** - * Output of render3 component compilation. - */ -export interface R3ComponentDef { - expression: o.Expression; - type: o.Type; -} - /** * Mappings indicating how the class interacts with its * host element (host bindings, listeners, etc). diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts index 7bb1788593..76de1f9af4 100644 --- a/packages/compiler/src/render3/view/compiler.ts +++ b/packages/compiler/src/render3/view/compiler.ts @@ -6,33 +6,27 @@ * found in the LICENSE file at https://angular.io/license */ -import {StaticSymbol} from '../../aot/static_symbol'; -import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileQueryMetadata, CompileTokenMetadata, identifierName, sanitizeIdentifier} from '../../compile_metadata'; -import {CompileReflector} from '../../compile_reflector'; +import {CompileDirectiveSummary, sanitizeIdentifier} from '../../compile_metadata'; import {BindingForm, convertPropertyBinding} from '../../compiler_util/expression_converter'; -import {ConstantPool, DefinitionKind} from '../../constant_pool'; +import {ConstantPool} from '../../constant_pool'; import * as core from '../../core'; import {AST, ParsedEvent, ParsedEventType, ParsedProperty} from '../../expression_parser/ast'; -import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config'; import * as o from '../../output/output_ast'; import {ParseError, ParseSourceSpan} from '../../parse_util'; 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 {error, OutputContext} from '../../util'; +import {error} from '../../util'; import {BoundEvent} from '../r3_ast'; -import {compileFactoryFunction, R3FactoryTarget} from '../r3_factory'; import {Identifiers as R3} from '../r3_identifiers'; -import {Render3ParseResult} from '../r3_template_transform'; -import {prepareSyntheticListenerFunctionName, prepareSyntheticPropertyName, typeWithParameters} from '../util'; +import {prepareSyntheticListenerFunctionName, prepareSyntheticPropertyName, R3CompiledExpression, typeWithParameters} from '../util'; -import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3HostMetadata, R3QueryMetadata} from './api'; +import {DeclarationListEmitMode, R3ComponentMetadata, R3DirectiveMetadata, R3HostMetadata, R3QueryMetadata} from './api'; import {MIN_STYLING_BINDING_SLOTS_REQUIRED, StylingBuilder, StylingInstructionCall} from './styling_builder'; 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[] = []; // This regex matches any binding names that contain the "attr." prefix, e.g. "attr.required" // If there is a match, the first matching group will contain the attribute name to bind. @@ -119,13 +113,14 @@ function addFeatures(definitionMap: DefinitionMap, meta: R3DirectiveMetadata|R3C */ export function compileDirectiveFromMetadata( meta: R3DirectiveMetadata, constantPool: ConstantPool, - bindingParser: BindingParser): R3DirectiveDef { + bindingParser: BindingParser): R3CompiledExpression { const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser); addFeatures(definitionMap, meta); - const expression = o.importExpr(R3.defineDirective).callFn([definitionMap.toLiteralMap()]); + const expression = + o.importExpr(R3.defineDirective).callFn([definitionMap.toLiteralMap()], undefined, true); const type = createDirectiveType(meta); - return {expression, type}; + return {expression, type, statements: []}; } /** @@ -133,7 +128,7 @@ export function compileDirectiveFromMetadata( */ export function compileComponentFromMetadata( meta: R3ComponentMetadata, constantPool: ConstantPool, - bindingParser: BindingParser): R3ComponentDef { + bindingParser: BindingParser): R3CompiledExpression { const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser); addFeatures(definitionMap, meta); @@ -213,19 +208,15 @@ export function compileComponentFromMetadata( // e.g. `directives: [MyDirective]` if (directivesUsed.size) { - let directivesExpr: o.Expression = o.literalArr(Array.from(directivesUsed)); - if (meta.wrapDirectivesAndPipesInClosure) { - directivesExpr = o.fn([], [new o.ReturnStatement(directivesExpr)]); - } + const directivesList = o.literalArr(Array.from(directivesUsed)); + const directivesExpr = compileDeclarationList(directivesList, meta.declarationListEmitMode); definitionMap.set('directives', directivesExpr); } // e.g. `pipes: [MyPipe]` if (pipesUsed.size) { - let pipesExpr: o.Expression = o.literalArr(Array.from(pipesUsed)); - if (meta.wrapDirectivesAndPipesInClosure) { - pipesExpr = o.fn([], [new o.ReturnStatement(pipesExpr)]); - } + const pipesList = o.literalArr(Array.from(pipesUsed)); + const pipesExpr = compileDeclarationList(pipesList, meta.declarationListEmitMode); definitionMap.set('pipes', pipesExpr); } @@ -261,10 +252,11 @@ export function compileComponentFromMetadata( definitionMap.set('changeDetection', o.literal(changeDetection)); } - const expression = o.importExpr(R3.defineComponent).callFn([definitionMap.toLiteralMap()]); + const expression = + o.importExpr(R3.defineComponent).callFn([definitionMap.toLiteralMap()], undefined, true); const type = createComponentType(meta); - return {expression, type}; + return {expression, type, statements: []}; } /** @@ -274,158 +266,78 @@ export function compileComponentFromMetadata( export function createComponentType(meta: R3ComponentMetadata): o.Type { const typeParams = createDirectiveTypeParams(meta); typeParams.push(stringArrayAsType(meta.template.ngContentSelectors)); - return o.expressionType(o.importExpr(R3.ComponentDefWithMeta, typeParams)); + return o.expressionType(o.importExpr(R3.ComponentDeclaration, typeParams)); } /** - * A wrapper around `compileDirective` which depends on render2 global analysis data as its input - * instead of the `R3DirectiveMetadata`. - * - * `R3DirectiveMetadata` is computed from `CompileDirectiveMetadata` and other statically reflected - * information. + * Compiles the array literal of declarations into an expression according to the provided emit + * mode. */ -export function compileDirectiveFromRender2( - outputCtx: OutputContext, directive: CompileDirectiveMetadata, reflector: CompileReflector, - bindingParser: BindingParser) { - const name = identifierName(directive.type)!; - name || error(`Cannot resolver the name of ${directive.type}`); - - const definitionField = outputCtx.constantPool.propertyNameOf(DefinitionKind.Directive); - - const meta = directiveMetadataFromGlobalMetadata(directive, outputCtx, reflector); - const res = compileDirectiveFromMetadata(meta, outputCtx.constantPool, bindingParser); - const factoryRes = compileFactoryFunction( - {...meta, injectFn: R3.directiveInject, target: R3FactoryTarget.Directive}); - const ngFactoryDefStatement = new o.ClassStmt( - name, null, - [new o.ClassField('ɵfac', o.INFERRED_TYPE, [o.StmtModifier.Static], factoryRes.factory)], [], - new o.ClassMethod(null, [], []), []); - const directiveDefStatement = new o.ClassStmt( - name, null, - [new o.ClassField(definitionField, o.INFERRED_TYPE, [o.StmtModifier.Static], res.expression)], - [], new o.ClassMethod(null, [], []), []); - - // Create the partial class to be merged with the actual class. - outputCtx.statements.push(ngFactoryDefStatement, directiveDefStatement); -} - -/** - * A wrapper around `compileComponent` which depends on render2 global analysis data as its input - * instead of the `R3DirectiveMetadata`. - * - * `R3ComponentMetadata` is computed from `CompileDirectiveMetadata` and other statically reflected - * information. - */ -export function compileComponentFromRender2( - outputCtx: OutputContext, component: CompileDirectiveMetadata, render3Ast: Render3ParseResult, - reflector: CompileReflector, bindingParser: BindingParser, directiveTypeBySel: Map, - pipeTypeByName: Map) { - const name = identifierName(component.type)!; - name || error(`Cannot resolver the name of ${component.type}`); - - const definitionField = outputCtx.constantPool.propertyNameOf(DefinitionKind.Component); - - const summary = component.toSummary(); - - // Compute the R3ComponentMetadata from the CompileDirectiveMetadata - const meta: R3ComponentMetadata = { - ...directiveMetadataFromGlobalMetadata(component, outputCtx, reflector), - selector: component.selector, - template: {nodes: render3Ast.nodes, ngContentSelectors: render3Ast.ngContentSelectors}, - directives: [], - pipes: typeMapToExpressionMap(pipeTypeByName, outputCtx), - viewQueries: queriesFromGlobalMetadata(component.viewQueries, outputCtx), - wrapDirectivesAndPipesInClosure: false, - styles: (summary.template && summary.template.styles) || EMPTY_ARRAY, - encapsulation: - (summary.template && summary.template.encapsulation) || core.ViewEncapsulation.Emulated, - interpolation: DEFAULT_INTERPOLATION_CONFIG, - animations: null, - viewProviders: - component.viewProviders.length > 0 ? new o.WrappedNodeExpr(component.viewProviders) : null, - relativeContextFilePath: '', - i18nUseExternalIds: true, - }; - const res = compileComponentFromMetadata(meta, outputCtx.constantPool, bindingParser); - const factoryRes = compileFactoryFunction( - {...meta, injectFn: R3.directiveInject, target: R3FactoryTarget.Directive}); - const ngFactoryDefStatement = new o.ClassStmt( - name, null, - [new o.ClassField('ɵfac', o.INFERRED_TYPE, [o.StmtModifier.Static], factoryRes.factory)], [], - new o.ClassMethod(null, [], []), []); - const componentDefStatement = new o.ClassStmt( - name, null, - [new o.ClassField(definitionField, o.INFERRED_TYPE, [o.StmtModifier.Static], res.expression)], - [], new o.ClassMethod(null, [], []), []); - - // Create the partial class to be merged with the actual class. - outputCtx.statements.push(ngFactoryDefStatement, componentDefStatement); -} - -/** - * Compute `R3DirectiveMetadata` given `CompileDirectiveMetadata` and a `CompileReflector`. - */ -function directiveMetadataFromGlobalMetadata( - directive: CompileDirectiveMetadata, outputCtx: OutputContext, - reflector: CompileReflector): R3DirectiveMetadata { - // The global-analysis based Ivy mode in ngc is no longer utilized/supported. - throw new Error('unsupported'); -} - -/** - * Convert `CompileQueryMetadata` into `R3QueryMetadata`. - */ -function queriesFromGlobalMetadata( - queries: CompileQueryMetadata[], outputCtx: OutputContext): R3QueryMetadata[] { - return queries.map(query => { - let read: o.Expression|null = null; - if (query.read && query.read.identifier) { - read = outputCtx.importExpr(query.read.identifier.reference); - } - return { - propertyName: query.propertyName, - first: query.first, - predicate: selectorsFromGlobalMetadata(query.selectors, outputCtx), - descendants: query.descendants, - read, - static: !!query.static - }; - }); -} - -/** - * Convert `CompileTokenMetadata` for query selectors into either an expression for a predicate - * type, or a list of string predicates. - */ -function selectorsFromGlobalMetadata( - selectors: CompileTokenMetadata[], outputCtx: OutputContext): o.Expression|string[] { - if (selectors.length > 1 || (selectors.length == 1 && selectors[0].value)) { - const selectorStrings = selectors.map(value => value.value as string); - selectorStrings.some(value => !value) && - error('Found a type among the string selectors expected'); - return outputCtx.constantPool.getConstLiteral( - o.literalArr(selectorStrings.map(value => o.literal(value)))); +function compileDeclarationList( + list: o.LiteralArrayExpr, mode: DeclarationListEmitMode): o.Expression { + switch (mode) { + case DeclarationListEmitMode.Direct: + // directives: [MyDir], + return list; + case DeclarationListEmitMode.Closure: + // directives: function () { return [MyDir]; } + return o.fn([], [new o.ReturnStatement(list)]); + case DeclarationListEmitMode.ClosureResolved: + // directives: function () { return [MyDir].map(ng.resolveForwardRef); } + const resolvedList = list.callMethod('map', [o.importExpr(R3.resolveForwardRef)]); + return o.fn([], [new o.ReturnStatement(resolvedList)]); } - - if (selectors.length == 1) { - const first = selectors[0]; - if (first.identifier) { - return outputCtx.importExpr(first.identifier.reference); - } - } - - error('Unexpected query form'); - return o.NULL_EXPR; } function prepareQueryParams(query: R3QueryMetadata, constantPool: ConstantPool): o.Expression[] { - const parameters = [getQueryPredicate(query, constantPool), o.literal(query.descendants)]; + const parameters = [getQueryPredicate(query, constantPool), o.literal(toQueryFlags(query))]; if (query.read) { parameters.push(query.read); } return parameters; } +/** + * A set of flags to be used with Queries. + * + * NOTE: Ensure changes here are in sync with `packages/core/src/render3/interfaces/query.ts` + */ +export const enum QueryFlags { + /** + * No flags + */ + none = 0b0000, + + /** + * Whether or not the query should descend into children. + */ + descendants = 0b0001, + + /** + * The query can be computed statically and hence can be assigned eagerly. + * + * NOTE: Backwards compatibility with ViewEngine. + */ + isStatic = 0b0010, + + /** + * If the `QueryList` should fire change event only if actual change to query was computed (vs old + * behavior where the change was fired whenever the query was recomputed, even if the recomputed + * query resulted in the same list.) + */ + emitDistinctChangesOnly = 0b0100, +} + +/** + * Translates query flags into `TQueryFlags` type in packages/core/src/render3/interfaces/query.ts + * @param query + */ +function toQueryFlags(query: R3QueryMetadata): number { + return (query.descendants ? QueryFlags.descendants : QueryFlags.none) | + (query.static ? QueryFlags.isStatic : QueryFlags.none) | + (query.emitDistinctChangesOnly ? QueryFlags.emitDistinctChangesOnly : QueryFlags.none); +} + function convertAttributesToExpressions(attributes: {[name: string]: o.Expression}): o.Expression[] { const values: o.Expression[] = []; @@ -444,11 +356,9 @@ function createContentQueriesFunction( const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME); for (const query of queries) { - const queryInstruction = query.static ? R3.staticContentQuery : R3.contentQuery; - // creation, e.g. r3.contentQuery(dirIndex, somePredicate, true, null); createStatements.push( - o.importExpr(queryInstruction) + o.importExpr(R3.contentQuery) .callFn([o.variable('dirIndex'), ...prepareQueryParams(query, constantPool) as any]) .toStmt()); @@ -517,7 +427,7 @@ export function createDirectiveTypeParams(meta: R3DirectiveMetadata): o.Type[] { */ export function createDirectiveType(meta: R3DirectiveMetadata): o.Type { const typeParams = createDirectiveTypeParams(meta); - return o.expressionType(o.importExpr(R3.DirectiveDefWithMeta, typeParams)); + return o.expressionType(o.importExpr(R3.DirectiveDeclaration, typeParams)); } // Define and update any view queries @@ -528,11 +438,9 @@ function createViewQueriesFunction( const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME); viewQueries.forEach((query: R3QueryMetadata) => { - const queryInstruction = query.static ? R3.staticViewQuery : R3.viewQuery; - // creation, e.g. r3.viewQuery(somePredicate, true); const queryDefinition = - o.importExpr(queryInstruction).callFn(prepareQueryParams(query, constantPool)); + o.importExpr(R3.viewQuery).callFn(prepareQueryParams(query, constantPool)); createStatements.push(queryDefinition.toStmt()); // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp)); @@ -812,13 +720,6 @@ function metadataAsSummary(meta: R3HostMetadata): CompileDirectiveSummary { } -function typeMapToExpressionMap( - map: Map, outputCtx: OutputContext): Map { - // Convert each map entry into another entry where the value is an expression importing the type. - const entries = Array.from(map).map( - ([key, type]): [string, o.Expression] => [key, outputCtx.importExpr(type)]); - return new Map(entries); -} const HOST_REG_EXP = /^(?:\[([^\]]+)\])|(?:\(([^\)]+)\))$/; // Represents the groups in the above regex. diff --git a/packages/compiler/src/render3/view/styling_builder.ts b/packages/compiler/src/render3/view/styling_builder.ts index 5430d04b07..eea4fe75ad 100644 --- a/packages/compiler/src/render3/view/styling_builder.ts +++ b/packages/compiler/src/render3/view/styling_builder.ts @@ -219,7 +219,11 @@ export class StylingBuilder { if (isEmptyExpression(value)) { return null; } - name = normalizePropName(name); + // CSS custom properties are case-sensitive so we shouldn't normalize them. + // See: https://www.w3.org/TR/css-variables-1/#defining-variables + if (!isCssCustomProperty(name)) { + name = hyphenate(name); + } const {property, hasOverrideFlag, suffix: bindingSuffix} = parseProperty(name); suffix = typeof suffix === 'string' && suffix.length !== 0 ? suffix : bindingSuffix; const entry: @@ -246,10 +250,6 @@ export class StylingBuilder { const entry: BoundStylingEntry = {name: property, value, sourceSpan, hasOverrideFlag, suffix: null}; if (isMapBased) { - if (this._classMapInput) { - throw new Error( - '[class] and [className] bindings cannot be used on the same element simultaneously'); - } this._classMapInput = entry; } else { (this._singleClassInputs = this._singleClassInputs || []).push(entry); @@ -611,6 +611,10 @@ function getStylePropInterpolationExpression(interpolation: Interpolation) { } } -function normalizePropName(prop: string): string { - return hyphenate(prop); +/** + * Checks whether property name is a custom CSS property. + * See: https://www.w3.org/TR/css-variables-1 + */ +function isCssCustomProperty(name: string): boolean { + return name.startsWith('--'); } diff --git a/packages/compiler/src/render3/view/t2_api.ts b/packages/compiler/src/render3/view/t2_api.ts index e87ee6cb94..7946b3b5a7 100644 --- a/packages/compiler/src/render3/view/t2_api.ts +++ b/packages/compiler/src/render3/view/t2_api.ts @@ -74,6 +74,8 @@ export interface DirectiveMeta { * Null otherwise */ exportAs: string[]|null; + + isStructural: boolean; } /** diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts index 4a96cf91ea..e75f7885a4 100644 --- a/packages/compiler/src/render3/view/template.ts +++ b/packages/compiler/src/render3/view/template.ts @@ -39,7 +39,7 @@ import {createLocalizeStatements} from './i18n/localize_utils'; import {I18nMetaVisitor} from './i18n/meta'; import {assembleBoundTextPlaceholders, assembleI18nBoundString, declareI18nVariable, getTranslationConstPrefix, hasI18nMeta, I18N_ICU_MAPPING_PREFIX, i18nFormatPlaceholderNames, icuFromI18nMessage, isI18nRootNode, isSingleI18nIcu, placeholdersToParams, TRANSLATION_VAR_PREFIX, wrapI18nPlaceholder} from './i18n/util'; import {StylingBuilder, StylingInstruction} from './styling_builder'; -import {asLiteral, chainedInstruction, CONTEXT_NAME, getAttrsForDirectiveMatching, getInterpolationArgsLength, IMPLICIT_REFERENCE, invalid, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, trimTrailingNulls, unsupported} from './util'; +import {asLiteral, chainedInstruction, CONTEXT_NAME, getAttrsForDirectiveMatching, getInterpolationArgsLength, IMPLICIT_REFERENCE, invalid, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, RESTORED_VIEW_CONTEXT_NAME, trimTrailingNulls, unsupported} from './util'; @@ -83,8 +83,10 @@ export function prepareEventListenerParameters( eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS); const statements = []; if (scope) { - statements.push(...scope.restoreViewStatement()); + // `variableDeclarations` needs to run first, because + // `restoreViewStatement` depends on the result. statements.push(...scope.variableDeclarations()); + statements.unshift(...scope.restoreViewStatement()); } statements.push(...bindingExpr.render3Stmts); @@ -108,15 +110,31 @@ export function prepareEventListenerParameters( } // Collects information needed to generate `consts` field of the ComponentDef. -// When a constant requires some pre-processing, the `prepareStatements` section -// contains corresponding statements. export interface ComponentDefConsts { + /** + * When a constant requires some pre-processing (e.g. i18n translation block that includes + * goog.getMsg and $localize calls), the `prepareStatements` section contains corresponding + * statements. + */ prepareStatements: o.Statement[]; + + /** + * Actual expressions that represent constants. + */ constExpressions: o.Expression[]; + + /** + * Cache to avoid generating duplicated i18n translation blocks. + */ + i18nVarRefsCache: Map; } function createComponentDefConsts(): ComponentDefConsts { - return {prepareStatements: [], constExpressions: []}; + return { + prepareStatements: [], + constExpressions: [], + i18nVarRefsCache: new Map(), + }; } export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver { @@ -342,8 +360,17 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver (scope: BindingScope, relativeLevel: number) => { let rhs: o.Expression; if (scope.bindingLevel === retrievalLevel) { - // e.g. ctx - rhs = o.variable(CONTEXT_NAME); + if (scope.isListenerScope() && scope.hasRestoreViewVariable()) { + // e.g. restoredCtx. + // We have to get the context from a view reference, if one is available, because + // the context that was passed in during creation may not be correct anymore. + // For more information see: https://github.com/angular/angular/pull/40360. + rhs = o.variable(RESTORED_VIEW_CONTEXT_NAME); + scope.notifyRestoredViewContextUse(); + } else { + // e.g. ctx + rhs = o.variable(CONTEXT_NAME); + } } else { const sharedCtxVar = scope.getSharedContextName(retrievalLevel); // e.g. ctx_r0 OR x(2); @@ -1300,7 +1327,20 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver // Note that static i18n attributes aren't in the i18n array, // because they're treated in the same way as regular attributes. if (attr.i18n) { - attrExprs.push(o.literal(attr.name), this.i18nTranslate(attr.i18n as i18n.Message)); + // When i18n attributes are present on elements with structural directives + // (e.g. `
    `), we want to avoid generating + // duplicate i18n translation blocks for `ɵɵtemplate` and `ɵɵelement` instruction + // attributes. So we do a cache lookup to see if suitable i18n translation block + // already exists. + const {i18nVarRefsCache} = this._constants; + let i18nVarRef: o.ReadVarExpr; + if (i18nVarRefsCache.has(attr.i18n)) { + i18nVarRef = i18nVarRefsCache.get(attr.i18n)!; + } else { + i18nVarRef = this.i18nTranslate(attr.i18n as i18n.Message); + i18nVarRefsCache.set(attr.i18n, i18nVarRef); + } + attrExprs.push(o.literal(attr.name), i18nVarRef); } else { attrExprs.push( ...getAttributeNameLiterals(attr.name), trustedConstAttribute(elementName, attr)); @@ -1629,6 +1669,7 @@ export class BindingScope implements LocalResolver { private map = new Map(); private referenceNameIndex = 0; private restoreViewVariable: o.ReadVarExpr|null = null; + private usesRestoredViewContext = false; static createRootScope(): BindingScope { return new BindingScope(); } @@ -1804,17 +1845,23 @@ export class BindingScope implements LocalResolver { } restoreViewStatement(): o.Statement[] { - // restoreView($state$); - return this.restoreViewVariable ? - [instruction(null, R3.restoreView, [this.restoreViewVariable]).toStmt()] : - []; + const statements: o.Statement[] = []; + if (this.restoreViewVariable) { + const restoreCall = instruction(null, R3.restoreView, [this.restoreViewVariable]); + // Either `const restoredCtx = restoreView($state$);` or `restoreView($state$);` + // depending on whether it is being used. + statements.push( + this.usesRestoredViewContext ? + o.variable(RESTORED_VIEW_CONTEXT_NAME).set(restoreCall).toConstDecl() : + restoreCall.toStmt()); + } + return statements; } viewSnapshotStatements(): o.Statement[] { // const $state$ = getCurrentView(); - const getCurrentViewInstruction = instruction(null, R3.getCurrentView, []); return this.restoreViewVariable ? - [this.restoreViewVariable.set(getCurrentViewInstruction).toConstDecl()] : + [this.restoreViewVariable.set(instruction(null, R3.getCurrentView, [])).toConstDecl()] : []; } @@ -1844,6 +1891,14 @@ export class BindingScope implements LocalResolver { const ref = `${REFERENCE_PREFIX}${current.referenceNameIndex++}`; return ref; } + + hasRestoreViewVariable(): boolean { + return !!this.restoreViewVariable; + } + + notifyRestoredViewContextUse(): void { + this.usesRestoredViewContext = true; + } } /** @@ -1974,6 +2029,10 @@ export interface ParseTemplateOptions { * Include whitespace nodes in the parsed output. */ preserveWhitespaces?: boolean; + /** + * Preserve original line endings instead of normalizing '\r\n' endings to '\n'. + */ + preserveLineEndings?: boolean; /** * How to parse interpolation markers. */ @@ -2037,6 +2096,32 @@ export interface ParseTemplateOptions { * Whether the template was inline. */ isInline?: boolean; + + /** + * Whether to always attempt to convert the parsed HTML AST to an R3 AST, despite HTML or i18n + * Meta parse errors. + * + * + * This option is useful in the context of the language service, where we want to get as much + * information as possible, despite any errors in the HTML. As an example, a user may be adding + * a new tag and expecting autocomplete on that tag. In this scenario, the HTML is in an errored + * state, as there is an incomplete open tag. However, we're still able to convert the HTML AST + * nodes to R3 AST nodes in order to provide information for the language service. + * + * Note that even when `true` the HTML parse and i18n errors are still appended to the errors + * output, but this is done after converting the HTML AST to R3 AST. + */ + alwaysAttemptHtmlToR3AstConversion?: boolean; + + /** + * Include HTML Comment nodes in a top-level comments array on the returned R3 AST. + * + * This option is required by tooling that needs to know the location of comment nodes within the + * AST. A concrete example is @angular-eslint which requires this in order to enable + * "eslint-disable" comments within HTML templates, which then allows users to turn off specific + * rules on a case by case basis, instead of for their whole project within a configuration file. + */ + collectCommentNodes?: boolean; } /** @@ -2056,13 +2141,13 @@ export function parseTemplate( template, templateUrl, {leadingTriviaChars: LEADING_TRIVIA_CHARS, ...options, tokenizeExpansionForms: true}); - if (parseResult.errors && parseResult.errors.length > 0) { - // TODO(ayazhafiz): we may not always want to bail out at this point (e.g. in - // the context of a language service). - return { + if (!options.alwaysAttemptHtmlToR3AstConversion && parseResult.errors && + parseResult.errors.length > 0) { + const parsedTemplate: ParsedTemplate = { interpolationConfig, preserveWhitespaces, template, + templateUrl, isInline, errors: parseResult.errors, nodes: [], @@ -2070,6 +2155,10 @@ export function parseTemplate( styles: [], ngContentSelectors: [] }; + if (options.collectCommentNodes) { + parsedTemplate.commentNodes = []; + } + return parsedTemplate; } let rootNodes: html.Node[] = parseResult.rootNodes; @@ -2083,11 +2172,13 @@ export function parseTemplate( enableI18nLegacyMessageIdFormat); const i18nMetaResult = i18nMetaVisitor.visitAllWithErrors(rootNodes); - if (i18nMetaResult.errors && i18nMetaResult.errors.length > 0) { - return { + if (!options.alwaysAttemptHtmlToR3AstConversion && i18nMetaResult.errors && + i18nMetaResult.errors.length > 0) { + const parsedTemplate: ParsedTemplate = { interpolationConfig, preserveWhitespaces, template, + templateUrl, isInline, errors: i18nMetaResult.errors, nodes: [], @@ -2095,6 +2186,10 @@ export function parseTemplate( styles: [], ngContentSelectors: [] }; + if (options.collectCommentNodes) { + parsedTemplate.commentNodes = []; + } + return parsedTemplate; } rootNodes = i18nMetaResult.rootNodes; @@ -2112,20 +2207,26 @@ export function parseTemplate( } } - const {nodes, errors, styleUrls, styles, ngContentSelectors} = - htmlAstToRender3Ast(rootNodes, bindingParser); + const {nodes, errors, styleUrls, styles, ngContentSelectors, commentNodes} = htmlAstToRender3Ast( + rootNodes, bindingParser, {collectCommentNodes: !!options.collectCommentNodes}); + errors.push(...parseResult.errors, ...i18nMetaResult.errors); - return { + const parsedTemplate: ParsedTemplate = { interpolationConfig, preserveWhitespaces, errors: errors.length > 0 ? errors : null, template, + templateUrl, isInline, nodes, styleUrls, styles, ngContentSelectors }; + if (options.collectCommentNodes) { + parsedTemplate.commentNodes = commentNodes; + } + return parsedTemplate; } const elementRegistry = new DomElementSchemaRegistry(); @@ -2164,10 +2265,16 @@ function trustedConstAttribute(tagName: string, attr: t.TextAttribute): o.Expres if (isTrustedTypesSink(tagName, attr.name)) { switch (elementRegistry.securityContext(tagName, attr.name, /* isAttribute */ true)) { case core.SecurityContext.HTML: - return o.importExpr(R3.trustConstantHtml).callFn([value], attr.valueSpan); + return o.taggedTemplate( + o.importExpr(R3.trustConstantHtml), + new o.TemplateLiteral([new o.TemplateLiteralElement(attr.value)], []), undefined, + attr.valueSpan); // NB: no SecurityContext.SCRIPT here, as the corresponding tags are stripped by the compiler. case core.SecurityContext.RESOURCE_URL: - return o.importExpr(R3.trustConstantResourceUrl).callFn([value], attr.valueSpan); + return o.taggedTemplate( + o.importExpr(R3.trustConstantResourceUrl), + new o.TemplateLiteral([new o.TemplateLiteralElement(attr.value)], []), undefined, + attr.valueSpan); default: return value; } @@ -2286,6 +2393,14 @@ export interface ParsedTemplate { */ template: string|o.Expression; + /** + * A full path to the file which contains the template. + * + * This can be either the original .ts file if the template is inline, or the .html file if an + * external file was used. + */ + templateUrl: string; + /** * Whether the template was inline (using `template`) or external (using `templateUrl`). */ @@ -2317,4 +2432,10 @@ export interface ParsedTemplate { * Any ng-content selectors extracted from the template. */ ngContentSelectors: string[]; + + /** + * Any R3 Comment Nodes extracted from the template when the `collectCommentNodes` parse template + * option is enabled. + */ + commentNodes?: t.Comment[]; } diff --git a/packages/compiler/src/render3/view/util.ts b/packages/compiler/src/render3/view/util.ts index ae69c865ba..e237933a98 100644 --- a/packages/compiler/src/render3/view/util.ts +++ b/packages/compiler/src/render3/view/util.ts @@ -45,6 +45,9 @@ export const IMPLICIT_REFERENCE = '$implicit'; /** Non bindable attribute name **/ export const NON_BINDABLE_ATTR = 'ngNonBindable'; +/** Name for the variable keeping track of the context returned by `ɵɵrestoreView`. */ +export const RESTORED_VIEW_CONTEXT_NAME = 'restoredCtx'; + /** * Creates an allocator for a temporary variable. * @@ -98,17 +101,24 @@ function mapToExpression( let declaredName: string; let publicName: string; let minifiedName: string; + let needsDeclaredName: boolean; if (Array.isArray(value)) { [publicName, declaredName] = value; + minifiedName = key; + needsDeclaredName = publicName !== declaredName; } else { [declaredName, publicName] = splitAtColon(key, [key, value]); + minifiedName = declaredName; + // Only include the declared name if extracted from the key, i.e. the key contains a colon. + // Otherwise the declared name should be omitted even if it is different from the public name, + // as it may have already been minified. + needsDeclaredName = publicName !== declaredName && key.includes(':'); } - minifiedName = declaredName; return { key: minifiedName, // put quotes around keys that contain potentially unsafe characters quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(minifiedName), - value: (keepDeclared && publicName !== declaredName) ? + value: (keepDeclared && needsDeclaredName) ? o.literalArr([asLiteral(publicName), asLiteral(declaredName)]) : asLiteral(publicName) }; @@ -142,12 +152,17 @@ export function getQueryPredicate( } } -export class DefinitionMap { +/** + * A representation for an object literal used during codegen of definition objects. The generic + * type `T` allows to reference a documented type of the generated structure, such that the + * property names that are set can be resolved to their documented declaration. + */ +export class DefinitionMap { values: {key: string, quoted: boolean, value: o.Expression}[] = []; - set(key: string, value: o.Expression|null): void { + set(key: keyof T, value: o.Expression|null): void { if (value) { - this.values.push({key, value, quoted: false}); + this.values.push({key: key as string, value, quoted: false}); } } diff --git a/packages/compiler/src/schema/dom_element_schema_registry.ts b/packages/compiler/src/schema/dom_element_schema_registry.ts index 69b9cb9785..2b6924c061 100644 --- a/packages/compiler/src/schema/dom_element_schema_registry.ts +++ b/packages/compiler/src/schema/dom_element_schema_registry.ts @@ -140,7 +140,7 @@ const SCHEMA: string[] = [ 'progress^[HTMLElement]|#max,#value', 'q,blockquote,cite^[HTMLElement]|', 'script^[HTMLElement]|!async,charset,%crossOrigin,!defer,event,htmlFor,integrity,src,text,type', - 'select^[HTMLElement]|!autofocus,!disabled,#length,!multiple,name,!required,#selectedIndex,#size,value', + 'select^[HTMLElement]|autocomplete,!autofocus,!disabled,#length,!multiple,name,!required,#selectedIndex,#size,value', 'shadow^[HTMLElement]|', 'slot^[HTMLElement]|name', 'source^[HTMLElement]|media,sizes,src,srcset,type', @@ -153,7 +153,7 @@ const SCHEMA: string[] = [ 'tr^[HTMLElement]|align,bgColor,ch,chOff,vAlign', 'tfoot,thead,tbody^[HTMLElement]|align,ch,chOff,vAlign', 'template^[HTMLElement]|', - 'textarea^[HTMLElement]|autocapitalize,!autofocus,#cols,defaultValue,dirName,!disabled,#maxLength,#minLength,name,placeholder,!readOnly,!required,#rows,selectionDirection,#selectionEnd,#selectionStart,value,wrap', + 'textarea^[HTMLElement]|autocapitalize,autocomplete,!autofocus,#cols,defaultValue,dirName,!disabled,#maxLength,#minLength,name,placeholder,!readOnly,!required,#rows,selectionDirection,#selectionEnd,#selectionStart,value,wrap', 'title^[HTMLElement]|text', 'track^[HTMLElement]|!default,kind,label,src,srclang', 'ul^[HTMLElement]|!compact,type', @@ -240,6 +240,13 @@ const _ATTR_TO_PROP: {[name: string]: string} = { 'tabindex': 'tabIndex', }; +// Invert _ATTR_TO_PROP. +const _PROP_TO_ATTR: {[name: string]: string} = + Object.keys(_ATTR_TO_PROP).reduce((inverted, attr) => { + inverted[_ATTR_TO_PROP[attr]] = attr; + return inverted; + }, {} as {[prop: string]: string}); + export class DomElementSchemaRegistry extends ElementSchemaRegistry { private _schema: {[element: string]: {[property: string]: string}} = {}; @@ -386,6 +393,12 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry { return Object.keys(this._schema); } + allKnownAttributesOfElement(tagName: string): string[] { + const elementProperties = this._schema[tagName.toLowerCase()] || this._schema['unknown']; + // Convert properties to attributes. + return Object.keys(elementProperties).map(prop => _PROP_TO_ATTR[prop] ?? prop); + } + normalizeAnimationStyleProperty(propName: string): string { return dashCaseToCamelCase(propName); } diff --git a/packages/compiler/src/shadow_css.ts b/packages/compiler/src/shadow_css.ts index 72463ca817..e8d08e7adb 100644 --- a/packages/compiler/src/shadow_css.ts +++ b/packages/compiler/src/shadow_css.ts @@ -260,7 +260,21 @@ export class ShadowCss { * .foo > .bar */ private _convertColonHost(cssText: string): string { - return this._convertColonRule(cssText, _cssColonHostRe, this._colonHostPartReplacer); + return cssText.replace(_cssColonHostRe, (_, hostSelectors: string, otherSelectors: string) => { + if (hostSelectors) { + const convertedSelectors: string[] = []; + const hostSelectorArray = hostSelectors.split(',').map(p => p.trim()); + for (const hostSelector of hostSelectorArray) { + if (!hostSelector) break; + const convertedSelector = + _polyfillHostNoCombinator + hostSelector.replace(_polyfillHost, '') + otherSelectors; + convertedSelectors.push(convertedSelector); + } + return convertedSelectors.join(','); + } else { + return _polyfillHostNoCombinator + otherSelectors; + } + }); } /* @@ -268,7 +282,7 @@ export class ShadowCss { * * to * - * .foo > .bar, .foo scopeName > .bar { } + * .foo > .bar, .foo > .bar { } * * and * @@ -279,40 +293,67 @@ export class ShadowCss { * .foo .bar { ... } */ private _convertColonHostContext(cssText: string): string { - return this._convertColonRule( - cssText, _cssColonHostContextRe, this._colonHostContextPartReplacer); - } + return cssText.replace(_cssColonHostContextReGlobal, selectorText => { + // We have captured a selector that contains a `:host-context` rule. - private _convertColonRule(cssText: string, regExp: RegExp, partReplacer: Function): string { - // m[1] = :host(-context), m[2] = contents of (), m[3] rest of rule - return cssText.replace(regExp, function(...m: string[]) { - if (m[2]) { - const parts = m[2].split(','); - const r: string[] = []; - for (let i = 0; i < parts.length; i++) { - const p = parts[i].trim(); - if (!p) break; - r.push(partReplacer(_polyfillHostNoCombinator, p, m[3])); + // For backward compatibility `:host-context` may contain a comma separated list of selectors. + // Each context selector group will contain a list of host-context selectors that must match + // an ancestor of the host. + // (Normally `contextSelectorGroups` will only contain a single array of context selectors.) + const contextSelectorGroups: string[][] = [[]]; + + // There may be more than `:host-context` in this selector so `selectorText` could look like: + // `:host-context(.one):host-context(.two)`. + // Execute `_cssColonHostContextRe` over and over until we have extracted all the + // `:host-context` selectors from this selector. + let match: RegExpMatchArray|null; + while (match = _cssColonHostContextRe.exec(selectorText)) { + // `match` = [':host-context()', , ] + + // The `` could actually be a comma separated list: `:host-context(.one, .two)`. + const newContextSelectors = + (match[1] ?? '').trim().split(',').map(m => m.trim()).filter(m => m !== ''); + + // We must duplicate the current selector group for each of these new selectors. + // For example if the current groups are: + // ``` + // [ + // ['a', 'b', 'c'], + // ['x', 'y', 'z'], + // ] + // ``` + // And we have a new set of comma separated selectors: `:host-context(m,n)` then the new + // groups are: + // ``` + // [ + // ['a', 'b', 'c', 'm'], + // ['x', 'y', 'z', 'm'], + // ['a', 'b', 'c', 'n'], + // ['x', 'y', 'z', 'n'], + // ] + // ``` + const contextSelectorGroupsLength = contextSelectorGroups.length; + repeatGroups(contextSelectorGroups, newContextSelectors.length); + for (let i = 0; i < newContextSelectors.length; i++) { + for (let j = 0; j < contextSelectorGroupsLength; j++) { + contextSelectorGroups[j + (i * contextSelectorGroupsLength)].push( + newContextSelectors[i]); + } } - return r.join(','); - } else { - return _polyfillHostNoCombinator + m[3]; + + // Update the `selectorText` and see repeat to see if there are more `:host-context`s. + selectorText = match[2]; } + + // The context selectors now must be combined with each other to capture all the possible + // selectors that `:host-context` can match. See `combineHostContextSelectors()` for more + // info about how this is done. + return contextSelectorGroups + .map(contextSelectors => combineHostContextSelectors(contextSelectors, selectorText)) + .join(', '); }); } - private _colonHostContextPartReplacer(host: string, part: string, suffix: string): string { - if (part.indexOf(_polyfillHost) > -1) { - return this._colonHostPartReplacer(host, part, suffix); - } else { - return host + part + suffix + ', ' + part + ' ' + host + suffix; - } - } - - private _colonHostPartReplacer(host: string, part: string, suffix: string): string { - return host + part.replace(_polyfillHost, '') + suffix; - } - /* * Convert combinators like ::shadow and pseudo-elements like ::content * by replacing with space. @@ -485,12 +526,14 @@ class SafeSelector { constructor(selector: string) { // Replaces attribute selectors with placeholders. // The WS in [attr="va lue"] would otherwise be interpreted as a selector separator. - selector = selector.replace(/(\[[^\]]*\])/g, (_, keep) => { - const replaceBy = `__ph-${this.index}__`; - this.placeholders.push(keep); - this.index++; - return replaceBy; - }); + selector = this._escapeRegexMatches(selector, /(\[[^\]]*\])/g); + + // CSS allows for certain special characters to be used in selectors if they're escaped. + // E.g. `.foo:blue` won't match a class called `foo:blue`, because the colon denotes a + // pseudo-class, but writing `.foo\:blue` will match, because the colon was escaped. + // Replace all escape sequences (`\` followed by a character) with a placeholder so + // that our handling of pseudo-selectors doesn't mess with them. + selector = this._escapeRegexMatches(selector, /(\\.)/g); // Replaces the expression in `:nth-child(2n + 1)` with a placeholder. // WS and "+" would otherwise be interpreted as selector separators. @@ -503,12 +546,25 @@ class SafeSelector { } restore(content: string): string { - return content.replace(/__ph-(\d+)__/g, (ph, index) => this.placeholders[+index]); + return content.replace(/__ph-(\d+)__/g, (_ph, index) => this.placeholders[+index]); } content(): string { return this._content; } + + /** + * Replaces all of the substrings that match a regex within a + * special string (e.g. `__ph-0__`, `__ph-1__`, etc). + */ + private _escapeRegexMatches(content: string, pattern: RegExp): string { + return content.replace(pattern, (_, keep) => { + const replaceBy = `__ph-${this.index}__`; + this.placeholders.push(keep); + this.index++; + return replaceBy; + }); + } } const _cssContentNextSelectorRe = @@ -519,11 +575,12 @@ const _cssContentUnscopedRuleRe = const _polyfillHost = '-shadowcsshost'; // note: :host-context pre-processed to -shadowcsshostcontext. const _polyfillHostContext = '-shadowcsscontext'; -const _parenSuffix = ')(?:\\((' + +const _parenSuffix = '(?:\\((' + '(?:\\([^)(]*\\)|[^)(]*)+?' + ')\\))?([^,{]*)'; -const _cssColonHostRe = new RegExp('(' + _polyfillHost + _parenSuffix, 'gim'); -const _cssColonHostContextRe = new RegExp('(' + _polyfillHostContext + _parenSuffix, 'gim'); +const _cssColonHostRe = new RegExp(_polyfillHost + _parenSuffix, 'gim'); +const _cssColonHostContextReGlobal = new RegExp(_polyfillHostContext + _parenSuffix, 'gim'); +const _cssColonHostContextRe = new RegExp(_polyfillHostContext + _parenSuffix, 'im'); const _polyfillHostNoCombinator = _polyfillHost + '-no-combinator'; const _polyfillHostNoCombinatorRe = /-shadowcsshost-no-combinator([^\s]*)/; const _shadowDOMSelectorsRe = [ @@ -635,3 +692,82 @@ function escapeBlocks( } return new StringWithEscapedBlocks(resultParts.join(''), escapedBlocks); } + +/** + * Combine the `contextSelectors` with the `hostMarker` and the `otherSelectors` + * to create a selector that matches the same as `:host-context()`. + * + * Given a single context selector `A` we need to output selectors that match on the host and as an + * ancestor of the host: + * + * ``` + * A , A {} + * ``` + * + * When there is more than one context selector we also have to create combinations of those + * selectors with each other. For example if there are `A` and `B` selectors the output is: + * + * ``` + * AB, AB , A B, + * B A, A B , B A {} + * ``` + * + * And so on... + * + * @param hostMarker the string that selects the host element. + * @param contextSelectors an array of context selectors that will be combined. + * @param otherSelectors the rest of the selectors that are not context selectors. + */ +function combineHostContextSelectors(contextSelectors: string[], otherSelectors: string): string { + const hostMarker = _polyfillHostNoCombinator; + _polyfillHostRe.lastIndex = 0; // reset the regex to ensure we get an accurate test + const otherSelectorsHasHost = _polyfillHostRe.test(otherSelectors); + + // If there are no context selectors then just output a host marker + if (contextSelectors.length === 0) { + return hostMarker + otherSelectors; + } + + const combined: string[] = [contextSelectors.pop() || '']; + while (contextSelectors.length > 0) { + const length = combined.length; + const contextSelector = contextSelectors.pop(); + for (let i = 0; i < length; i++) { + const previousSelectors = combined[i]; + // Add the new selector as a descendant of the previous selectors + combined[length * 2 + i] = previousSelectors + ' ' + contextSelector; + // Add the new selector as an ancestor of the previous selectors + combined[length + i] = contextSelector + ' ' + previousSelectors; + // Add the new selector to act on the same element as the previous selectors + combined[i] = contextSelector + previousSelectors; + } + } + // Finally connect the selector to the `hostMarker`s: either acting directly on the host + // (A) or as an ancestor (A ). + return combined + .map( + s => otherSelectorsHasHost ? + `${s}${otherSelectors}` : + `${s}${hostMarker}${otherSelectors}, ${s} ${hostMarker}${otherSelectors}`) + .join(','); +} + +/** + * Mutate the given `groups` array so that there are `multiples` clones of the original array + * stored. + * + * For example `repeatGroups([a, b], 3)` will result in `[a, b, a, b, a, b]` - but importantly the + * newly added groups will be clones of the original. + * + * @param groups An array of groups of strings that will be repeated. This array is mutated + * in-place. + * @param multiples The number of times the current groups should appear. + */ +export function repeatGroups(groups: string[][], multiples: number): void { + const length = groups.length; + for (let i = 1; i < multiples; i++) { + for (let j = 0; j < length; j++) { + groups[j + (i * length)] = groups[j].slice(0); + } + } +} diff --git a/packages/compiler/src/template_parser/binding_parser.ts b/packages/compiler/src/template_parser/binding_parser.ts index ea3636673a..daffb70155 100644 --- a/packages/compiler/src/template_parser/binding_parser.ts +++ b/packages/compiler/src/template_parser/binding_parser.ts @@ -120,16 +120,17 @@ export class BindingParser { parseInterpolation(value: string, sourceSpan: ParseSourceSpan): ASTWithSource { const sourceInfo = sourceSpan.start.toString(); + const absoluteOffset = sourceSpan.fullStart.offset; try { const ast = this._exprParser.parseInterpolation( - value, sourceInfo, sourceSpan.start.offset, this._interpolationConfig)!; + value, sourceInfo, absoluteOffset, this._interpolationConfig)!; if (ast) this._reportExpressionParserErrors(ast.errors, sourceSpan); this._checkPipes(ast, sourceSpan); return ast; } catch (e) { this._reportError(`${e}`, sourceSpan); - return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, sourceSpan.start.offset); + return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset); } } @@ -140,16 +141,17 @@ export class BindingParser { */ parseInterpolationExpression(expression: string, sourceSpan: ParseSourceSpan): ASTWithSource { const sourceInfo = sourceSpan.start.toString(); + const absoluteOffset = sourceSpan.start.offset; try { - const ast = this._exprParser.parseInterpolationExpression( - expression, sourceInfo, sourceSpan.start.offset); + const ast = + this._exprParser.parseInterpolationExpression(expression, sourceInfo, absoluteOffset); if (ast) this._reportExpressionParserErrors(ast.errors, sourceSpan); this._checkPipes(ast, sourceSpan); return ast; } catch (e) { this._reportError(`${e}`, sourceSpan); - return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, sourceSpan.start.offset); + return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset); } } @@ -244,6 +246,10 @@ export class BindingParser { targetProps: ParsedProperty[], keySpan?: ParseSourceSpan) { if (isAnimationLabel(name)) { name = name.substring(1); + if (keySpan !== undefined) { + keySpan = moveParseSourceSpan( + keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset)); + } if (value) { this._reportError( `Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` + @@ -274,9 +280,19 @@ export class BindingParser { if (name.startsWith(ANIMATE_PROP_PREFIX)) { isAnimationProp = true; name = name.substring(ANIMATE_PROP_PREFIX.length); + if (keySpan !== undefined) { + keySpan = moveParseSourceSpan( + keySpan, + new AbsoluteSourceSpan( + keySpan.start.offset + ANIMATE_PROP_PREFIX.length, keySpan.end.offset)); + } } else if (isAnimationLabel(name)) { isAnimationProp = true; name = name.substring(1); + if (keySpan !== undefined) { + keySpan = moveParseSourceSpan( + keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset)); + } } if (isAnimationProp) { @@ -424,6 +440,10 @@ export class BindingParser { if (isAnimationLabel(name)) { name = name.substr(1); + if (keySpan !== undefined) { + keySpan = moveParseSourceSpan( + keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset)); + } this._parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan); } else { this._parseRegularEvent( @@ -443,21 +463,19 @@ export class BindingParser { const matches = splitAtPeriod(name, [name, '']); const eventName = matches[0]; const phase = matches[1].toLowerCase(); - if (phase) { - switch (phase) { - case 'start': - case 'done': - const ast = this._parseAction(expression, handlerSpan); - targetEvents.push(new ParsedEvent( - eventName, phase, ParsedEventType.Animation, ast, sourceSpan, handlerSpan, keySpan)); - break; + const ast = this._parseAction(expression, handlerSpan); + targetEvents.push(new ParsedEvent( + eventName, phase, ParsedEventType.Animation, ast, sourceSpan, handlerSpan, keySpan)); - default: - this._reportError( - `The provided animation output phase value "${phase}" for "@${ - eventName}" is not supported (use start or done)`, - sourceSpan); - break; + if (eventName.length === 0) { + this._reportError(`Animation event name is missing in binding`, sourceSpan); + } + if (phase) { + if (phase !== 'start' && phase !== 'done') { + this._reportError( + `The provided animation output phase value "${phase}" for "@${ + eventName}" is not supported (use start or done)`, + sourceSpan); } } else { this._reportError( diff --git a/packages/compiler/src/url_resolver.ts b/packages/compiler/src/url_resolver.ts index dc13d283ac..6e11f16f7d 100644 --- a/packages/compiler/src/url_resolver.ts +++ b/packages/compiler/src/url_resolver.ts @@ -26,7 +26,7 @@ export function createOfflineCompileUrlResolver(): UrlResolver { * * ## Example * - * {@example compiler/ts/url_resolver/url_resolver.ts region='url_resolver'} + * * * @security When compiling templates at runtime, you must * ensure that the entire template comes from a trusted source. diff --git a/packages/compiler/src/view_compiler/view_compiler.ts b/packages/compiler/src/view_compiler/view_compiler.ts index 6f3254927c..b8c9f76c80 100644 --- a/packages/compiler/src/view_compiler/view_compiler.ts +++ b/packages/compiler/src/view_compiler/view_compiler.ts @@ -143,7 +143,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { // Note: queries start with id 1 so we can use the number in a Bloom filter! const queryId = queryIndex + 1; const bindingType = query.first ? QueryBindingType.First : QueryBindingType.All; - const flags = NodeFlags.TypeViewQuery | calcStaticDynamicQueryFlags(query); + const flags = NodeFlags.TypeViewQuery | calcQueryFlags(query); this.nodes.push(() => ({ sourceSpan: null, nodeFlags: flags, @@ -485,7 +485,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { dirAst.directive.queries.forEach((query, queryIndex) => { const queryId = dirAst.contentQueryStartId + queryIndex; - const flags = NodeFlags.TypeContentQuery | calcStaticDynamicQueryFlags(query); + const flags = NodeFlags.TypeContentQuery | calcQueryFlags(query); const bindingType = query.first ? QueryBindingType.First : QueryBindingType.All; this.nodes.push(() => ({ sourceSpan: dirAst.sourceSpan, @@ -1028,7 +1028,7 @@ function elementEventNameAndTarget( } } -function calcStaticDynamicQueryFlags(query: CompileQueryMetadata) { +function calcQueryFlags(query: CompileQueryMetadata) { let flags = NodeFlags.None; // Note: We only make queries static that query for a single item and the user specifically // set the to be static. This is because of backwards compatibility with the old view compiler... @@ -1037,6 +1037,9 @@ function calcStaticDynamicQueryFlags(query: CompileQueryMetadata) { } else { flags |= NodeFlags.DynamicQuery; } + if (query.emitDistinctChangesOnly) { + flags |= NodeFlags.EmitDistinctChangesOnly; + } return flags; } diff --git a/packages/compiler/test/aot/jit_summaries_spec.ts b/packages/compiler/test/aot/jit_summaries_spec.ts index aaad84555c..9d37f8ce52 100644 --- a/packages/compiler/test/aot/jit_summaries_spec.ts +++ b/packages/compiler/test/aot/jit_summaries_spec.ts @@ -448,7 +448,7 @@ describe('aot summaries for jit', () => { expect(lib3ModuleNgSummarySource) .toMatch(/export function Lib3ModuleNgSummary\(\).*reference:i3\.ReexportModule,/s); // ngsummaries should re-export all used summaries directly. With external symbol re-exports - // enabled, the the "lib1" summaries would be re-exported through "lib2" in order to avoid + // enabled, the "lib1" summaries would be re-exported through "lib2" in order to avoid // a *direct* dependency on "lib1". expect(lib3ModuleNgSummarySource) .toContain( diff --git a/packages/compiler/test/aot/test_util.ts b/packages/compiler/test/aot/test_util.ts index 41d6a3858f..bb37cf06fe 100644 --- a/packages/compiler/test/aot/test_util.ts +++ b/packages/compiler/test/aot/test_util.ts @@ -427,7 +427,7 @@ export class MockAotCompilerHost implements AotCompilerHost { if (this.metadataVisible) { const metadataPath = modulePath.replace(DTS, '.metadata.json'); if (this.tsHost.fileExists(metadataPath)) { - let result = JSON.parse(this.tsHost.readFile(metadataPath)); + let result = JSON.parse(this.tsHost.readFile(metadataPath)) as {[key: string]: any}[]; return Array.isArray(result) ? result : [result]; } } diff --git a/packages/compiler/test/compiler_facade_interface_spec.ts b/packages/compiler/test/compiler_facade_interface_spec.ts index 963bfa5d17..e0481e504a 100644 --- a/packages/compiler/test/compiler_facade_interface_spec.ts +++ b/packages/compiler/test/compiler_facade_interface_spec.ts @@ -7,7 +7,7 @@ */ import * as core from '../../core/src/compiler/compiler_facade_interface'; -import {R3FactoryTarget, R3ResolvedDependencyType} from '../public_api'; +import {FactoryTarget} from '../public_api'; import * as compiler from '../src/compiler_facade_interface'; /** @@ -45,39 +45,32 @@ 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 coreR3ResolvedDependencyType: core.R3ResolvedDependencyType = - null! as compiler.R3ResolvedDependencyType; -const compilerR3ResolvedDependencyType: compiler.R3ResolvedDependencyType = - null! as core.R3ResolvedDependencyType; +const coreR3FactoryTarget: core.FactoryTarget = null! as compiler.FactoryTarget; +const compilerR3FactoryTarget: compiler.FactoryTarget = null! as core.FactoryTarget; -const coreR3ResolvedDependencyType2: R3ResolvedDependencyType = - null! as core.R3ResolvedDependencyType; -const compilerR3ResolvedDependencyType2: R3ResolvedDependencyType = - null! as core.R3ResolvedDependencyType; +const coreR3FactoryTarget2: FactoryTarget = null! as core.FactoryTarget; +const compilerR3FactoryTarget2: FactoryTarget = null! as core.FactoryTarget; -const coreR3ResolvedDependencyType3: core.R3ResolvedDependencyType = - null! as R3ResolvedDependencyType; -const compilerR3ResolvedDependencyType3: compiler.R3ResolvedDependencyType = - null! as R3ResolvedDependencyType; - -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 coreR3FactoryTarget3: core.R3FactoryTarget = null! as R3FactoryTarget; -const compilerR3FactoryTarget3: compiler.R3FactoryTarget = null! as R3FactoryTarget; +const coreR3FactoryTarget3: core.FactoryTarget = null! as FactoryTarget; +const compilerR3FactoryTarget3: compiler.FactoryTarget = null! as FactoryTarget; const coreR3DependencyMetadataFacade: core.R3DependencyMetadataFacade = null! as compiler.R3DependencyMetadataFacade; const compilerR3DependencyMetadataFacade: compiler.R3DependencyMetadataFacade = null! as core.R3DependencyMetadataFacade; +const coreR3DeclareDependencyMetadataFacade: core.R3DeclareDependencyMetadataFacade = + null! as compiler.R3DeclareDependencyMetadataFacade; +const compilerR3DeclareDependencyMetadataFacade: compiler.R3DeclareDependencyMetadataFacade = + null! as core.R3DeclareDependencyMetadataFacade; + const coreR3PipeMetadataFacade: core.R3PipeMetadataFacade = null! as compiler.R3PipeMetadataFacade; const compilerR3PipeMetadataFacade: compiler.R3PipeMetadataFacade = null! as core.R3PipeMetadataFacade; +const coreR3DeclarePipeFacade: core.R3DeclarePipeFacade = null! as compiler.R3DeclarePipeFacade; +const compilerR3DeclarePipeFacade: compiler.R3DeclarePipeFacade = null! as core.R3DeclarePipeFacade; + const coreR3InjectableMetadataFacade: core.R3InjectableMetadataFacade = null! as compiler.R3InjectableMetadataFacade; const compilerR3InjectableMetadataFacade: compiler.R3InjectableMetadataFacade = @@ -88,21 +81,51 @@ const coreR3NgModuleMetadataFacade: core.R3NgModuleMetadataFacade = const compilerR3NgModuleMetadataFacade: compiler.R3NgModuleMetadataFacade = null! as core.R3NgModuleMetadataFacade; +const coreR3DeclareNgModuleFacade: core.R3DeclareNgModuleFacade = + null! as compiler.R3DeclareNgModuleFacade; +const compilerR3DeclareNgModuleFacade: compiler.R3DeclareNgModuleFacade = + null! as core.R3DeclareNgModuleFacade; + const coreR3InjectorMetadataFacade: core.R3InjectorMetadataFacade = null! as compiler.R3InjectorMetadataFacade; const compilerR3InjectorMetadataFacade: compiler.R3InjectorMetadataFacade = null! as core.R3InjectorMetadataFacade; +const coreR3DeclareInjectorFacade: core.R3DeclareInjectorFacade = + null! as compiler.R3DeclareInjectorFacade; +const compilerR3DeclareInjectorFacade: compiler.R3DeclareInjectorFacade = + null! as core.R3DeclareInjectorFacade; + const coreR3DirectiveMetadataFacade: core.R3DirectiveMetadataFacade = null! as compiler.R3DirectiveMetadataFacade; const compilerR3DirectiveMetadataFacade: compiler.R3DirectiveMetadataFacade = null! as core.R3DirectiveMetadataFacade; +const coreR3DeclareDirectiveFacade: core.R3DeclareDirectiveFacade = + null! as compiler.R3DeclareDirectiveFacade; +const compilerR3DeclareDirectiveFacade: compiler.R3DeclareDirectiveFacade = + null! as core.R3DeclareDirectiveFacade; + const coreR3ComponentMetadataFacade: core.R3ComponentMetadataFacade = null! as compiler.R3ComponentMetadataFacade; const compilerR3ComponentMetadataFacade: compiler.R3ComponentMetadataFacade = null! as core.R3ComponentMetadataFacade; +const coreR3DeclareComponentFacade: core.R3DeclareComponentFacade = + null! as compiler.R3DeclareComponentFacade; +const compilerR3DeclareComponentFacade: compiler.R3DeclareComponentFacade = + null! as core.R3DeclareComponentFacade; + +const coreR3DeclareUsedDirectiveFacade: core.R3DeclareUsedDirectiveFacade = + null! as compiler.R3DeclareUsedDirectiveFacade; +const compilerR3DeclareUsedDirectiveFacade: compiler.R3DeclareUsedDirectiveFacade = + null! as core.R3DeclareUsedDirectiveFacade; + +const coreR3UsedDirectiveMetadata: core.R3UsedDirectiveMetadata = + null! as compiler.R3UsedDirectiveMetadata; +const compilerR3UsedDirectiveMetadata: compiler.R3UsedDirectiveMetadata = + null! as core.R3UsedDirectiveMetadata; + const coreViewEncapsulation: core.ViewEncapsulation = null! as compiler.ViewEncapsulation; const compilerViewEncapsulation: compiler.ViewEncapsulation = null! as core.ViewEncapsulation; @@ -110,3 +133,8 @@ const coreR3QueryMetadataFacade: core.R3QueryMetadataFacade = null! as compiler.R3QueryMetadataFacade; const compilerR3QueryMetadataFacade: compiler.R3QueryMetadataFacade = null! as core.R3QueryMetadataFacade; + +const coreR3DeclareQueryMetadataFacade: core.R3DeclareQueryMetadataFacade = + null! as compiler.R3DeclareQueryMetadataFacade; +const compilerR3DeclareQueryMetadataFacade: compiler.R3DeclareQueryMetadataFacade = + null! as core.R3DeclareQueryMetadataFacade; diff --git a/packages/compiler/test/core_spec.ts b/packages/compiler/test/core_spec.ts index cf045725a0..6e98b952c4 100644 --- a/packages/compiler/test/core_spec.ts +++ b/packages/compiler/test/core_spec.ts @@ -153,7 +153,8 @@ import * as core from '@angular/core'; expectToBe(compilerCore.InjectFlags.Default, core.InjectFlags.Default); expectToBe(compilerCore.InjectFlags.SkipSelf, core.InjectFlags.SkipSelf); expectToBe(compilerCore.InjectFlags.Self, core.InjectFlags.Self); - + expectToBe(compilerCore.InjectFlags.Host, core.InjectFlags.Host); + expectToBe(compilerCore.InjectFlags.Optional, core.InjectFlags.Optional); expectToBe(compilerCore.ArgumentType.Inline, core.ɵArgumentType.Inline); expectToBe(compilerCore.ArgumentType.Dynamic, core.ɵArgumentType.Dynamic); diff --git a/packages/compiler/test/css_parser/BUILD.bazel b/packages/compiler/test/css_parser/BUILD.bazel deleted file mode 100644 index 13f19b8330..0000000000 --- a/packages/compiler/test/css_parser/BUILD.bazel +++ /dev/null @@ -1,30 +0,0 @@ -load("//tools:defaults.bzl", "jasmine_node_test", "karma_web_test_suite", "ts_library") - -ts_library( - name = "css_parser_lib", - testonly = True, - srcs = glob(["**/*.ts"]), - deps = [ - "//packages:types", - "//packages/compiler", - "//packages/compiler/testing", - "//packages/core/testing", - "//packages/platform-browser", - "//packages/platform-browser/testing", - ], -) - -jasmine_node_test( - name = "css_parser", - bootstrap = ["//tools/testing:node_es5"], - deps = [ - ":css_parser_lib", - ], -) - -karma_web_test_suite( - name = "css_parser_web", - deps = [ - ":css_parser_lib", - ], -) diff --git a/packages/compiler/test/css_parser/css_lexer_spec.ts b/packages/compiler/test/css_parser/css_lexer_spec.ts deleted file mode 100644 index 20879d7a42..0000000000 --- a/packages/compiler/test/css_parser/css_lexer_spec.ts +++ /dev/null @@ -1,387 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {describe, expect, it} from '../../../core/testing/src/testing_internal'; -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(); - } - - return tokens; -} - -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 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 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 lex simple selectors and their inner properties', () => { - const cssCode = '\n' + - ' .selector { my-prop: my-value; }\n'; - const tokens = tokenize(cssCode); - - expect(tokens[0].type).toEqual(CssTokenType.Character); - expect(tokens[0].strValue).toEqual('.'); - - expect(tokens[1].type).toEqual(CssTokenType.Identifier); - expect(tokens[1].strValue).toEqual('selector'); - - expect(tokens[2].type).toEqual(CssTokenType.Character); - expect(tokens[2].strValue).toEqual('{'); - - expect(tokens[3].type).toEqual(CssTokenType.Identifier); - expect(tokens[3].strValue).toEqual('my-prop'); - - expect(tokens[4].type).toEqual(CssTokenType.Character); - expect(tokens[4].strValue).toEqual(':'); - - expect(tokens[5].type).toEqual(CssTokenType.Identifier); - expect(tokens[5].strValue).toEqual('my-value'); - - expect(tokens[6].type).toEqual(CssTokenType.Character); - expect(tokens[6].strValue).toEqual(';'); - - expect(tokens[7].type).toEqual(CssTokenType.Character); - expect(tokens[7].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); - }); - - it('should lex quoted strings and escape accordingly', () => { - const cssCode = 'prop: \'some { value } \\\' that is quoted\''; - const tokens = tokenize(cssCode); - - 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); - - 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[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[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[0].type).toEqual(CssTokenType.AtKeyword); - expect(tokens[0].strValue).toEqual('@import'); - - expect(tokens[1].type).toEqual(CssTokenType.Character); - expect(tokens[1].strValue).toEqual('('); - - expect(tokens[2].type).toEqual(CssTokenType.Character); - expect(tokens[2].strValue).toEqual(')'); - - expect(tokens[3].type).toEqual(CssTokenType.AtKeyword); - expect(tokens[3].strValue).toEqual('@something'); - }); - - 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[0].type).toEqual(CssTokenType.Number); - expect(tokens[0].strValue).toEqual('40'); - - expect(tokens[1].type).toEqual(CssTokenType.Character); - expect(tokens[1].strValue).toEqual('%'); - - expect(tokens[2].type).toEqual(CssTokenType.Identifier); - expect(tokens[2].strValue).toEqual('is'); - - expect(tokens[3].type).toEqual(CssTokenType.Number); - expect(tokens[3].strValue).toEqual('40'); - }); - - it('should allow escaped character and unicode character-strings in CSS selectors', () => { - const cssCode = '\\123456 .some\\thing \{\}'; - const tokens = tokenize(cssCode); - - expect(tokens[0].type).toEqual(CssTokenType.Identifier); - expect(tokens[0].strValue).toEqual('\\123456'); - - expect(tokens[1].type).toEqual(CssTokenType.Character); - expect(tokens[2].type).toEqual(CssTokenType.Identifier); - expect(tokens[2].strValue).toEqual('some\\thing'); - }); - - it('should distinguish identifiers and numbers from special characters', () => { - const cssCode = 'one*two=-4+three-4-equals_value$'; - const tokens = tokenize(cssCode); - - expect(tokens[0].type).toEqual(CssTokenType.Identifier); - expect(tokens[0].strValue).toEqual('one'); - - expect(tokens[1].type).toEqual(CssTokenType.Character); - expect(tokens[1].strValue).toEqual('*'); - - expect(tokens[2].type).toEqual(CssTokenType.Identifier); - expect(tokens[2].strValue).toEqual('two'); - - expect(tokens[3].type).toEqual(CssTokenType.Character); - expect(tokens[3].strValue).toEqual('='); - - expect(tokens[4].type).toEqual(CssTokenType.Number); - expect(tokens[4].strValue).toEqual('-4'); - - expect(tokens[5].type).toEqual(CssTokenType.Character); - expect(tokens[5].strValue).toEqual('+'); - - expect(tokens[6].type).toEqual(CssTokenType.Identifier); - expect(tokens[6].strValue).toEqual('three-4-equals_value'); - - expect(tokens[7].type).toEqual(CssTokenType.Character); - expect(tokens[7].strValue).toEqual('$'); - }); - - it('should filter out comments and whitespace by default', () => { - const cssCode = '.selector /* comment */ { /* value */ }'; - const tokens = tokenize(cssCode); - - expect(tokens[0].strValue).toEqual('.'); - expect(tokens[1].strValue).toEqual('selector'); - expect(tokens[2].strValue).toEqual('{'); - expect(tokens[3].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[0].strValue).toEqual('.'); - expect(tokens[1].strValue).toEqual('selector'); - expect(tokens[2].strValue).toEqual(' '); - - expect(tokens[3].type).toEqual(CssTokenType.Comment); - expect(tokens[3].strValue).toEqual('/* comment */'); - - expect(tokens[4].strValue).toEqual(' '); - expect(tokens[5].strValue).toEqual('{'); - expect(tokens[6].strValue).toEqual(' '); - - expect(tokens[7].type).toEqual(CssTokenType.Comment); - expect(tokens[7].strValue).toEqual('/* value */'); - }); - - describe('Selector Mode', () => { - it('should throw an error if a selector is being parsed while in the wrong mode', () => { - const cssCode = '.class > tag'; - - let capturedMessage: string|null = null; - try { - tokenize(cssCode, false, CssLexerMode.STYLE_BLOCK); - } catch (e) { - capturedMessage = getRawMessage(e); - } - - expect(capturedMessage).toMatch(/Unexpected character \[\>\] at column 0:7 in expression/g); - - capturedMessage = null; - try { - tokenize(cssCode, false, CssLexerMode.SELECTOR); - } catch (e) { - capturedMessage = getRawMessage(e); - } - - expect(capturedMessage).toEqual(null); - }); - }); - - 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(); - }); - }); - - 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); - - 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( - '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(` - 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(); - }); - }); -}); -})(); diff --git a/packages/compiler/test/css_parser/css_parser_spec.ts b/packages/compiler/test/css_parser/css_parser_spec.ts deleted file mode 100644 index 3020ee87ee..0000000000 --- a/packages/compiler/test/css_parser/css_parser_spec.ts +++ /dev/null @@ -1,802 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - - -import {describe, expect, it} from '../../../core/testing/src/testing_internal'; -import {CssBlockAst, CssBlockDefinitionRuleAst, CssBlockRuleAst, CssDefinitionAst, CssInlineRuleAst, CssKeyframeDefinitionAst, CssKeyframeRuleAst, CssMediaQueryRuleAst, CssSelectorRuleAst, CssStyleSheetAst, CssStyleValueAst} from '../../src/css_parser/css_ast'; -import {BlockType, CssParseError, CssParser, CssToken, ParsedCssResult} from '../../src/css_parser/css_parser'; -import {ParseLocation} from '../../src/parse_util'; - -export function assertTokens(tokens: CssToken[], valuesArr: string[]) { - for (let i = 0; i < tokens.length; i++) { - expect(tokens[i].strValue == valuesArr[i]); - } -} - -{ - describe('CssParser', () => { - function parse(css: string): ParsedCssResult { - return new CssParser().parse(css, 'some-fake-css-file.css'); - } - - function makeAst(css: string): CssStyleSheetAst { - const output = parse(css); - const errors = output.errors; - if (errors.length > 0) { - throw new Error(errors.map((error: CssParseError) => error.msg).join(', ')); - } - return output.ast; - } - - it('should parse CSS into a stylesheet Ast', () => { - const styles = '.selector { prop: value123; }'; - - const ast = makeAst(styles); - expect(ast.rules.length).toEqual(1); - - const rule = ast.rules[0]; - const selector = rule.selectors[0]; - expect(selector.strValue).toEqual('.selector'); - - const block: CssBlockAst = rule.block; - expect(block.entries.length).toEqual(1); - - const definition = block.entries[0]; - expect(definition.property.strValue).toEqual('prop'); - - const value = definition.value; - expect(value.tokens[0].strValue).toEqual('value123'); - }); - - it('should parse multiple CSS selectors sharing the same set of styles', () => { - const styles = ` - .class, #id, tag, [attr], key + value, * value, :-moz-any-link { - prop: value123; - } - `; - - const ast = makeAst(styles); - expect(ast.rules.length).toEqual(1); - - const rule = ast.rules[0]; - expect(rule.selectors.length).toBe(7); - - const classRule = rule.selectors[0]; - const idRule = rule.selectors[1]; - const tagRule = rule.selectors[2]; - const attrRule = rule.selectors[3]; - const plusOpRule = rule.selectors[4]; - const starOpRule = rule.selectors[5]; - const mozRule = rule.selectors[6]; - - assertTokens(classRule.selectorParts[0].tokens, ['.', 'class']); - assertTokens(idRule.selectorParts[0].tokens, ['.', 'class']); - assertTokens(attrRule.selectorParts[0].tokens, ['[', 'attr', ']']); - - assertTokens(plusOpRule.selectorParts[0].tokens, ['key']); - expect(plusOpRule.selectorParts[0].operator.strValue).toEqual('+'); - assertTokens(plusOpRule.selectorParts[1].tokens, ['value']); - - assertTokens(starOpRule.selectorParts[0].tokens, ['*']); - assertTokens(starOpRule.selectorParts[1].tokens, ['value']); - - assertTokens(mozRule.selectorParts[0].pseudoSelectors[0].tokens, [':', '-moz-any-link']); - - const style1 = rule.block.entries[0]; - expect(style1.property.strValue).toEqual('prop'); - assertTokens(style1.value.tokens, ['value123']); - }); - - it('should parse keyframe rules', () => { - const styles = ` - @keyframes rotateMe { - from { - transform: rotate(-360deg); - } - 50% { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } - } - `; - - const ast = makeAst(styles); - expect(ast.rules.length).toEqual(1); - - const rule = ast.rules[0]; - expect(rule.name!.strValue).toEqual('rotateMe'); - - const block = rule.block; - const fromRule = block.entries[0]; - - expect(fromRule.name!.strValue).toEqual('from'); - const fromStyle = (fromRule.block).entries[0]; - expect(fromStyle.property.strValue).toEqual('transform'); - assertTokens(fromStyle.value.tokens, ['rotate', '(', '-360', 'deg', ')']); - - const midRule = block.entries[1]; - - expect(midRule.name!.strValue).toEqual('50%'); - const midStyle = (midRule.block).entries[0]; - expect(midStyle.property.strValue).toEqual('transform'); - assertTokens(midStyle.value.tokens, ['rotate', '(', '0', 'deg', ')']); - - const toRule = block.entries[2]; - - expect(toRule.name!.strValue).toEqual('to'); - const toStyle = (toRule.block).entries[0]; - expect(toStyle.property.strValue).toEqual('transform'); - assertTokens(toStyle.value.tokens, ['rotate', '(', '360', 'deg', ')']); - }); - - it('should parse media queries into a stylesheet Ast', () => { - const styles = ` - @media all and (max-width:100px) { - .selector { - prop: value123; - } - } - `; - - const ast = makeAst(styles); - expect(ast.rules.length).toEqual(1); - - const rule = ast.rules[0]; - assertTokens(rule.query.tokens, ['all', 'and', '(', 'max-width', ':', '100', 'px', ')']); - - const block = rule.block; - expect(block.entries.length).toEqual(1); - - const rule2 = block.entries[0]; - expect(rule2.selectors[0].strValue).toEqual('.selector'); - - const block2 = rule2.block; - expect(block2.entries.length).toEqual(1); - }); - - it('should parse inline CSS values', () => { - const styles = ` - @import url('remote.css'); - @charset "UTF-8"; - @namespace ng url(http://angular.io/namespace/ng); - `; - - const ast = makeAst(styles); - - const importRule = ast.rules[0]; - expect(importRule.type).toEqual(BlockType.Import); - assertTokens(importRule.value.tokens, ['url', '(', 'remote', '.', 'css', ')']); - - const charsetRule = ast.rules[1]; - expect(charsetRule.type).toEqual(BlockType.Charset); - assertTokens(charsetRule.value.tokens, ['UTF-8']); - - const namespaceRule = ast.rules[2]; - expect(namespaceRule.type).toEqual(BlockType.Namespace); - assertTokens( - namespaceRule.value.tokens, ['ng', 'url', '(', 'http://angular.io/namespace/ng', ')']); - }); - - it('should parse CSS values that contain functions and leave the inner function data untokenized', - () => { - const styles = ` - .class { - background: url(matias.css); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - height: calc(100% - 50px); - background-image: linear-gradient( 45deg, rgba(100, 0, 0, 0.5), black ); - } - `; - - const ast = makeAst(styles); - expect(ast.rules.length).toEqual(1); - - const defs = (ast.rules[0]).block.entries; - expect(defs.length).toEqual(4); - - assertTokens((defs[0]).value.tokens, ['url', '(', 'matias.css', ')']); - assertTokens( - (defs[1]).value.tokens, - ['cubic-bezier', '(', '0.755, 0.050, 0.855, 0.060', ')']); - assertTokens((defs[2]).value.tokens, ['calc', '(', '100% - 50px', ')']); - assertTokens( - (defs[3]).value.tokens, - ['linear-gradient', '(', '45deg, rgba(100, 0, 0, 0.5), black', ')']); - }); - - it('should parse un-named block-level CSS values', () => { - const styles = ` - @font-face { - font-family: "Matias"; - font-weight: bold; - src: url(font-face.ttf); - } - @viewport { - max-width: 100px; - min-height: 1000px; - } - `; - - const ast = makeAst(styles); - - const fontFaceRule = ast.rules[0]; - expect(fontFaceRule.type).toEqual(BlockType.FontFace); - expect(fontFaceRule.block.entries.length).toEqual(3); - - const viewportRule = ast.rules[1]; - expect(viewportRule.type).toEqual(BlockType.Viewport); - expect(viewportRule.block.entries.length).toEqual(2); - }); - - it('should parse multiple levels of semicolons', () => { - const styles = ` - ;;; - @import url('something something') - ;;;;;;;; - ;;;;;;;; - ;@font-face { - ;src : url(font-face.ttf);;;;;;;; - ;;;-webkit-animation:my-animation - };;; - @media all and (max-width:100px) - {; - .selector {prop: value123;}; - ;.selector2{prop:1}} - `; - - const ast = makeAst(styles); - - const importRule = ast.rules[0]; - expect(importRule.type).toEqual(BlockType.Import); - assertTokens(importRule.value.tokens, ['url', '(', 'something something', ')']); - - const fontFaceRule = ast.rules[1]; - expect(fontFaceRule.type).toEqual(BlockType.FontFace); - expect(fontFaceRule.block.entries.length).toEqual(2); - - const mediaQueryRule = ast.rules[2]; - assertTokens( - mediaQueryRule.query.tokens, ['all', 'and', '(', 'max-width', ':', '100', 'px', ')']); - expect(mediaQueryRule.block.entries.length).toEqual(2); - }); - - it('should throw an error if an unknown @value block rule is parsed', () => { - const styles = ` - @matias { hello: there; } - `; - - expect(() => { - makeAst(styles); - }).toThrowError(/^CSS Parse Error: The CSS "at" rule "@matias" is not allowed to used here/g); - }); - - it('should parse empty rules', () => { - const styles = ` - .empty-rule { } - .somewhat-empty-rule { /* property: value; */ } - .non-empty-rule { property: value; } - `; - - const ast = makeAst(styles); - - const rules = ast.rules; - expect((rules[0]).block.entries.length).toEqual(0); - expect((rules[1]).block.entries.length).toEqual(0); - expect((rules[2]).block.entries.length).toEqual(1); - }); - - it('should parse the @document rule', () => { - const styles = ` - @document url(http://www.w3.org/), - url-prefix(http://www.w3.org/Style/), - domain(mozilla.org), - regexp("https:.*") - { - /* CSS rules here apply to: - - The page "http://www.w3.org/". - - Any page whose URL begins with "http://www.w3.org/Style/" - - Any page whose URL's host is "mozilla.org" or ends with - ".mozilla.org" - - Any page whose URL starts with "https:" */ - - /* make the above-mentioned pages really ugly */ - body { - color: purple; - background: yellow; - } - } - `; - - const ast = makeAst(styles); - - const rules = ast.rules; - const documentRule = rules[0]; - expect(documentRule.type).toEqual(BlockType.Document); - - const rule = documentRule.block.entries[0]; - expect(rule.strValue).toEqual('body'); - }); - - it('should parse the @page rule', () => { - const styles = ` - @page one { - .selector { prop: value; } - } - @page two { - .selector2 { prop: value2; } - } - `; - - const ast = makeAst(styles); - - const rules = ast.rules; - - const pageRule1 = rules[0]; - expect(pageRule1.query.strValue).toEqual('@page one'); - expect(pageRule1.query.tokens[0].strValue).toEqual('one'); - expect(pageRule1.type).toEqual(BlockType.Page); - - const pageRule2 = rules[1]; - expect(pageRule2.query.strValue).toEqual('@page two'); - expect(pageRule2.query.tokens[0].strValue).toEqual('two'); - expect(pageRule2.type).toEqual(BlockType.Page); - - const selectorOne = pageRule1.block.entries[0]; - expect(selectorOne.strValue).toEqual('.selector'); - - const selectorTwo = pageRule2.block.entries[0]; - expect(selectorTwo.strValue).toEqual('.selector2'); - }); - - it('should parse the @supports rule', () => { - const styles = ` - @supports (animation-name: "rotate") { - a:hover { animation: rotate 1s; } - } - `; - - const ast = makeAst(styles); - - const rules = ast.rules; - - const supportsRule = rules[0]; - assertTokens(supportsRule.query.tokens, ['(', 'animation-name', ':', 'rotate', ')']); - expect(supportsRule.type).toEqual(BlockType.Supports); - - const selectorOne = supportsRule.block.entries[0]; - expect(selectorOne.strValue).toEqual('a:hover'); - }); - - it('should collect multiple errors during parsing', () => { - const styles = ` - .class$value { something: something } - @custom { something: something } - #id { cool^: value } - `; - - const output = parse(styles); - expect(output.errors.length).toEqual(3); - }); - - it('should recover from selector errors and continue parsing', () => { - const styles = ` - tag& { key: value; } - .%tag { key: value; } - #tag$ { key: value; } - `; - - const output = parse(styles); - const errors = output.errors; - const ast = output.ast; - - expect(errors.length).toEqual(3); - - expect(ast.rules.length).toEqual(3); - - const rule1 = ast.rules[0]; - expect(rule1.selectors[0].strValue).toEqual('tag&'); - expect(rule1.block.entries.length).toEqual(1); - - const rule2 = ast.rules[1]; - expect(rule2.selectors[0].strValue).toEqual('.%tag'); - expect(rule2.block.entries.length).toEqual(1); - - const rule3 = ast.rules[2]; - expect(rule3.selectors[0].strValue).toEqual('#tag$'); - expect(rule3.block.entries.length).toEqual(1); - }); - - it('should throw an error when parsing invalid CSS Selectors', () => { - const styles = '.class[[prop%=value}] { style: val; }'; - const output = parse(styles); - const errors = output.errors; - - expect(errors.length).toEqual(3); - - expect(errors[0].msg).toMatch(/Unexpected character \[\[\] at column 0:7/g); - - expect(errors[1].msg).toMatch(/Unexpected character \[%\] at column 0:12/g); - - expect(errors[2].msg).toMatch(/Unexpected character \[}\] at column 0:19/g); - }); - - it('should throw an error if an attribute selector is not closed properly', () => { - const styles = '.class[prop=value { style: val; }'; - const output = parse(styles); - const errors = output.errors; - - expect(errors[0].msg).toMatch(/Unbalanced CSS attribute selector at column 0:12/g); - }); - - it('should throw an error if a pseudo function selector is not closed properly', () => { - const styles = 'body:lang(en { key:value; }'; - const output = parse(styles); - const errors = output.errors; - - expect(errors[0].msg) - .toMatch(/Character does not match expected Character value \("{" should match "\)"\)/); - }); - - it('should raise an error when a semi colon is missing from a CSS style/pair that isn\'t the last entry', - () => { - const styles = `.class { - color: red - background: blue - }`; - - const output = parse(styles); - const errors = output.errors; - - expect(errors.length).toEqual(1); - - expect(errors[0].msg) - .toMatch(/The CSS key\/value definition did not end with a semicolon at column 1:15/g); - }); - - it('should parse the inner value of a :not() pseudo-selector as a CSS selector', () => { - const styles = `div:not(.ignore-this-div) { - prop: value; - }`; - - const output = parse(styles); - const errors = output.errors; - const ast = output.ast; - - expect(errors.length).toEqual(0); - - const rule1 = ast.rules[0]; - expect(rule1.selectors.length).toEqual(1); - - const simpleSelector = rule1.selectors[0].selectorParts[0]; - assertTokens(simpleSelector.tokens, ['div']); - - const pseudoSelector = simpleSelector.pseudoSelectors[0]; - expect(pseudoSelector.name).toEqual('not'); - assertTokens(pseudoSelector.tokens, ['.', 'ignore-this-div']); - }); - - it('should parse the inner selectors of a :host-context selector', () => { - const styles = `body > :host-context(.a, .b, .c:hover) { - prop: value; - }`; - - const output = parse(styles); - const errors = output.errors; - const ast = output.ast; - - expect(errors.length).toEqual(0); - - const rule1 = ast.rules[0]; - expect(rule1.selectors.length).toEqual(1); - - const simpleSelector = rule1.selectors[0].selectorParts[1]; - const innerSelectors = simpleSelector.pseudoSelectors[0].innerSelectors; - - assertTokens(innerSelectors[0].selectorParts[0].tokens, ['.', 'a']); - assertTokens(innerSelectors[1].selectorParts[0].tokens, ['.', 'b']); - - const finalSelector = innerSelectors[2].selectorParts[0]; - assertTokens(finalSelector.tokens, ['.', 'c', ':', 'hover']); - assertTokens(finalSelector.pseudoSelectors[0].tokens, [':', 'hover']); - }); - - it('should raise parse errors when CSS key/value pairs are invalid', () => { - const styles = `.class { - background color: value; - color: value - font-size; - font-weight - }`; - - const output = parse(styles); - const errors = output.errors; - - expect(errors.length).toEqual(4); - - expect(errors[0].msg) - .toMatch( - /Identifier does not match expected Character value \("color" should match ":"\) at column 1:19/g); - - expect(errors[1].msg) - .toMatch(/The CSS key\/value definition did not end with a semicolon at column 2:15/g); - - expect(errors[2].msg) - .toMatch(/The CSS property was not paired with a style value at column 3:8/g); - - expect(errors[3].msg) - .toMatch(/The CSS property was not paired with a style value at column 4:8/g); - }); - - it('should recover from CSS key/value parse errors', () => { - const styles = ` - .problem-class { background color: red; color: white; } - .good-boy-class { background-color: red; color: white; } - `; - - const output = parse(styles); - const ast = output.ast; - - expect(ast.rules.length).toEqual(2); - - const rule1 = ast.rules[0]; - expect(rule1.block.entries.length).toEqual(2); - - const style1 = rule1.block.entries[0]; - expect(style1.property.strValue).toEqual('background color'); - assertTokens(style1.value.tokens, ['red']); - - const style2 = rule1.block.entries[1]; - expect(style2.property.strValue).toEqual('color'); - assertTokens(style2.value.tokens, ['white']); - }); - - describe('location offsets', () => { - let styles: string; - - function assertMatchesOffsetAndChar( - location: ParseLocation, expectedOffset: number, expectedChar: string): void { - expect(location.offset).toEqual(expectedOffset); - expect(styles[expectedOffset]).toEqual(expectedChar); - } - - it('should collect the source span location of each AST node with regular selectors', () => { - styles = '.problem-class { border-top-right: 1px; color: white; }\n'; - styles += '#good-boy-rule_ { background-color: #fe4; color: teal; }'; - - const output = parse(styles); - const ast = output.ast; - assertMatchesOffsetAndChar(ast.location.start, 0, '.'); - assertMatchesOffsetAndChar(ast.location.end, 111, '}'); - - const rule1 = ast.rules[0]; - assertMatchesOffsetAndChar(rule1.location.start, 0, '.'); - assertMatchesOffsetAndChar(rule1.location.end, 54, '}'); - - const rule2 = ast.rules[1]; - assertMatchesOffsetAndChar(rule2.location.start, 56, '#'); - assertMatchesOffsetAndChar(rule2.location.end, 111, '}'); - - const selector1 = rule1.selectors[0]; - assertMatchesOffsetAndChar(selector1.location.start, 0, '.'); - assertMatchesOffsetAndChar(selector1.location.end, 1, 'p'); // problem-class - - const selector2 = rule2.selectors[0]; - assertMatchesOffsetAndChar(selector2.location.start, 56, '#'); - assertMatchesOffsetAndChar(selector2.location.end, 57, 'g'); // good-boy-rule_ - - const block1 = rule1.block; - assertMatchesOffsetAndChar(block1.location.start, 15, '{'); - assertMatchesOffsetAndChar(block1.location.end, 54, '}'); - - const block2 = rule2.block; - assertMatchesOffsetAndChar(block2.location.start, 72, '{'); - assertMatchesOffsetAndChar(block2.location.end, 111, '}'); - - const block1def1 = block1.entries[0]; - assertMatchesOffsetAndChar(block1def1.location.start, 17, 'b'); // border-top-right - assertMatchesOffsetAndChar(block1def1.location.end, 36, 'p'); // px - - const block1def2 = block1.entries[1]; - assertMatchesOffsetAndChar(block1def2.location.start, 40, 'c'); // color - assertMatchesOffsetAndChar(block1def2.location.end, 47, 'w'); // white - - const block2def1 = block2.entries[0]; - assertMatchesOffsetAndChar(block2def1.location.start, 74, 'b'); // background-color - assertMatchesOffsetAndChar(block2def1.location.end, 93, 'f'); // fe4 - - const block2def2 = block2.entries[1]; - assertMatchesOffsetAndChar(block2def2.location.start, 98, 'c'); // color - assertMatchesOffsetAndChar(block2def2.location.end, 105, 't'); // teal - - const block1value1 = block1def1.value; - assertMatchesOffsetAndChar(block1value1.location.start, 35, '1'); - assertMatchesOffsetAndChar(block1value1.location.end, 36, 'p'); - - const block1value2 = block1def2.value; - assertMatchesOffsetAndChar(block1value2.location.start, 47, 'w'); - assertMatchesOffsetAndChar(block1value2.location.end, 47, 'w'); - - const block2value1 = block2def1.value; - assertMatchesOffsetAndChar(block2value1.location.start, 92, '#'); - assertMatchesOffsetAndChar(block2value1.location.end, 93, 'f'); - - const block2value2 = block2def2.value; - assertMatchesOffsetAndChar(block2value2.location.start, 105, 't'); - assertMatchesOffsetAndChar(block2value2.location.end, 105, 't'); - }); - - it('should collect the source span location of each AST node with media query data', () => { - styles = '@media (all and max-width: 100px) { a { display:none; } }'; - - const output = parse(styles); - const ast = output.ast; - - const mediaQuery = ast.rules[0]; - assertMatchesOffsetAndChar(mediaQuery.location.start, 0, '@'); - assertMatchesOffsetAndChar(mediaQuery.location.end, 56, '}'); - - const predicate = mediaQuery.query; - assertMatchesOffsetAndChar(predicate.location.start, 0, '@'); - assertMatchesOffsetAndChar(predicate.location.end, 32, ')'); - - const rule = mediaQuery.block.entries[0]; - assertMatchesOffsetAndChar(rule.location.start, 36, 'a'); - assertMatchesOffsetAndChar(rule.location.end, 54, '}'); - }); - - it('should collect the source span location of each AST node with keyframe data', () => { - styles = '@keyframes rotateAndZoomOut { '; - styles += 'from { transform: rotate(0deg); } '; - styles += '100% { transform: rotate(360deg) scale(2); }'; - styles += '}'; - - const output = parse(styles); - const ast = output.ast; - - const keyframes = ast.rules[0]; - assertMatchesOffsetAndChar(keyframes.location.start, 0, '@'); - assertMatchesOffsetAndChar(keyframes.location.end, 108, '}'); - - const step1 = keyframes.block.entries[0]; - assertMatchesOffsetAndChar(step1.location.start, 30, 'f'); - assertMatchesOffsetAndChar(step1.location.end, 62, '}'); - - const step2 = keyframes.block.entries[1]; - assertMatchesOffsetAndChar(step2.location.start, 64, '1'); - assertMatchesOffsetAndChar(step2.location.end, 107, '}'); - }); - - it('should collect the source span location of each AST node with an inline rule', () => { - styles = '@import url(something.css)'; - - const output = parse(styles); - const ast = output.ast; - - const rule = ast.rules[0]; - assertMatchesOffsetAndChar(rule.location.start, 0, '@'); - assertMatchesOffsetAndChar(rule.location.end, 25, ')'); - - const value = rule.value; - assertMatchesOffsetAndChar(value.location.start, 8, 'u'); - assertMatchesOffsetAndChar(value.location.end, 25, ')'); - }); - - it('should property collect the start/end locations with an invalid stylesheet', () => { - styles = '#id { something: value'; - - const output = parse(styles); - const ast = output.ast; - - assertMatchesOffsetAndChar(ast.location.start, 0, '#'); - assertMatchesOffsetAndChar(ast.location.end, 22, undefined!); - }); - }); - - it('should parse minified CSS content properly', () => { - // this code was taken from the angular.io webpage's CSS code - const styles = ` -.is-hidden{display:none!important} -.is-visible{display:block!important} -.is-visually-hidden{height:1px;width:1px;overflow:hidden;opacity:0.01;position:absolute;bottom:0;right:0;z-index:1} -.grid-fluid,.grid-fixed{margin:0 auto} -.grid-fluid .c1,.grid-fixed .c1,.grid-fluid .c2,.grid-fixed .c2,.grid-fluid .c3,.grid-fixed .c3,.grid-fluid .c4,.grid-fixed .c4,.grid-fluid .c5,.grid-fixed .c5,.grid-fluid .c6,.grid-fixed .c6,.grid-fluid .c7,.grid-fixed .c7,.grid-fluid .c8,.grid-fixed .c8,.grid-fluid .c9,.grid-fixed .c9,.grid-fluid .c10,.grid-fixed .c10,.grid-fluid .c11,.grid-fixed .c11,.grid-fluid .c12,.grid-fixed .c12{display:inline;float:left} -.grid-fluid .c1.grid-right,.grid-fixed .c1.grid-right,.grid-fluid .c2.grid-right,.grid-fixed .c2.grid-right,.grid-fluid .c3.grid-right,.grid-fixed .c3.grid-right,.grid-fluid .c4.grid-right,.grid-fixed .c4.grid-right,.grid-fluid .c5.grid-right,.grid-fixed .c5.grid-right,.grid-fluid .c6.grid-right,.grid-fixed .c6.grid-right,.grid-fluid .c7.grid-right,.grid-fixed .c7.grid-right,.grid-fluid .c8.grid-right,.grid-fixed .c8.grid-right,.grid-fluid .c9.grid-right,.grid-fixed .c9.grid-right,.grid-fluid .c10.grid-right,.grid-fixed .c10.grid-right,.grid-fluid .c11.grid-right,.grid-fixed .c11.grid-right,.grid-fluid .c12.grid-right,.grid-fixed .c12.grid-right{float:right} -.grid-fluid .c1.nb,.grid-fixed .c1.nb,.grid-fluid .c2.nb,.grid-fixed .c2.nb,.grid-fluid .c3.nb,.grid-fixed .c3.nb,.grid-fluid .c4.nb,.grid-fixed .c4.nb,.grid-fluid .c5.nb,.grid-fixed .c5.nb,.grid-fluid .c6.nb,.grid-fixed .c6.nb,.grid-fluid .c7.nb,.grid-fixed .c7.nb,.grid-fluid .c8.nb,.grid-fixed .c8.nb,.grid-fluid .c9.nb,.grid-fixed .c9.nb,.grid-fluid .c10.nb,.grid-fixed .c10.nb,.grid-fluid .c11.nb,.grid-fixed .c11.nb,.grid-fluid .c12.nb,.grid-fixed .c12.nb{margin-left:0} -.grid-fluid .c1.na,.grid-fixed .c1.na,.grid-fluid .c2.na,.grid-fixed .c2.na,.grid-fluid .c3.na,.grid-fixed .c3.na,.grid-fluid .c4.na,.grid-fixed .c4.na,.grid-fluid .c5.na,.grid-fixed .c5.na,.grid-fluid .c6.na,.grid-fixed .c6.na,.grid-fluid .c7.na,.grid-fixed .c7.na,.grid-fluid .c8.na,.grid-fixed .c8.na,.grid-fluid .c9.na,.grid-fixed .c9.na,.grid-fluid .c10.na,.grid-fixed .c10.na,.grid-fluid .c11.na,.grid-fixed .c11.na,.grid-fluid .c12.na,.grid-fixed .c12.na{margin-right:0} - `; - - const output = parse(styles); - const errors = output.errors; - expect(errors.length).toEqual(0); - - const ast = output.ast; - expect(ast.rules.length).toEqual(8); - }); - - it('should parse a snippet of keyframe code from animate.css properly', () => { - // this code was taken from the angular.io webpage's CSS code - const styles = ` -@charset "UTF-8"; - -/*! - * animate.css -http://daneden.me/animate - * Version - 3.5.1 - * Licensed under the MIT license - http://opensource.org/licenses/MIT - * - * Copyright (c) 2016 Daniel Eden - */ - -.animated { - -webkit-animation-duration: 1s; - animation-duration: 1s; - -webkit-animation-fill-mode: both; - animation-fill-mode: both; -} - -.animated.infinite { - -webkit-animation-iteration-count: infinite; - animation-iteration-count: infinite; -} - -.animated.hinge { - -webkit-animation-duration: 2s; - animation-duration: 2s; -} - -.animated.flipOutX, -.animated.flipOutY, -.animated.bounceIn, -.animated.bounceOut { - -webkit-animation-duration: .75s; - animation-duration: .75s; -} - -@-webkit-keyframes bounce { - from, 20%, 53%, 80%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - -webkit-transform: translate3d(0,0,0); - transform: translate3d(0,0,0); - } - - 40%, 43% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -30px, 0); - transform: translate3d(0, -30px, 0); - } - - 70% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -15px, 0); - transform: translate3d(0, -15px, 0); - } - - 90% { - -webkit-transform: translate3d(0,-4px,0); - transform: translate3d(0,-4px,0); - } -} - `; - - const output = parse(styles); - const errors = output.errors; - expect(errors.length).toEqual(0); - - const ast = output.ast; - expect(ast.rules.length).toEqual(6); - - const finalRule = ast.rules[ast.rules.length - 1]; - expect(finalRule.type).toEqual(BlockType.Keyframes); - expect(finalRule.block.entries.length).toEqual(4); - }); - }); -} diff --git a/packages/compiler/test/css_parser/css_visitor_spec.ts b/packages/compiler/test/css_parser/css_visitor_spec.ts deleted file mode 100644 index 9c6d485fd0..0000000000 --- a/packages/compiler/test/css_parser/css_visitor_spec.ts +++ /dev/null @@ -1,333 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - - -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, 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 { - expect(tokens.length).toEqual(valuesArr.length); - for (let i = 0; i < tokens.length; i++) { - expect(tokens[i].strValue == valuesArr[i]); - } -} - -class MyVisitor implements CssAstVisitor { - captures: {[key: string]: any[]} = {}; - - /** - * @internal - */ - _capture(method: string, ast: CssAst, context: any) { - this.captures[method] = this.captures[method] || []; - this.captures[method].push([ast, context]); - } - - constructor(ast: CssStyleSheetAst, context: any) { - ast.visit(this, context); - } - - visitCssValue(ast: CssStyleValueAst, context: any): void { - this._capture('visitCssValue', ast, context); - } - - visitCssInlineRule(ast: CssInlineRuleAst, context: any): void { - this._capture('visitCssInlineRule', ast, context); - } - - visitCssAtRulePredicate(ast: CssAtRulePredicateAst, context: any): void { - this._capture('visitCssAtRulePredicate', ast, context); - } - - visitCssKeyframeRule(ast: CssKeyframeRuleAst, context: any): void { - this._capture('visitCssKeyframeRule', ast, context); - ast.block.visit(this, context); - } - - visitCssKeyframeDefinition(ast: CssKeyframeDefinitionAst, context: any): void { - this._capture('visitCssKeyframeDefinition', ast, context); - ast.block.visit(this, context); - } - - visitCssMediaQueryRule(ast: CssMediaQueryRuleAst, context: any): void { - this._capture('visitCssMediaQueryRule', ast, context); - ast.query.visit(this, context); - ast.block.visit(this, context); - } - - visitCssSelectorRule(ast: CssSelectorRuleAst, context: any): void { - this._capture('visitCssSelectorRule', ast, 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); - }); - } - - visitCssSimpleSelector(ast: CssSimpleSelectorAst, context: any): void { - this._capture('visitCssSimpleSelector', ast, context); - ast.pseudoSelectors.forEach((pseudoAst: CssPseudoSelectorAst) => { - pseudoAst.visit(this, context); - }); - } - - visitCssDefinition(ast: CssDefinitionAst, context: any): void { - this._capture('visitCssDefinition', ast, context); - ast.value.visit(this, context); - } - - visitCssBlock(ast: CssBlockAst, context: any): void { - this._capture('visitCssBlock', ast, 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); - }); - } - - visitCssStyleSheet(ast: CssStyleSheetAst, context: any): void { - this._capture('visitCssStyleSheet', ast, context); - ast.rules.forEach((ruleAst: CssRuleAst) => { - ruleAst.visit(this, context); - }); - } - - visitCssUnknownRule(ast: CssUnknownRuleAst, context: any): void { - this._capture('visitCssUnknownRule', ast, context); - } - - visitCssUnknownTokenList(ast: CssUnknownTokenListAst, context: any): void { - this._capture('visitCssUnknownTokenList', ast, context); - } - - visitCssPseudoSelector(ast: CssPseudoSelectorAst, context: any): void { - this._capture('visitCssPseudoSelector', ast, context); - } -} - -function _getCaptureAst(capture: any[], index = 0): CssAst { - return capture[index][0]; -} - -(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; -} - -describe('CSS parsing and visiting', () => { - let ast: CssStyleSheetAst; - const context = {}; - - beforeEach(() => { - const cssCode = ` - .rule1 { prop1: value1 } - .rule2 { prop2: value2 } - - @media all (max-width: 100px) { - #id { prop3 :value3; } - } - - @import url(file.css); - - @keyframes rotate { - from { - prop4: value4; - } - 50%, 100% { - prop5: value5; - } - } - `; - ast = parse(cssCode); - }); - - it('should parse and visit a stylesheet', () => { - const visitor = new MyVisitor(ast, context); - const captures = visitor.captures['visitCssStyleSheet']; - - expect(captures.length).toEqual(1); - - 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']; - - expect(captures.length).toEqual(3); - - const rule1 = _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 rule2 = _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 rule3 = _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']); - }); - - 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); - - const def1 = _getCaptureAst(captures, 0); - expect(def1.property.strValue).toEqual('prop1'); - expect(def1.value.tokens[0].strValue).toEqual('value1'); - - const def2 = _getCaptureAst(captures, 1); - expect(def2.property.strValue).toEqual('prop2'); - expect(def2.value.tokens[0].strValue).toEqual('value2'); - - const def3 = _getCaptureAst(captures, 2); - expect(def3.property.strValue).toEqual('prop3'); - expect(def3.value.tokens[0].strValue).toEqual('value3'); - - const def4 = _getCaptureAst(captures, 3); - expect(def4.property.strValue).toEqual('prop4'); - expect(def4.value.tokens[0].strValue).toEqual('value4'); - - const def5 = _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']; - - expect(captures.length).toEqual(1); - - const query1 = _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']; - - expect(captures.length).toEqual(1); - - const predicate = _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']; - - expect(captures.length).toEqual(1); - - const inline1 = _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']; - - expect(captures.length).toEqual(1); - - const keyframe1 = _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']; - - expect(captures.length).toEqual(2); - - const def1 = _getCaptureAst(captures, 0); - _assertTokens(def1.steps, ['from']); - expect(def1.block.entries.length).toEqual(1); - - const def2 = _getCaptureAst(captures, 1); - _assertTokens(def2.steps, ['50%', '100%']); - expect(def2.block.entries.length).toEqual(1); - }); - - 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']; - - expect(captures.length).toEqual(1); - - const rule = _getCaptureAst(captures, 0); - expect(rule.ruleName).toEqual('@someUnknownRule'); - - _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']; - - expect(captures.length).toEqual(1); - - const rule = _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']; - - expect(captures.length).toEqual(1); - - const rule = _getCaptureAst(captures, 0); - _assertTokens(rule.tokens, ['six', 'seven', 'eight']); - }); -}); -})(); diff --git a/packages/compiler/test/expression_parser/parser_spec.ts b/packages/compiler/test/expression_parser/parser_spec.ts index 52db296380..5c4ab2c876 100644 --- a/packages/compiler/test/expression_parser/parser_spec.ts +++ b/packages/compiler/test/expression_parser/parser_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ASTWithSource, BindingPipe, Interpolation, ParserError, TemplateBinding, VariableBinding} from '@angular/compiler/src/expression_parser/ast'; +import {AbsoluteSourceSpan, ASTWithSource, BindingPipe, EmptyExpr, Interpolation, ParserError, TemplateBinding, VariableBinding} from '@angular/compiler/src/expression_parser/ast'; import {Lexer} from '@angular/compiler/src/expression_parser/lexer'; import {IvyParser, Parser, SplitInterpolation} from '@angular/compiler/src/expression_parser/parser'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -106,6 +106,7 @@ describe('parser', () => { checkAction('[]'); checkAction('[].length'); checkAction('[1, 2].length'); + checkAction('[1, 2,]', '[1, 2]'); }); it('should parse map', () => { @@ -313,6 +314,13 @@ describe('parser', () => { it('should report when encountering interpolation', () => { expectActionError('{{a()}}', 'Got interpolation ({{}}) where expression was expected'); }); + + it('should not report interpolation inside a string', () => { + expect(parseAction(`"{{a()}}"`).errors).toEqual([]); + expect(parseAction(`'{{a()}}'`).errors).toEqual([]); + expect(parseAction(`"{{a('\\"')}}"`).errors).toEqual([]); + expect(parseAction(`'{{a("\\'")}}'`).errors).toEqual([]); + }); }); describe('parse spans', () => { @@ -363,6 +371,39 @@ describe('parser', () => { expect(unparseWithSpan(ast)).toContain(['a.b = c', 'a.b = c']); expect(unparseWithSpan(ast)).toContain(['a.b = c', '[nameSpan] b']); }); + + it('should include parenthesis in spans', () => { + // When a LHS expression is parenthesized, the parenthesis on the left used to be + // excluded from the span. This test verifies that the parenthesis are properly included + // in the span for both LHS and RHS expressions. + // https://github.com/angular/angular/issues/40721 + expectSpan('(foo) && (bar)'); + expectSpan('(foo) || (bar)'); + expectSpan('(foo) == (bar)'); + expectSpan('(foo) === (bar)'); + expectSpan('(foo) != (bar)'); + expectSpan('(foo) !== (bar)'); + expectSpan('(foo) > (bar)'); + expectSpan('(foo) >= (bar)'); + expectSpan('(foo) < (bar)'); + expectSpan('(foo) <= (bar)'); + expectSpan('(foo) + (bar)'); + expectSpan('(foo) - (bar)'); + expectSpan('(foo) * (bar)'); + expectSpan('(foo) / (bar)'); + expectSpan('(foo) % (bar)'); + expectSpan('(foo) | pipe'); + expectSpan('(foo)()'); + expectSpan('(foo).bar'); + expectSpan('(foo)?.bar'); + expectSpan('(foo).bar = (baz)'); + expectSpan('(foo | pipe) == false'); + expectSpan('(((foo) && bar) || baz) === true'); + + function expectSpan(input: string) { + expect(unparseWithSpan(parseBinding(input))).toContain([jasmine.any(String), input]); + } + }); }); describe('general error handling', () => { @@ -439,6 +480,17 @@ describe('parser', () => { expectBindingError(input, err); }); } + + it('should parse an incomplete pipe with a source span that includes trailing whitespace', + () => { + const bindingText = 'foo | '; + const binding = parseBinding(bindingText).ast as BindingPipe; + + // The sourceSpan should include all characters of the input. + expect(rawSpan(binding.sourceSpan)).toEqual([0, bindingText.length]); + // The nameSpan should be positioned at the end of the input. + expect(rawSpan(binding.nameSpan)).toEqual([bindingText.length, bindingText.length]); + }); }); it('should only allow identifier or keyword as formatter names', () => { @@ -485,6 +537,13 @@ describe('parser', () => { expectBindingError('{{a.b}}', 'Got interpolation ({{}}) where expression was expected'); }); + it('should not report interpolation inside a string', () => { + expect(parseBinding(`"{{exp}}"`).errors).toEqual([]); + expect(parseBinding(`'{{exp}}'`).errors).toEqual([]); + expect(parseBinding(`'{{\\"}}'`).errors).toEqual([]); + expect(parseBinding(`'{{\\'}}'`).errors).toEqual([]); + }); + it('should parse conditional expression', () => { checkBinding('a < b ? a : b'); }); @@ -837,6 +896,37 @@ describe('parser', () => { expect(ast.expressions[0].name).toEqual('a'); }); + it('should parse interpolation inside quotes', () => { + const ast = parseInterpolation('"{{a}}"')!.ast as Interpolation; + expect(ast.strings).toEqual(['"', '"']); + expect(ast.expressions.length).toEqual(1); + expect(ast.expressions[0].name).toEqual('a'); + }); + + it('should parse interpolation with interpolation characters inside quotes', () => { + checkInterpolation('{{"{{a}}"}}', '{{ "{{a}}" }}'); + checkInterpolation('{{"{{"}}', '{{ "{{" }}'); + checkInterpolation('{{"}}"}}', '{{ "}}" }}'); + checkInterpolation('{{"{"}}', '{{ "{" }}'); + checkInterpolation('{{"}"}}', '{{ "}" }}'); + }); + + it('should parse interpolation with escaped quotes', () => { + checkInterpolation(`{{'It\\'s just Angular'}}`, `{{ "It's just Angular" }}`); + checkInterpolation(`{{'It\\'s {{ just Angular'}}`, `{{ "It's {{ just Angular" }}`); + checkInterpolation(`{{'It\\'s }} just Angular'}}`, `{{ "It's }} just Angular" }}`); + }); + + it('should parse interpolation with escaped backslashes', () => { + checkInterpolation(`{{foo.split('\\\\')}}`, `{{ foo.split("\\") }}`); + checkInterpolation(`{{foo.split('\\\\\\\\')}}`, `{{ foo.split("\\\\") }}`); + checkInterpolation(`{{foo.split('\\\\\\\\\\\\')}}`, `{{ foo.split("\\\\\\") }}`); + }); + + it('should not parse interpolation with mismatching quotes', () => { + expect(parseInterpolation(`{{ "{{a}}' }}`)).toBeNull(); + }); + it('should parse prefix/suffix with multiple interpolation', () => { const originalExp = 'before {{ a }} middle {{ b }} after'; const ast = parseInterpolation(originalExp)!.ast; @@ -853,6 +943,12 @@ describe('parser', () => { 'Parser Error: Blank expressions are not allowed in interpolated strings'); }); + it('should produce an empty expression ast for empty interpolations', () => { + const parsed = parseInterpolation('{{}}')!.ast as Interpolation; + expect(parsed.expressions.length).toBe(1); + expect(parsed.expressions[0]).toBeAnInstanceOf(EmptyExpr); + }); + it('should parse conditional expression', () => { checkInterpolation('{{ a < b ? a : b }}'); }); @@ -894,6 +990,10 @@ describe('parser', () => { it('should retain // in nested, unterminated strings', () => { checkInterpolation(`{{ "a\'b\`" //comment}}`, `{{ "a\'b\`" }}`); }); + + it('should ignore quotes inside a comment', () => { + checkInterpolation(`"{{name // " }}"`, `"{{ name }}"`); + }); }); }); @@ -916,6 +1016,13 @@ describe('parser', () => { 'Got interpolation ({{}}) where expression was expected'); }); + it('should not report interpolation inside a string', () => { + expect(parseSimpleBinding(`"{{exp}}"`).errors).toEqual([]); + expect(parseSimpleBinding(`'{{exp}}'`).errors).toEqual([]); + expect(parseSimpleBinding(`'{{\\"}}'`).errors).toEqual([]); + expect(parseSimpleBinding(`'{{\\'}}'`).errors).toEqual([]); + }); + it('should report when encountering field write', () => { expectError(validate(parseSimpleBinding('a = b')), 'Bindings cannot contain assignments'); }); @@ -1074,8 +1181,11 @@ function parseSimpleBindingIvy( } function checkInterpolation(exp: string, expected?: string) { - const ast = parseInterpolation(exp)!; + const ast = parseInterpolation(exp); if (expected == null) expected = exp; + if (ast === null) { + throw Error(`Failed to parse expression "${exp}"`); + } expect(unparse(ast)).toEqual(expected); validate(ast); } @@ -1120,3 +1230,7 @@ function checkActionWithError(text: string, expected: string, error: string) { checkAction(text, expected); expectActionError(text, error); } + +function rawSpan(span: AbsoluteSourceSpan): [number, number] { + return [span.start, span.end]; +} diff --git a/packages/compiler/test/ml_parser/html_parser_spec.ts b/packages/compiler/test/ml_parser/html_parser_spec.ts index 9f1932d224..48e87d07cb 100644 --- a/packages/compiler/test/ml_parser/html_parser_spec.ts +++ b/packages/compiler/test/ml_parser/html_parser_spec.ts @@ -709,6 +709,23 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} ]); }); + it('should set the end source span excluding trailing whitespace whitespace', () => { + expect(humanizeDomSourceSpans( + parser.parse('\n\n\n \n', 'TestComp', { + leadingTriviaChars: [' ', '\n', '\r', '\t'], + }))) + .toEqual([ + [ + html.Element, 'input', 0, '', '', + '' + ], + [html.Attribute, 'type', 'text', 'type="text"'], + [html.Text, '\n\n\n ', 0, ''], + [html.Element, 'span', 0, '\n', '', ''], + [html.Text, '\n', 1, ''], + ]); + }); + it('should not set the end source span for elements that are implicitly closed', () => { expect(humanizeDomSourceSpans(parser.parse('

    ', 'TestComp'))).toEqual([ [html.Element, 'div', 0, '

    ', '
    ', '
    '], diff --git a/packages/compiler/test/ml_parser/lexer_spec.ts b/packages/compiler/test/ml_parser/lexer_spec.ts index 886e1e30a4..7d451b52ea 100644 --- a/packages/compiler/test/ml_parser/lexer_spec.ts +++ b/packages/compiler/test/ml_parser/lexer_spec.ts @@ -234,6 +234,13 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u }); describe('tags', () => { + it('terminated with EOF', () => { + expect(tokenizeAndHumanizeSourceSpans(' { expect(tokenizeAndHumanizeSourceSpans('')).toEqual([ [lex.TokenType.INCOMPLETE_TAG_OPEN, ' { + it('should parse an SVG tag', () => { + expect(tokenizeAndHumanizeParts(`<svg:title>test</svg:title>`)).toEqual([ + [lex.TokenType.TAG_OPEN_START, 'svg', 'title'], + [lex.TokenType.TAG_OPEN_END], + [lex.TokenType.TEXT, 'test'], + [lex.TokenType.TAG_CLOSE, 'svg', 'title'], + [lex.TokenType.EOF], + ]); + }); + + it('should parse an SVG <title> tag with children', () => { + expect(tokenizeAndHumanizeParts(`<svg:title><f>test</f></svg:title>`)).toEqual([ + [lex.TokenType.TAG_OPEN_START, 'svg', 'title'], + [lex.TokenType.TAG_OPEN_END], + [lex.TokenType.TAG_OPEN_START, '', 'f'], + [lex.TokenType.TAG_OPEN_END], + [lex.TokenType.TEXT, 'test'], + [lex.TokenType.TAG_CLOSE, '', 'f'], + [lex.TokenType.TAG_CLOSE, 'svg', 'title'], + [lex.TokenType.EOF], + ]); + }); + }); + describe('expansion forms', () => { it('should parse an expansion form', () => { expect( diff --git a/packages/compiler/test/render3/r3_ast_spans_spec.ts b/packages/compiler/test/render3/r3_ast_spans_spec.ts index f073904a0e..978e7245d0 100644 --- a/packages/compiler/test/render3/r3_ast_spans_spec.ts +++ b/packages/compiler/test/render3/r3_ast_spans_spec.ts @@ -148,6 +148,13 @@ describe('R3 AST source spans', () => { ['TextAttribute', 'a', 'a', '<empty>'], ]); }); + + it('is correct for self-closing elements with trailing whitespace', () => { + expectFromHtml('<input />\n <span>\n</span>').toEqual([ + ['Element', '<input />', '<input />', '<input />'], + ['Element', '<span>\n</span>', '<span>', '</span>'], + ]); + }); }); describe('bound text nodes', () => { @@ -193,6 +200,30 @@ describe('R3 AST source spans', () => { ['BoundAttribute', 'data-prop="{{v}}"', 'prop', '{{v}}'], ]); }); + + it('is correct for bound properties via @', () => { + expectFromHtml('<div bind-@animation="v"></div>').toEqual([ + ['Element', '<div bind-@animation="v"></div>', '<div bind-@animation="v">', '</div>'], + ['BoundAttribute', 'bind-@animation="v"', 'animation', 'v'], + ]); + }); + + it('is correct for bound properties via animation-', () => { + expectFromHtml('<div bind-animate-animationName="v"></div>').toEqual([ + [ + 'Element', '<div bind-animate-animationName="v"></div>', + '<div bind-animate-animationName="v">', '</div>' + ], + ['BoundAttribute', 'bind-animate-animationName="v"', 'animationName', 'v'], + ]); + }); + + it('is correct for bound properties via @ without value', () => { + expectFromHtml('<div @animation></div>').toEqual([ + ['Element', '<div @animation></div>', '<div @animation>', '</div>'], + ['BoundAttribute', '@animation', 'animation', '<empty>'], + ]); + }); }); describe('templates', () => { @@ -401,6 +432,13 @@ describe('R3 AST source spans', () => { ['BoundEvent', 'data-bindon-prop="v"', 'prop', 'v'], ]); }); + + it('is correct for bound events via @', () => { + expectFromHtml('<div (@name.done)="v"></div>').toEqual([ + ['Element', '<div (@name.done)="v"></div>', '<div (@name.done)="v">', '</div>'], + ['BoundEvent', '(@name.done)="v"', 'name.done', 'v'], + ]); + }); }); describe('references', () => { diff --git a/packages/compiler/test/render3/r3_template_transform_spec.ts b/packages/compiler/test/render3/r3_template_transform_spec.ts index 8f74ce86eb..d27e7e51d4 100644 --- a/packages/compiler/test/render3/r3_template_transform_spec.ts +++ b/packages/compiler/test/render3/r3_template_transform_spec.ts @@ -92,8 +92,8 @@ class R3AstHumanizer implements t.Visitor<void> { } } -function expectFromHtml(html: string) { - const res = parse(html); +function expectFromHtml(html: string, ignoreError = false) { + const res = parse(html, {ignoreError}); return expectFromR3Nodes(res.nodes); } @@ -116,6 +116,19 @@ describe('R3 template transform', () => { }); describe('Nodes without binding', () => { + it('should parse incomplete tags terminated by EOF', () => { + expectFromHtml('<a', true /* ignoreError */).toEqual([ + ['Element', 'a'], + ]); + }); + + it('should parse incomplete tags terminated by another tag', () => { + expectFromHtml('<a <span></span>', true /* ignoreError */).toEqual([ + ['Element', 'a'], + ['Element', 'span'], + ]); + }); + it('should parse text nodes', () => { expectFromHtml('a').toEqual([ ['Text', 'a'], @@ -273,6 +286,11 @@ describe('R3 template transform', () => { ]); }); + it('should report an error if a reference is used multiple times on the same template', () => { + expect(() => parse('<ng-template #a #a></ng-template>')) + .toThrowError(/Reference "#a" is defined more than once/); + }); + it('should parse variables via let-...', () => { expectFromHtml('<ng-template let-a="b"></ng-template>').toEqual([ ['Template'], @@ -399,6 +417,27 @@ describe('R3 template transform', () => { expect(() => parse('<div (event)="">')).toThrowError(/Empty expressions are not allowed/); expect(() => parse('<div (event)=" ">')).toThrowError(/Empty expressions are not allowed/); }); + + it('should parse bound animation events when event name is empty', () => { + expectFromHtml('<div (@)="onAnimationEvent($event)"></div>', true).toEqual([ + ['Element', 'div'], + ['BoundEvent', '', null, 'onAnimationEvent($event)'], + ]); + expect(() => parse('<div (@)></div>')) + .toThrowError(/Animation event name is missing in binding/); + }); + + it('should report invalid phase value of animation event', () => { + expect(() => parse('<div (@event.invalidPhase)></div>')) + .toThrowError( + /The provided animation output phase value "invalidphase" for "@event" is not supported \(use start or done\)/); + expect(() => parse('<div (@event.)></div>')) + .toThrowError( + /The animation trigger output event \(@event\) is missing its phase value name \(start or done are currently supported\)/); + expect(() => parse('<div (@event)></div>')) + .toThrowError( + /The animation trigger output event \(@event\) is missing its phase value name \(start or done are currently supported\)/); + }); }); describe('variables', () => { @@ -442,6 +481,11 @@ describe('R3 template transform', () => { it('should report missing reference names', () => { expect(() => parse('<div #></div>')).toThrowError(/Reference does not have a name/); }); + + it('should report an error if a reference is used multiple times on the same element', () => { + expect(() => parse('<div #a #a></div>')) + .toThrowError(/Reference "#a" is defined more than once/); + }); }); describe('literal attribute', () => { diff --git a/packages/compiler/test/render3/view/binding_spec.ts b/packages/compiler/test/render3/view/binding_spec.ts index 5d792c338e..fe2f98863b 100644 --- a/packages/compiler/test/render3/view/binding_spec.ts +++ b/packages/compiler/test/render3/view/binding_spec.ts @@ -38,6 +38,7 @@ function makeSelectorMatcher(): SelectorMatcher<DirectiveMeta> { inputs: new IdentityInputMapping(['ngForOf']), outputs: new IdentityInputMapping([]), isComponent: false, + isStructural: true, selector: '[ngFor][ngForOf]', }); matcher.addSelectables(CssSelector.parse('[dir]'), { @@ -46,6 +47,7 @@ function makeSelectorMatcher(): SelectorMatcher<DirectiveMeta> { inputs: new IdentityInputMapping([]), outputs: new IdentityInputMapping([]), isComponent: false, + isStructural: false, selector: '[dir]' }); matcher.addSelectables(CssSelector.parse('[hasOutput]'), { @@ -54,6 +56,7 @@ function makeSelectorMatcher(): SelectorMatcher<DirectiveMeta> { inputs: new IdentityInputMapping([]), outputs: new IdentityInputMapping(['outputBinding']), isComponent: false, + isStructural: false, selector: '[hasOutput]' }); matcher.addSelectables(CssSelector.parse('[hasInput]'), { @@ -62,6 +65,7 @@ function makeSelectorMatcher(): SelectorMatcher<DirectiveMeta> { inputs: new IdentityInputMapping(['inputBinding']), outputs: new IdentityInputMapping([]), isComponent: false, + isStructural: false, selector: '[hasInput]' }); return matcher; @@ -107,6 +111,7 @@ describe('t2 binding', () => { inputs: new IdentityInputMapping([]), outputs: new IdentityInputMapping([]), isComponent: false, + isStructural: false, selector: 'text[dir]' }); const binder = new R3TargetBinder(matcher); diff --git a/packages/compiler/test/render3/view/i18n_spec.ts b/packages/compiler/test/render3/view/i18n_spec.ts index b6fca34b77..170f48ca5d 100644 --- a/packages/compiler/test/render3/view/i18n_spec.ts +++ b/packages/compiler/test/render3/view/i18n_spec.ts @@ -523,7 +523,7 @@ describe('serializeI18nMessageForLocalize', () => { expect(humanizeSourceSpan(messageParts[3].sourceSpan)).toEqual('"" (29-29)'); expect(placeHolders[0].text).toEqual('START_BOLD_TEXT'); - expect(humanizeSourceSpan(placeHolders[0].sourceSpan)).toEqual('"<b> " (10-16)'); + expect(humanizeSourceSpan(placeHolders[0].sourceSpan)).toEqual('"<b>" (10-13)'); expect(placeHolders[1].text).toEqual('INTERPOLATION'); expect(humanizeSourceSpan(placeHolders[1].sourceSpan)).toEqual('"{{value}}" (16-25)'); expect(placeHolders[2].text).toEqual('CLOSE_BOLD_TEXT'); diff --git a/packages/compiler/test/render3/view/util.ts b/packages/compiler/test/render3/view/util.ts index e974e0e155..cf99f7f34c 100644 --- a/packages/compiler/test/render3/view/util.ts +++ b/packages/compiler/test/render3/view/util.ts @@ -16,6 +16,7 @@ import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../../src/ml import * as a from '../../../src/render3/r3_ast'; import {htmlAstToRender3Ast, Render3ParseResult} from '../../../src/render3/r3_template_transform'; import {I18nMetaVisitor} from '../../../src/render3/view/i18n/meta'; +import {LEADING_TRIVIA_CHARS} from '../../../src/render3/view/template'; import {BindingParser} from '../../../src/template_parser/binding_parser'; import {MockSchemaRegistry} from '../../../testing'; @@ -78,15 +79,18 @@ export function toStringExpression(expr: e.AST): string { // Parse an html string to IVY specific info export function parseR3( - input: string, options: {preserveWhitespaces?: boolean, leadingTriviaChars?: string[]} = {}): - Render3ParseResult { + input: string, + options: {preserveWhitespaces?: boolean, + leadingTriviaChars?: string[], + ignoreError?: boolean} = {}): Render3ParseResult { const htmlParser = new HtmlParser(); - const parseResult = htmlParser.parse( - input, 'path:://to/template', - {tokenizeExpansionForms: true, leadingTriviaChars: options.leadingTriviaChars}); + const parseResult = htmlParser.parse(input, 'path:://to/template', { + tokenizeExpansionForms: true, + leadingTriviaChars: options.leadingTriviaChars ?? LEADING_TRIVIA_CHARS, + }); - if (parseResult.errors.length > 0) { + if (parseResult.errors.length > 0 && !options.ignoreError) { const msg = parseResult.errors.map(e => e.toString()).join('\n'); throw new Error(msg); } @@ -103,9 +107,9 @@ export function parseR3( ['onEvent'], ['onEvent']); const bindingParser = new BindingParser(expressionParser, DEFAULT_INTERPOLATION_CONFIG, schemaRegistry, null, []); - const r3Result = htmlAstToRender3Ast(htmlNodes, bindingParser); + const r3Result = htmlAstToRender3Ast(htmlNodes, bindingParser, {collectCommentNodes: false}); - if (r3Result.errors.length > 0) { + if (r3Result.errors.length > 0 && !options.ignoreError) { const msg = r3Result.errors.map(e => e.toString()).join('\n'); throw new Error(msg); } diff --git a/packages/compiler/test/shadow_css_spec.ts b/packages/compiler/test/shadow_css_spec.ts index ea9c771972..32cd76dbab 100644 --- a/packages/compiler/test/shadow_css_spec.ts +++ b/packages/compiler/test/shadow_css_spec.ts @@ -6,11 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ -import {CssRule, processRules, ShadowCss} from '@angular/compiler/src/shadow_css'; +import {CssRule, processRules, repeatGroups, ShadowCss} from '@angular/compiler/src/shadow_css'; import {normalizeCSS} from '@angular/platform-browser/testing/src/browser_util'; { - describe('ShadowCss', function() { + describe('ShadowCss', () => { function s(css: string, contentAttr: string, hostAttr: string = '') { const shadowCss = new ShadowCss(); const shim = shadowCss.shimCssText(css, contentAttr, hostAttr); @@ -112,6 +112,15 @@ import {normalizeCSS} from '@angular/platform-browser/testing/src/browser_util'; expect(s('[is="one"] {}', 'contenta')).toEqual('[is="one"][contenta] {}'); }); + it('should handle escaped sequences in selectors', () => { + expect(s('one\\/two {}', 'contenta')).toEqual('one\\/two[contenta] {}'); + expect(s('one\\:two {}', 'contenta')).toEqual('one\\:two[contenta] {}'); + expect(s('one\\\\:two {}', 'contenta')).toEqual('one\\\\[contenta]:two {}'); + expect(s('.one\\:two {}', 'contenta')).toEqual('.one\\:two[contenta] {}'); + expect(s('.one\\:two .three\\:four {}', 'contenta')) + .toEqual('.one\\:two[contenta] .three\\:four[contenta] {}'); + }); + describe((':host'), () => { it('should handle no context', () => { expect(s(':host {}', 'contenta', 'a-host')).toEqual('[a-host] {}'); @@ -136,6 +145,10 @@ import {normalizeCSS} from '@angular/platform-browser/testing/src/browser_util'; .toEqual('ul[a-host] > .z[contenta], li[a-host] > .z[contenta] {}'); }); + it('should handle compound class selectors', () => { + expect(s(':host(.a.b) {}', 'contenta', 'a-host')).toEqual('.a.b[a-host] {}'); + }); + it('should handle multiple class selectors', () => { expect(s(':host(.x,.y) {}', 'contenta', 'a-host')).toEqual('.x[a-host], .y[a-host] {}'); expect(s(':host(.x,.y) > .z {}', 'contenta', 'a-host')) @@ -199,6 +212,91 @@ import {normalizeCSS} from '@angular/platform-browser/testing/src/browser_util'; expect(s(':host-context([a=b]) {}', 'contenta', 'a-host')) .toEqual('[a=b][a-host], [a="b"] [a-host] {}'); }); + + it('should handle multiple :host-context() selectors', () => { + expect(s(':host-context(.one):host-context(.two) {}', 'contenta', 'a-host')) + .toEqual( + '.one.two[a-host], ' + // `one` and `two` both on the host + '.one.two [a-host], ' + // `one` and `two` are both on the same ancestor + '.one .two[a-host], ' + // `one` is an ancestor and `two` is on the host + '.one .two [a-host], ' + // `one` and `two` are both ancestors (in that order) + '.two .one[a-host], ' + // `two` is an ancestor and `one` is on the host + '.two .one [a-host]' + // `two` and `one` are both ancestors (in that order) + ' {}'); + + expect(s(':host-context(.X):host-context(.Y):host-context(.Z) {}', 'contenta', 'a-host') + .replace(/ \{\}$/, '') + .split(/\,\s+/)) + .toEqual([ + '.X.Y.Z[a-host]', + '.X.Y.Z [a-host]', + '.X.Y .Z[a-host]', + '.X.Y .Z [a-host]', + '.X.Z .Y[a-host]', + '.X.Z .Y [a-host]', + '.X .Y.Z[a-host]', + '.X .Y.Z [a-host]', + '.X .Y .Z[a-host]', + '.X .Y .Z [a-host]', + '.X .Z .Y[a-host]', + '.X .Z .Y [a-host]', + '.Y.Z .X[a-host]', + '.Y.Z .X [a-host]', + '.Y .Z .X[a-host]', + '.Y .Z .X [a-host]', + '.Z .Y .X[a-host]', + '.Z .Y .X [a-host]', + ]); + }); + + // It is not clear what the behavior should be for a `:host-context` with no selectors. + // This test is checking that the result is backward compatible with previous behavior. + // Arguably it should actually be an error that should be reported. + it('should handle :host-context with no ancestor selectors', () => { + expect(s(':host-context .inner {}', 'contenta', 'a-host')) + .toEqual('[a-host] .inner[contenta] {}'); + expect(s(':host-context() .inner {}', 'contenta', 'a-host')) + .toEqual('[a-host] .inner[contenta] {}'); + }); + + // More than one selector such as this is not valid as part of the :host-context spec. + // This test is checking that the result is backward compatible with previous behavior. + // Arguably it should actually be an error that should be reported. + it('should handle selectors', () => { + expect(s(':host-context(.one,.two) .inner {}', 'contenta', 'a-host')) + .toEqual( + '.one[a-host] .inner[contenta], ' + + '.one [a-host] .inner[contenta], ' + + '.two[a-host] .inner[contenta], ' + + '.two [a-host] .inner[contenta] ' + + '{}'); + }); + }); + + describe((':host-context and :host combination selector'), () => { + it('should handle selectors on the same element', () => { + expect(s(':host-context(div):host(.x) > .y {}', 'contenta', 'a-host')) + .toEqual('div.x[a-host] > .y[contenta] {}'); + }); + + it('should handle selectors on different elements', () => { + expect(s(':host-context(div) :host(.x) > .y {}', 'contenta', 'a-host')) + .toEqual('div .x[a-host] > .y[contenta] {}'); + + expect(s(':host-context(div) > :host(.x) > .y {}', 'contenta', 'a-host')) + .toEqual('div > .x[a-host] > .y[contenta] {}'); + }); + + it('should parse multiple rules containing :host-context and :host', () => { + const input = ` + :host-context(outer1) :host(bar) {} + :host-context(outer2) :host(foo) {} + `; + expect(s(input, 'contenta', 'a-host')) + .toEqual( + 'outer1 bar[a-host] {} ' + + 'outer2 foo[a-host] {}'); + }); }); it('should support polyfill-next-selector', () => { @@ -408,4 +506,32 @@ import {normalizeCSS} from '@angular/platform-browser/testing/src/browser_util'; }); }); }); + + describe('repeatGroups()', () => { + it('should do nothing if `multiples` is 0', () => { + const groups = [['a1', 'b1', 'c1'], ['a2', 'b2', 'c2']]; + repeatGroups(groups, 0); + expect(groups).toEqual([['a1', 'b1', 'c1'], ['a2', 'b2', 'c2']]); + }); + + it('should do nothing if `multiples` is 1', () => { + const groups = [['a1', 'b1', 'c1'], ['a2', 'b2', 'c2']]; + repeatGroups(groups, 1); + expect(groups).toEqual([['a1', 'b1', 'c1'], ['a2', 'b2', 'c2']]); + }); + + it('should add clones of the original groups if `multiples` is greater than 1', () => { + const group1 = ['a1', 'b1', 'c1']; + const group2 = ['a2', 'b2', 'c2']; + const groups = [group1, group2]; + repeatGroups(groups, 3); + expect(groups).toEqual([group1, group2, group1, group2, group1, group2]); + expect(groups[0]).toBe(group1); + expect(groups[1]).toBe(group2); + expect(groups[2]).not.toBe(group1); + expect(groups[3]).not.toBe(group2); + expect(groups[4]).not.toBe(group1); + expect(groups[5]).not.toBe(group2); + }); + }); } diff --git a/packages/compiler/test/template_parser/template_parser_spec.ts b/packages/compiler/test/template_parser/template_parser_spec.ts index d43238c102..69a0c3e754 100644 --- a/packages/compiler/test/template_parser/template_parser_spec.ts +++ b/packages/compiler/test/template_parser/template_parser_spec.ts @@ -540,6 +540,54 @@ describe('TemplateParser', () => { expect(humanizeTplAst(parse('{{a}}', []))).toEqual([[BoundTextAst, '{{ a }}']]); }); + it('should parse bound text nodes inside quotes', () => { + expect(humanizeTplAst(parse('"{{a}}"', []))).toEqual([[BoundTextAst, '"{{ a }}"']]); + }); + + it('should parse bound text nodes with interpolations inside quotes', () => { + expect(humanizeTplAst(parse('{{ "{{a}}" }}', []))).toEqual([[BoundTextAst, '{{ "{{a}}" }}']]); + expect(humanizeTplAst(parse('{{"{{"}}', []))).toEqual([[BoundTextAst, '{{ "{{" }}']]); + expect(humanizeTplAst(parse('{{"}}"}}', []))).toEqual([[BoundTextAst, '{{ "}}" }}']]); + expect(humanizeTplAst(parse('{{"{"}}', []))).toEqual([[BoundTextAst, '{{ "{" }}']]); + expect(humanizeTplAst(parse('{{"}"}}', []))).toEqual([[BoundTextAst, '{{ "}" }}']]); + }); + + it('should parse bound text nodes with escaped quotes', () => { + expect(humanizeTplAst(parse(`{{'It\\'s just Angular'}}`, []))).toEqual([ + [BoundTextAst, `{{ "It's just Angular" }}`] + ]); + + expect(humanizeTplAst(parse(`{{'It\\'s {{ just Angular'}}`, []))).toEqual([ + [BoundTextAst, `{{ "It's {{ just Angular" }}`] + ]); + + expect(humanizeTplAst(parse(`{{'It\\'s }} just Angular'}}`, []))).toEqual([ + [BoundTextAst, `{{ "It's }} just Angular" }}`] + ]); + }); + + it('should not parse bound text nodes with mismatching quotes', () => { + expect(humanizeTplAst(parse(`{{ "{{a}}' }}`, []))).toEqual([[TextAst, `{{ "{{a}}' }}`]]); + }); + + it('should parse interpolation with escaped backslashes', () => { + expect(humanizeTplAst(parse(`{{foo.split('\\\\')}}`, []))).toEqual([ + [BoundTextAst, `{{ foo.split("\\") }}`] + ]); + expect(humanizeTplAst(parse(`{{foo.split('\\\\\\\\')}}`, []))).toEqual([ + [BoundTextAst, `{{ foo.split("\\\\") }}`] + ]); + expect(humanizeTplAst(parse(`{{foo.split('\\\\\\\\\\\\')}}`, []))).toEqual([ + [BoundTextAst, `{{ foo.split("\\\\\\") }}`] + ]); + }); + + it('should ignore quotes inside a comment', () => { + expect(humanizeTplAst(parse(`"{{name // " }}"`, []))).toEqual([ + [BoundTextAst, `"{{ name }}"`] + ]); + }); + it('should parse with custom interpolation config', inject([TemplateParser], (parser: TemplateParser) => { const component = CompileDirectiveMetadata.create({ diff --git a/packages/core/package.json b/packages/core/package.json index e01bce8c7d..f365d07681 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -5,11 +5,11 @@ "author": "angular", "license": "MIT", "dependencies": { - "tslib": "^2.0.0" + "tslib": "^2.1.0" }, "peerDependencies": { "rxjs": "^6.5.3", - "zone.js": "^0.10.2 || ^0.11.3" + "zone.js": "~0.11.4" }, "repository": { "type": "git", diff --git a/packages/core/schematics/BUILD.bazel b/packages/core/schematics/BUILD.bazel index b68ceb4eb9..cfd2d69125 100644 --- a/packages/core/schematics/BUILD.bazel +++ b/packages/core/schematics/BUILD.bazel @@ -11,6 +11,8 @@ pkg_npm( visibility = ["//packages/core:__pkg__"], deps = [ "//packages/core/schematics/migrations/abstract-control-parent", + "//packages/core/schematics/migrations/activated-route-snapshot-fragment", + "//packages/core/schematics/migrations/can-activate-with-redirect-to", "//packages/core/schematics/migrations/dynamic-queries", "//packages/core/schematics/migrations/initial-navigation", "//packages/core/schematics/migrations/missing-injectable", @@ -26,5 +28,6 @@ pkg_npm( "//packages/core/schematics/migrations/undecorated-classes-with-decorated-fields", "//packages/core/schematics/migrations/undecorated-classes-with-di", "//packages/core/schematics/migrations/wait-for-async", + "//packages/core/schematics/migrations/xhr-factory", ], ) diff --git a/packages/core/schematics/migrations.json b/packages/core/schematics/migrations.json index fb469fa76a..66bcf0ec96 100644 --- a/packages/core/schematics/migrations.json +++ b/packages/core/schematics/migrations.json @@ -79,6 +79,21 @@ "version": "11.0.0-beta", "description": "Updates the `initialNavigation` property for `RouterModule.forRoot`.", "factory": "./migrations/initial-navigation/index" + }, + "migration-v11.1-can-activate-with-redirect-to": { + "version": "11.1.0-beta", + "description": "Removes `canActivate` from a `Route` config when `redirectTo` is also present", + "factory": "./migrations/can-activate-with-redirect-to/index" + }, + "migration-v12-activated-route-snapshot-fragment": { + "version": "12.0.0-beta", + "description": "In Angular version 12, the type of ActivatedRouteSnapshot.fragment is nullable. This migration automatically adds non-null assertions to it.", + "factory": "./migrations/activated-route-snapshot-fragment/index" + }, + "migration-v12-xhr-factory": { + "version": "12.0.0-next.6", + "description": "`XhrFactory` has been moved from `@angular/common/http` to `@angular/common`.", + "factory": "./migrations/xhr-factory/index" } } } diff --git a/packages/core/schematics/migrations/abstract-control-parent/util.ts b/packages/core/schematics/migrations/abstract-control-parent/util.ts index f5e47c516f..37d5cbff8f 100644 --- a/packages/core/schematics/migrations/abstract-control-parent/util.ts +++ b/packages/core/schematics/migrations/abstract-control-parent/util.ts @@ -8,14 +8,11 @@ import {normalize} from 'path'; import * as ts from 'typescript'; +import {isNullCheck, isSafeAccess} from '../../utils/typescript/nodes'; +import {hasOneOfTypes, isNullableType} from '../../utils/typescript/symbol'; /** Names of symbols from `@angular/forms` whose `parent` accesses have to be migrated. */ -const abstractControlSymbols = new Set<string>([ - 'AbstractControl', - 'FormArray', - 'FormControl', - 'FormGroup', -]); +const abstractControlSymbols = ['AbstractControl', 'FormArray', 'FormControl', 'FormGroup']; /** * Finds the `PropertyAccessExpression`-s that are accessing the `parent` property in @@ -38,78 +35,6 @@ export function findParentAccesses( return results; } -/** Checks whether a node's type is nullable (`null`, `undefined` or `void`). */ -function isNullableType(typeChecker: ts.TypeChecker, node: ts.Node) { - // Skip expressions in the form of `foo.bar!.baz` since the `TypeChecker` seems - // to identify them as null, even though the user indicated that it won't be. - if (node.parent && ts.isNonNullExpression(node.parent)) { - return false; - } - - const type = typeChecker.getTypeAtLocation(node); - const typeNode = typeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.None); - let hasSeenNullableType = false; - - // Trace the type of the node back to a type node, walk - // through all of its sub-nodes and look for nullable tyes. - if (typeNode) { - (function walk(current: ts.Node) { - if (current.kind === ts.SyntaxKind.NullKeyword || - current.kind === ts.SyntaxKind.UndefinedKeyword || - current.kind === ts.SyntaxKind.VoidKeyword) { - hasSeenNullableType = true; - // Note that we don't descend into type literals, because it may cause - // us to mis-identify the root type as nullable, because it has a nullable - // property (e.g. `{ foo: string | null }`). - } else if (!hasSeenNullableType && !ts.isTypeLiteralNode(current)) { - current.forEachChild(walk); - } - })(typeNode); - } - - return hasSeenNullableType; -} - -/** - * Checks whether a particular node is part of a null check. E.g. given: - * `control.parent ? control.parent.value : null` the null check would be `control.parent`. - */ -function isNullCheck(node: ts.PropertyAccessExpression): boolean { - if (!node.parent) { - return false; - } - - // `control.parent && control.parent.value` where `node` is `control.parent`. - if (ts.isBinaryExpression(node.parent) && node.parent.left === node) { - return true; - } - - // `control.parent && control.parent.parent && control.parent.parent.value` - // where `node` is `control.parent`. - if (node.parent.parent && ts.isBinaryExpression(node.parent.parent) && - node.parent.parent.left === node.parent) { - return true; - } - - // `if (control.parent) {...}` where `node` is `control.parent`. - if (ts.isIfStatement(node.parent) && node.parent.expression === node) { - return true; - } - - // `control.parent ? control.parent.value : null` where `node` is `control.parent`. - if (ts.isConditionalExpression(node.parent) && node.parent.condition === node) { - return true; - } - - return false; -} - -/** Checks whether a property access is safe (e.g. `foo.parent?.value`). */ -function isSafeAccess(node: ts.PropertyAccessExpression): boolean { - return node.parent != null && ts.isPropertyAccessExpression(node.parent) && - node.parent.expression === node && node.parent.questionDotToken != null; -} - /** Checks whether a property access is on an `AbstractControl` coming from `@angular/forms`. */ function isAbstractControlReference( typeChecker: ts.TypeChecker, node: ts.PropertyAccessExpression): boolean { @@ -119,37 +44,14 @@ function isAbstractControlReference( // If such a node is found, we check whether the type is one of the `AbstractControl` symbols // and whether it comes from the `@angular/forms` directory in the `node_modules`. while (ts.isPropertyAccessExpression(current)) { - const type = typeChecker.getTypeAtLocation(current.expression); - const symbol = type.getSymbol(); - if (symbol && type) { + const symbol = typeChecker.getTypeAtLocation(current.expression)?.getSymbol(); + if (symbol) { const sourceFile = symbol.valueDeclaration?.getSourceFile(); return sourceFile != null && formsPattern.test(normalize(sourceFile.fileName).replace(/\\/g, '/')) && - hasAbstractControlType(typeChecker, type); + hasOneOfTypes(typeChecker, current.expression, abstractControlSymbols); } current = current.expression; } return false; } - -/** - * Walks through the sub-types of a type, looking for a type that - * has the same name as one of the `AbstractControl` types. - */ -function hasAbstractControlType(typeChecker: ts.TypeChecker, type: ts.Type): boolean { - const typeNode = typeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.None); - let hasMatch = false; - if (typeNode) { - (function walk(current: ts.Node) { - if (ts.isIdentifier(current) && abstractControlSymbols.has(current.text)) { - hasMatch = true; - // Note that we don't descend into type literals, because it may cause - // us to mis-identify the root type as nullable, because it has a nullable - // property (e.g. `{ foo: FormControl }`). - } else if (!hasMatch && !ts.isTypeLiteralNode(current)) { - current.forEachChild(walk); - } - })(typeNode); - } - return hasMatch; -} diff --git a/packages/core/schematics/migrations/google3/BUILD.bazel b/packages/core/schematics/migrations/google3/BUILD.bazel index 0eecceb175..bd21e587de 100644 --- a/packages/core/schematics/migrations/google3/BUILD.bazel +++ b/packages/core/schematics/migrations/google3/BUILD.bazel @@ -6,6 +6,8 @@ ts_library( tsconfig = "//packages/core/schematics:tsconfig.json", visibility = ["//packages/core/schematics/test/google3:__pkg__"], deps = [ + "//packages/core/schematics/migrations/activated-route-snapshot-fragment", + "//packages/core/schematics/migrations/can-activate-with-redirect-to", "//packages/core/schematics/migrations/dynamic-queries", "//packages/core/schematics/migrations/initial-navigation", "//packages/core/schematics/migrations/initial-navigation/google3", diff --git a/packages/core/schematics/migrations/renderer-to-renderer2/helpers.ts b/packages/core/schematics/migrations/renderer-to-renderer2/helpers.ts index dcf11af830..a706d4ce53 100644 --- a/packages/core/schematics/migrations/renderer-to-renderer2/helpers.ts +++ b/packages/core/schematics/migrations/renderer-to-renderer2/helpers.ts @@ -56,8 +56,6 @@ function getHelperDeclaration(name: HelperFunction): ts.Node { case HelperFunction.splitNamespace: return getSplitNamespaceHelper(); } - - throw new Error(`Unsupported helper called "${name}".`); } /** Creates a helper for a custom `any` type during the migration. */ diff --git a/packages/core/schematics/migrations/renderer-to-renderer2/index.ts b/packages/core/schematics/migrations/renderer-to-renderer2/index.ts index 9caa76a98c..e3bc12466d 100644 --- a/packages/core/schematics/migrations/renderer-to-renderer2/index.ts +++ b/packages/core/schematics/migrations/renderer-to-renderer2/index.ts @@ -7,7 +7,7 @@ */ import {Rule, SchematicsException, Tree} from '@angular-devkit/schematics'; -import {relative} from 'path'; +import {basename, join, relative} from 'path'; import * as ts from 'typescript'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; @@ -43,11 +43,15 @@ export default function(): Rule { } function runRendererToRenderer2Migration(tree: Tree, tsconfigPath: string, basePath: string) { + // Technically we can get away with using `MODULE_AUGMENTATION_FILENAME` as the path, but as of + // TS 4.2, the module resolution caching seems to be more aggressive which causes the file to be + // retained between test runs. We can avoid it by using the full path. + const augmentedFilePath = join(basePath, MODULE_AUGMENTATION_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. - if (fileName === MODULE_AUGMENTATION_FILENAME) { + if (basename(fileName) === MODULE_AUGMENTATION_FILENAME) { return ` import '@angular/core'; declare module "@angular/core" { @@ -55,8 +59,8 @@ function runRendererToRenderer2Migration(tree: Tree, tsconfigPath: string, baseP } `; } - return null; - }, [MODULE_AUGMENTATION_FILENAME]); + return undefined; + }, [augmentedFilePath]); const typeChecker = program.getTypeChecker(); const printer = ts.createPrinter(); const sourceFiles = diff --git a/packages/core/schematics/migrations/static-queries/index.ts b/packages/core/schematics/migrations/static-queries/index.ts index b0c684a78e..06f61ec974 100644 --- a/packages/core/schematics/migrations/static-queries/index.ts +++ b/packages/core/schematics/migrations/static-queries/index.ts @@ -9,7 +9,6 @@ import {logging} from '@angular-devkit/core'; import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics'; import {relative} from 'path'; -import {from} from 'rxjs'; import * as ts from 'typescript'; import {NgComponentTemplateVisitor} from '../../utils/ng_component_template'; @@ -41,11 +40,7 @@ interface AnalyzedProject { /** Entry point for the V8 static-query migration. */ export default function(): Rule { - return (tree: Tree, context: SchematicContext) => { - // We need to cast the returned "Observable" to "any" as there is a - // RxJS version mismatch that breaks the TS compilation. - return from(runMigration(tree, context).then(() => tree)) as any; - }; + return runMigration; } /** Runs the V8 migration static-query migration for all determined TypeScript projects. */ diff --git a/packages/core/schematics/migrations/wait-for-async/index.ts b/packages/core/schematics/migrations/wait-for-async/index.ts index 8c04faa58d..fb0804ef9c 100644 --- a/packages/core/schematics/migrations/wait-for-async/index.ts +++ b/packages/core/schematics/migrations/wait-for-async/index.ts @@ -7,7 +7,7 @@ */ import {Rule, SchematicsException, Tree} from '@angular-devkit/schematics'; -import {relative} from 'path'; +import {basename, join, relative} from 'path'; import * as ts from 'typescript'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; @@ -38,11 +38,15 @@ export default function(): Rule { } function runWaitForAsyncMigration(tree: Tree, tsconfigPath: string, basePath: string) { + // Technically we can get away with using `MODULE_AUGMENTATION_FILENAME` as the path, but as of + // TS 4.2, the module resolution caching seems to be more aggressive which causes the file to be + // retained between test runs. We can avoid it by using the full path. + const augmentedFilePath = join(basePath, MODULE_AUGMENTATION_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/testing" to include a named export called "async". This ensures that // we can rely on the type checker for this migration after `async` has been removed. - if (fileName === MODULE_AUGMENTATION_FILENAME) { + if (basename(fileName) === MODULE_AUGMENTATION_FILENAME) { return ` import '@angular/core/testing'; declare module "@angular/core/testing" { @@ -50,8 +54,8 @@ function runWaitForAsyncMigration(tree: Tree, tsconfigPath: string, basePath: st } `; } - return null; - }, [MODULE_AUGMENTATION_FILENAME]); + return undefined; + }, [augmentedFilePath]); const typeChecker = program.getTypeChecker(); const printer = ts.createPrinter(); const sourceFiles = diff --git a/packages/core/schematics/test/BUILD.bazel b/packages/core/schematics/test/BUILD.bazel index a26b22892f..135467711d 100644 --- a/packages/core/schematics/test/BUILD.bazel +++ b/packages/core/schematics/test/BUILD.bazel @@ -9,6 +9,8 @@ ts_library( ], deps = [ "//packages/core/schematics/migrations/abstract-control-parent", + "//packages/core/schematics/migrations/activated-route-snapshot-fragment", + "//packages/core/schematics/migrations/can-activate-with-redirect-to", "//packages/core/schematics/migrations/dynamic-queries", "//packages/core/schematics/migrations/initial-navigation", "//packages/core/schematics/migrations/missing-injectable", @@ -24,6 +26,7 @@ ts_library( "//packages/core/schematics/migrations/undecorated-classes-with-decorated-fields", "//packages/core/schematics/migrations/undecorated-classes-with-di", "//packages/core/schematics/migrations/wait-for-async", + "//packages/core/schematics/migrations/xhr-factory", "//packages/core/schematics/utils", "@npm//@angular-devkit/core", "@npm//@angular-devkit/schematics", 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 ed1e2d3495..af708d2adb 100644 --- a/packages/core/schematics/test/static_queries_migration_usage_spec.ts +++ b/packages/core/schematics/test/static_queries_migration_usage_spec.ts @@ -683,13 +683,13 @@ describe('static-queries migration with usage strategy', () => { it('should not mark queries used in promises as static', async () => { writeFile('/es2015.dom.d.ts', ` interface PromiseConstructor { - resolve(): Promise; - reject(): Promise; + resolve(): Promise<unknown>; + reject(): Promise<unknown>; } interface Promise { - then(cb: Function): Promise; - catch(cb: Function): Promise; + then(cb: Function): Promise<unknown>; + catch(cb: Function): Promise<unknown>; } declare var Promise: PromiseConstructor; 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 282962fedc..0b7242db13 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 @@ -1484,7 +1484,10 @@ describe('Undecorated classes with DI migration', () => { {flatModuleId: 'AUTOGENERATED', flatModuleOutFile: 'AUTOGENERATED'} })); - writeFile('/second.ts', ``); + // This file doesn't do anything, but it's necessary in order to hit the code path for + // the assertion. As of TS 4.2 it needs to have _some_ kind of content, otherwise the + // compiler will throw an error. + writeFile('/second.ts', `export const foo = 1;`); writeFile('/test.ts', ` import {Injectable, NgModule, NgZone} from '@angular/core'; diff --git a/packages/core/schematics/tsconfig.json b/packages/core/schematics/tsconfig.json index ba11c35a3d..53930ff133 100644 --- a/packages/core/schematics/tsconfig.json +++ b/packages/core/schematics/tsconfig.json @@ -3,7 +3,9 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "strict": true, - "lib": ["es2015"], + "moduleResolution": "node", + "target": "es2019", + "lib": ["es2019"], "types": ["jasmine"], "baseUrl": ".", "paths": { diff --git a/packages/core/schematics/utils/typescript/compiler_host.ts b/packages/core/schematics/utils/typescript/compiler_host.ts index e39bdbc8ec..a4c159d65a 100644 --- a/packages/core/schematics/utils/typescript/compiler_host.ts +++ b/packages/core/schematics/utils/typescript/compiler_host.ts @@ -10,7 +10,7 @@ import {dirname, relative, resolve} from 'path'; import * as ts from 'typescript'; import {parseTsconfigFile} from './parse_tsconfig'; -export type FakeReadFileFn = (fileName: string) => string|null; +export type FakeReadFileFn = (fileName: string) => string|undefined; /** * Creates a TypeScript program instance for a TypeScript project within @@ -40,6 +40,7 @@ export function createMigrationCompilerHost( tree: Tree, options: ts.CompilerOptions, basePath: string, fakeRead?: FakeReadFileFn): ts.CompilerHost { const host = ts.createCompilerHost(options, true); + const defaultReadFile = host.readFile; // We need to overwrite the host "readFile" method, as we want the TypeScript // program to be based on the file contents in the virtual file tree. Otherwise @@ -47,12 +48,19 @@ export function createMigrationCompilerHost( // source files. host.readFile = fileName => { const treeRelativePath = relative(basePath, fileName); - const fakeOutput = fakeRead ? fakeRead(treeRelativePath) : null; - const buffer = fakeOutput === null ? tree.read(treeRelativePath) : fakeOutput; + let result: string|undefined = fakeRead?.(treeRelativePath); + + if (result === undefined) { + // If the relative path resolved to somewhere outside of the tree, fall back to + // TypeScript's default file reading function since the `tree` will throw an error. + result = treeRelativePath.startsWith('..') ? defaultReadFile.call(host, fileName) : + tree.read(treeRelativePath)?.toString(); + } + // Strip BOM as otherwise TSC methods (Ex: getWidth) will return an offset, // which breaks the CLI UpdateRecorder. // See: https://github.com/angular/angular/pull/30719 - return buffer ? buffer.toString().replace(/^\uFEFF/, '') : undefined; + return result ? result.replace(/^\uFEFF/, '') : undefined; }; return host; diff --git a/packages/core/schematics/utils/typescript/imports.ts b/packages/core/schematics/utils/typescript/imports.ts index ccfe75aadd..bf33f0a258 100644 --- a/packages/core/schematics/utils/typescript/imports.ts +++ b/packages/core/schematics/utils/typescript/imports.ts @@ -110,7 +110,7 @@ export function replaceImport( /** Finds an import specifier with a particular name. */ -function findImportSpecifier( +export function findImportSpecifier( nodes: ts.NodeArray<ts.ImportSpecifier>, specifierName: string): ts.ImportSpecifier|undefined { return nodes.find(element => { const {name, propertyName} = element; diff --git a/packages/core/schematics/utils/typescript/nodes.ts b/packages/core/schematics/utils/typescript/nodes.ts index f6b5a3eb0a..f14f4ef46b 100644 --- a/packages/core/schematics/utils/typescript/nodes.ts +++ b/packages/core/schematics/utils/typescript/nodes.ts @@ -26,3 +26,43 @@ export function closestNode<T extends ts.Node>(node: ts.Node, kind: ts.SyntaxKin return null; } + +/** + * Checks whether a particular node is part of a null check. E.g. given: + * `foo.bar ? foo.bar.value : null` the null check would be `foo.bar`. + */ +export function isNullCheck(node: ts.Node): boolean { + if (!node.parent) { + return false; + } + + // `foo.bar && foo.bar.value` where `node` is `foo.bar`. + if (ts.isBinaryExpression(node.parent) && node.parent.left === node) { + return true; + } + + // `foo.bar && foo.bar.parent && foo.bar.parent.value` + // where `node` is `foo.bar`. + if (node.parent.parent && ts.isBinaryExpression(node.parent.parent) && + node.parent.parent.left === node.parent) { + return true; + } + + // `if (foo.bar) {...}` where `node` is `foo.bar`. + if (ts.isIfStatement(node.parent) && node.parent.expression === node) { + return true; + } + + // `foo.bar ? foo.bar.value : null` where `node` is `foo.bar`. + if (ts.isConditionalExpression(node.parent) && node.parent.condition === node) { + return true; + } + + return false; +} + +/** Checks whether a property access is safe (e.g. `foo.parent?.value`). */ +export function isSafeAccess(node: ts.Node): boolean { + return node.parent != null && ts.isPropertyAccessExpression(node.parent) && + node.parent.expression === node && node.parent.questionDotToken != null; +} diff --git a/packages/core/schematics/utils/typescript/symbol.ts b/packages/core/schematics/utils/typescript/symbol.ts index 9d4bbffd1f..a63cc3a989 100644 --- a/packages/core/schematics/utils/typescript/symbol.ts +++ b/packages/core/schematics/utils/typescript/symbol.ts @@ -27,3 +27,57 @@ export function isReferenceToImport( return !!(nodeSymbol && importSymbol) && nodeSymbol.valueDeclaration === importSymbol.valueDeclaration; } + +/** Checks whether a node's type is nullable (`null`, `undefined` or `void`). */ +export function isNullableType(typeChecker: ts.TypeChecker, node: ts.Node) { + // Skip expressions in the form of `foo.bar!.baz` since the `TypeChecker` seems + // to identify them as null, even though the user indicated that it won't be. + if (node.parent && ts.isNonNullExpression(node.parent)) { + return false; + } + + const type = typeChecker.getTypeAtLocation(node); + const typeNode = typeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.None); + let hasSeenNullableType = false; + + // Trace the type of the node back to a type node, walk + // through all of its sub-nodes and look for nullable tyes. + if (typeNode) { + (function walk(current: ts.Node) { + if (current.kind === ts.SyntaxKind.NullKeyword || + current.kind === ts.SyntaxKind.UndefinedKeyword || + current.kind === ts.SyntaxKind.VoidKeyword) { + hasSeenNullableType = true; + // Note that we don't descend into type literals, because it may cause + // us to mis-identify the root type as nullable, because it has a nullable + // property (e.g. `{ foo: string | null }`). + } else if (!hasSeenNullableType && !ts.isTypeLiteralNode(current)) { + current.forEachChild(walk); + } + })(typeNode); + } + + return hasSeenNullableType; +} + +/** + * Walks through the types and sub-types of a node, looking for a + * type that has the same name as one of the passed-in ones. + */ +export function hasOneOfTypes( + typeChecker: ts.TypeChecker, node: ts.Node, types: string[]): boolean { + const type = typeChecker.getTypeAtLocation(node); + const typeNode = + type ? typeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.None) : undefined; + let hasMatch = false; + if (typeNode) { + (function walk(current: ts.Node) { + if (ts.isIdentifier(current) && types.includes(current.text)) { + hasMatch = true; + } else if (!hasMatch && !ts.isTypeLiteralNode(current)) { + current.forEachChild(walk); + } + })(typeNode); + } + return hasMatch; +} diff --git a/packages/core/src/application_init.ts b/packages/core/src/application_init.ts index 4bf5fd25bd..69b6fe473d 100644 --- a/packages/core/src/application_init.ts +++ b/packages/core/src/application_init.ts @@ -6,8 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ +import {Observable} from 'rxjs'; import {Inject, Injectable, InjectionToken, Optional} from './di'; -import {isPromise} from './util/lang'; +import {isObservable, isPromise} from './util/lang'; import {noop} from './util/noop'; @@ -15,32 +16,79 @@ import {noop} from './util/noop'; * A [DI token](guide/glossary#di-token "DI token definition") that you can use to provide * one or more initialization functions. * - * 可用于提供一个或多个初始化功能的 [DI 令牌。](guide/glossary#di-token "DI 令牌定义") - * * The provided functions are injected at application startup and executed during - * app initialization. If any of these functions returns a Promise, initialization - * does not complete until the Promise is resolved. - * - * 所提供的函数是在应用程序启动时注入的,并在应用程序初始化期间执行。如果这些函数中的任何一个返回 Promise,则直到 Promise 被解析之前,初始化都不会完成。 + * app initialization. If any of these functions returns a Promise or an Observable, initialization + * does not complete until the Promise is resolved or the Observable is completed. * * You can, for example, create a factory function that loads language data * or an external configuration, and provide that function to the `APP_INITIALIZER` token. * The function is executed during the application bootstrap process, * and the needed data is available on startup. * - * 例如,你可以创建一个工厂函数来加载语言数据或外部配置,并将该函数提供给 `APP_INITIALIZER` 令牌。该功能在应用程序引导过程中执行,并且所需的数据在启动时可用。 - * * @see `ApplicationInitStatus` * + * @usageNotes + * + * The following example illustrates how to configure a multi-provider using `APP_INITIALIZER` token + * and a function returning a promise. + * + * ``` + * function initializeApp(): Promise<any> { + * return new Promise((resolve, reject) => { + * // Do some asynchronous stuff + * resolve(); + * }); + * } + * + * @NgModule({ + * imports: [BrowserModule], + * declarations: [AppComponent], + * bootstrap: [AppComponent], + * providers: [{ + * provide: APP_INITIALIZER, + * useFactory: () => initializeApp, + * multi: true + * }] + * }) + * export class AppModule {} + * ``` + * + * It's also possible to configure a multi-provider using `APP_INITIALIZER` token and a function + * returning an observable, see an example below. Note: the `HttpClient` in this example is used for + * demo purposes to illustrate how the factory function can work with other providers available + * through DI. + * + * ``` + * function initializeApp(httpClient: HttpClient): Observable<any> { + * return httpClient.get("https://someUrl.com/api/user") + * .pipe( + * tap(user => { ... }) + * ) + * } + * + * @NgModule({ + * imports: [BrowserModule, HttpClientModule], + * declarations: [AppComponent], + * bootstrap: [AppComponent], + * providers: [{ + * provide: APP_INITIALIZER, + * useFactory: initializeApp, + * deps: [HttpClient], + * multi: true + * }] + * }) + * export class AppModule {} + * ``` + * * @publicApi */ -export const APP_INITIALIZER = new InjectionToken<Array<() => void>>('Application Initializer'); +export const APP_INITIALIZER = + new InjectionToken<ReadonlyArray<() => Observable<unknown>| Promise<unknown>| void>>( + 'Application Initializer'); /** * A class that reflects the state of running {@link APP_INITIALIZER} functions. * - * 反映正在运行的 {@link APP_INITIALIZER} 函数状态的类。 - * * @publicApi */ @Injectable() @@ -51,7 +99,8 @@ export class ApplicationInitStatus { public readonly donePromise: Promise<any>; public readonly done = false; - constructor(@Inject(APP_INITIALIZER) @Optional() private appInits: (() => any)[]) { + constructor(@Inject(APP_INITIALIZER) @Optional() private readonly appInits: + ReadonlyArray<() => Observable<unknown>| Promise<unknown>| void>) { this.donePromise = new Promise((res, rej) => { this.resolve = res; this.reject = rej; @@ -76,6 +125,11 @@ export class ApplicationInitStatus { const initResult = this.appInits[i](); if (isPromise(initResult)) { asyncInitPromises.push(initResult); + } else if (isObservable(initResult)) { + const observableAsPromise = new Promise<void>((resolve, reject) => { + initResult.subscribe({complete: resolve, error: reject}); + }); + asyncInitPromises.push(observableAsPromise); } } } diff --git a/packages/core/src/application_module.ts b/packages/core/src/application_module.ts index 77f36a1efe..1079d19812 100644 --- a/packages/core/src/application_module.ts +++ b/packages/core/src/application_module.ts @@ -10,7 +10,6 @@ import {APP_INITIALIZER, ApplicationInitStatus} from './application_init'; import {ApplicationRef} from './application_ref'; import {APP_ID_RANDOM_PROVIDER} from './application_tokens'; 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'; import {ErrorHandler} from './error_handler'; @@ -78,7 +77,7 @@ export const APPLICATION_MODULE_PROVIDERS: StaticProvider[] = [ { provide: ApplicationRef, useClass: ApplicationRef, - deps: [NgZone, Console, Injector, ErrorHandler, ComponentFactoryResolver, ApplicationInitStatus] + deps: [NgZone, Injector, ErrorHandler, ComponentFactoryResolver, ApplicationInitStatus] }, {provide: SCHEDULER, deps: [NgZone], useFactory: zoneSchedulerFactory}, { @@ -124,13 +123,9 @@ export function zoneSchedulerFactory(ngZone: NgZone): (fn: () => void) => void { * providers of `@angular/core` dependencies that `ApplicationRef` needs * to bootstrap components. * - * 为应用配置根注入器,它带有 `ApplicationRef` 在引导组件时所需的来自 `@angular/core` 的提供者。 - * * Re-exported by `BrowserModule`, which is included automatically in the root * `AppModule` when you create a new app with the CLI `new` command. * - * 由 `BrowserModule` 重新导出,当你使用 CLI `new` 命令创建新应用时,它会自动包含在根 `AppModule` 中。 - * * @publicApi */ @NgModule({providers: APPLICATION_MODULE_PROVIDERS}) diff --git a/packages/core/src/application_ref.ts b/packages/core/src/application_ref.ts index eda196b95f..40d6bf6591 100644 --- a/packages/core/src/application_ref.ts +++ b/packages/core/src/application_ref.ts @@ -130,8 +130,6 @@ export const ALLOW_MULTIPLE_PLATFORMS = new InjectionToken<boolean>('AllowMultip /** * A token for third-party components that can register themselves with NgProbe. * - * 本令牌可以在 NgProbe 中注册自己的第三方组件。 - * * @publicApi */ export class NgProbeToken { @@ -142,8 +140,6 @@ export class NgProbeToken { * Creates a platform. * Platforms must be created on launch using this function. * - * 创建一个平台。必须使用此函数在启动时创建平台。 - * * @publicApi */ export function createPlatform(injector: Injector): PlatformRef { @@ -161,24 +157,13 @@ export function createPlatform(injector: Injector): PlatformRef { /** * Creates a factory for a platform. Can be used to provide or override `Providers` specific to - * your applciation's runtime needs, such as `PLATFORM_INITIALIZER` and `PLATFORM_ID`. - * - * 为平台创建工厂。可用于提供或覆盖针对你的应用程序的运行时需求的 `Providers`,比如 `PLATFORM_INITIALIZER` 和 `PLATFORM_ID` 。 - * + * your application's runtime needs, such as `PLATFORM_INITIALIZER` and `PLATFORM_ID`. * @param parentPlatformFactory Another platform factory to modify. Allows you to compose factories * to build up configurations that might be required by different libraries or parts of the * application. - * - * 要修改的另一个平台工厂。允许你组合多个工厂来构建一些配置,其它库或应用程序的其它部分可能需要的它们。 - * * @param name Identifies the new platform factory. - * - * 标识新的平台工厂。 - * * @param providers A set of dependency providers for platforms created with the new factory. * - * 使用新工厂创建的平台的一组依赖项提供者。 - * * @publicApi */ export function createPlatformFactory( @@ -208,8 +193,6 @@ export function createPlatformFactory( /** * Checks that there is currently a platform that contains the given token as a provider. * - * 检查当前是否存在以给定令牌为提供者的平台。 - * * @publicApi */ export function assertPlatform(requiredToken: any): PlatformRef { @@ -231,8 +214,6 @@ export function assertPlatform(requiredToken: any): PlatformRef { * Destroys the current Angular platform and all Angular applications on the page. * Destroys all modules and listeners registered with the platform. * - * 销毁页面上的当前 Angular 平台和所有 Angular 应用程序。销毁在平台上注册的所有模块和监听器。 - * * @publicApi */ export function destroyPlatform(): void { @@ -244,8 +225,6 @@ export function destroyPlatform(): void { /** * Returns the current platform. * - * 返回当前平台。 - * * @publicApi */ export function getPlatform(): PlatformRef|null { @@ -315,8 +294,6 @@ export interface BootstrapOptions { * A page's platform is initialized implicitly when a platform is created using a platform * factory such as `PlatformBrowser`, or explicitly by calling the `createPlatform()` function. * - * Angular 平台是 Angular 在网页上的入口点。每个页面只有一个平台。页面上运行的每个 Angular 应用程序所共有的服务(例如反射)都在其范围内绑定。当使用 `PlatformBrowser` 这样的平台工厂创建平台时,将隐式初始化此页面的平台;也可以通过调用 `createPlatform()` 函数来显式初始化此页面的平台。 - * * @publicApi */ @Injectable() @@ -331,14 +308,10 @@ export class PlatformRef { /** * Creates an instance of an `@NgModule` for the given platform for offline compilation. * - * 为给定的平台创建 `@NgModule` 的实例,以进行离线编译。 - * * @usageNotes * * The following example creates the NgModule for a browser platform. * - * 以下示例为浏览器平台创建 NgModule。 - * * ```typescript * my_module.ts: * @@ -377,12 +350,17 @@ export class PlatformRef { if (!exceptionHandler) { 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); - } - })); + ngZone!.runOutsideAngular(() => { + const subscription = ngZone!.onError.subscribe({ + next: (error: any) => { + exceptionHandler.handleError(error); + } + }); + moduleRef.onDestroy(() => { + remove(this._modules, moduleRef); + subscription.unsubscribe(); + }); + }); return _callAndReportToErrorHandler(exceptionHandler, ngZone!, () => { const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus); initStatus.runInitializers(); @@ -402,14 +380,9 @@ export class PlatformRef { /** * Creates an instance of an `@NgModule` for a given platform using the given runtime compiler. * - * 使用给定的运行时编译器为给定的平台创建 `@NgModule` 的实例。 - * * @usageNotes - * * ### Simple Example * - * ### 简单的例子 - * * ```typescript * @NgModule({ * imports: [BrowserModule] @@ -448,9 +421,6 @@ export class PlatformRef { /** * Registers a listener to be called when the platform is destroyed. - * - * 注册销毁平台时要调用的监听器。 - * */ onDestroy(callback: () => void): void { this._destroyListeners.push(callback); @@ -459,9 +429,6 @@ export class PlatformRef { /** * Retrieves the platform {@link Injector}, which is the parent injector for * every Angular application on the page and provides singleton providers. - * - * 检索平台 {@link Injector},该平台是页面上每个 Angular 应用程序的父注入器,并提供单例提供者。 - * */ get injector(): Injector { return this._injector; @@ -470,9 +437,6 @@ export class PlatformRef { /** * Destroys the current Angular platform and all Angular applications on the page. * Destroys all modules and listeners registered with the platform. - * - * 销毁页面上的当前 Angular 平台和所有 Angular 应用程序。销毁在平台上注册的所有模块和监听器。 - * */ destroy() { if (this._destroyed) { @@ -537,8 +501,6 @@ function optionsReducer<T extends Object>(dst: any, objs: T|T[]): T { /** * A reference to an Angular application running on a page. * - * 对页面上运行的 Angular 应用程序的引用。 - * * @usageNotes * * {@a is-stable-examples} @@ -635,48 +597,34 @@ export class ApplicationRef { private _bootstrapListeners: ((compRef: ComponentRef<any>) => void)[] = []; private _views: InternalViewRef[] = []; private _runningTick: boolean = false; - private _enforceNoNewChanges: boolean = false; private _stable = true; + private _onMicrotaskEmptySubscription: Subscription; /** * Get a list of component types registered to this application. * This list is populated even before the component is created. - * - * 获取注册到该应用程序的组件类型的列表。在创建组件之前,会填充此列表。 - * */ public readonly componentTypes: Type<any>[] = []; /** * Get a list of components registered to this application. - * - * 获取已注册到该应用中的组件的列表。 - * */ public readonly components: ComponentRef<any>[] = []; /** * Returns an Observable that indicates when the application is stable or unstable. * - * 返回一个 Observable,指示应用程序何时变得稳定或不稳定。 - * * @see [Usage notes](#is-stable-examples) for examples and caveats when using this API. - * - * [用法说明](#is-stable-examples)的例子和使用此 API 时的注意事项。 - * */ // TODO(issue/24571): remove '!'. public readonly isStable!: Observable<boolean>; /** @internal */ constructor( - private _zone: NgZone, private _console: Console, private _injector: Injector, - private _exceptionHandler: ErrorHandler, + private _zone: NgZone, private _injector: Injector, private _exceptionHandler: ErrorHandler, private _componentFactoryResolver: ComponentFactoryResolver, private _initStatus: ApplicationInitStatus) { - this._enforceNoNewChanges = isDevMode(); - - this._zone.onMicrotaskEmpty.subscribe({ + this._onMicrotaskEmptySubscription = this._zone.onMicrotaskEmpty.subscribe({ next: () => { this._zone.run(() => { this.tick(); @@ -736,29 +684,17 @@ export class ApplicationRef { /** * Bootstrap a new component at the root level of the application. * - * 在应用程序的根级引导新组件。 - * * @usageNotes - * * ### Bootstrap process * - * ### 引导过程 - * * When bootstrapping a new root component into an application, Angular mounts the * specified application component onto DOM elements identified by the componentType's * selector and kicks off automatic change detection to finish initializing the component. * - * 将新的根组件引导到应用程序时,Angular 将指定的应用程序组件安装到由 componentType 的选择器标识的 DOM 元素上,并启动自动变更检测以完成组件的初始化。 - * * Optionally, a component can be mounted onto a DOM element that does not match the * componentType's selector. * - * (可选)可以将组件安装到与 componentType 的选择器不匹配的 DOM 元素上。 - * * ### Example - * - * ### 例子 - * * {@example core/ts/platform/platform.ts region='longform'} */ bootstrap<C>(componentOrFactory: ComponentFactory<C>|Type<C>, rootSelectorOrNode?: string|any): @@ -781,19 +717,27 @@ export class ApplicationRef { isBoundToModule(componentFactory) ? undefined : this._injector.get(NgModuleRef); const selectorOrNode = rootSelectorOrNode || componentFactory.selector; const compRef = componentFactory.create(Injector.NULL, [], selectorOrNode, ngModule); - - compRef.onDestroy(() => { - this._unloadComponent(compRef); - }); + const nativeElement = compRef.location.nativeElement; const testability = compRef.injector.get(Testability, null); - if (testability) { - compRef.injector.get(TestabilityRegistry) - .registerApplication(compRef.location.nativeElement, testability); + const testabilityRegistry = testability && compRef.injector.get(TestabilityRegistry); + if (testability && testabilityRegistry) { + testabilityRegistry.registerApplication(nativeElement, testability); } + compRef.onDestroy(() => { + this.detachView(compRef.hostView); + remove(this.components, compRef); + if (testabilityRegistry) { + testabilityRegistry.unregisterApplication(nativeElement); + } + }); + this._loadComponent(compRef); - if (isDevMode()) { - this._console.log( + // Note that we have still left the `isDevMode()` condition in order to avoid + // creating a breaking change for projects that still use the View Engine. + if ((typeof ngDevMode === 'undefined' || ngDevMode) && isDevMode()) { + const _console = this._injector.get(Console); + _console.log( `Angular is running in development mode. Call enableProdMode() to enable production mode.`); } return compRef; @@ -802,17 +746,12 @@ export class ApplicationRef { /** * Invoke this method to explicitly process change detection and its side-effects. * - * 调用此方法可以显式处理变更检测及其副作用。 - * * In development mode, `tick()` also performs a second change detection cycle to ensure that no * further changes are detected. If additional changes are picked up during this second cycle, * bindings in the app have side-effects that cannot be resolved in a single change detection * pass. * In this case, Angular throws an error, since an Angular application can only have one change * detection pass during which all change detection must complete. - * - * 在开发模式下,`tick()` 还会执行第二个变更检测周期,以确保没有检测到其他更改。如果在第二个周期内获取了其他更改,则应用程序中的绑定就会产生副作用,这些副作用无法通过一次变更检测过程解决。在这种情况下,Angular 就会引发错误,因为 Angular 应用程序只能进行一次变更检测遍历,在此过程中必须完成所有变更检测。 - * */ tick(): void { if (this._runningTick) { @@ -824,7 +763,9 @@ export class ApplicationRef { for (let view of this._views) { view.detectChanges(); } - if (this._enforceNoNewChanges) { + // Note that we have still left the `isDevMode()` condition in order to avoid + // creating a breaking change for projects that still use the View Engine. + if ((typeof ngDevMode === 'undefined' || ngDevMode) && isDevMode()) { for (let view of this._views) { view.checkNoChanges(); } @@ -841,9 +782,6 @@ export class ApplicationRef { * Attaches a view so that it will be dirty checked. * The view will be automatically detached when it is destroyed. * This will throw if the view is already attached to a ViewContainer. - * - * 附加视图,以便对其进行脏检查。视图销毁后将自动分离。如果视图已附加到 ViewContainer,则会抛出此错误。 - * */ attachView(viewRef: ViewRef): void { const view = (viewRef as InternalViewRef); @@ -853,9 +791,6 @@ export class ApplicationRef { /** * Detaches a view from dirty checking again. - * - * 再次从脏检查中分离视图。 - * */ detachView(viewRef: ViewRef): void { const view = (viewRef as InternalViewRef); @@ -873,22 +808,14 @@ export class ApplicationRef { listeners.forEach((listener) => listener(componentRef)); } - private _unloadComponent(componentRef: ComponentRef<any>): void { - this.detachView(componentRef.hostView); - remove(this.components, componentRef); - } - /** @internal */ ngOnDestroy() { - // TODO(alxhub): Dispose of the NgZone. this._views.slice().forEach((view) => view.destroy()); + this._onMicrotaskEmptySubscription.unsubscribe(); } /** * Returns the number of attached views. - * - * 返回已附加视图的数量。 - * */ get viewCount() { return this._views.length; diff --git a/packages/core/src/application_tokens.ts b/packages/core/src/application_tokens.ts index 670f2ffdfa..659fa27efe 100644 --- a/packages/core/src/application_tokens.ts +++ b/packages/core/src/application_tokens.ts @@ -15,14 +15,10 @@ import {ComponentRef} from './linker/component_factory'; * primarily for prefixing application attributes and CSS styles when * {@link ViewEncapsulation#Emulated ViewEncapsulation.Emulated} is being used. * - * 表示唯一字符串 ID 的 [DI 令牌](guide/glossary#di-token "DI 令牌定义"),主要用于在使用 {@link ViewEncapsulation#Emulated ViewEncapsulation.Emulated} 时为应用程序属性和 CSS 样式添加前缀。 - * * BY default, the value is randomly generated and assigned to the application by Angular. * To provide a custom ID value, use a DI provider <!-- TODO: provider --> to configure * the root {@link Injector} that uses this token. * - * 默认情况下,该值是随机生成的,并且由 Angular 赋值给此应用。要提供一个自定义的 ID 值,可以使用一个 DI 提供者,根 {@link Injector} 会使用此令牌。 - * * @publicApi */ export const APP_ID = new InjectionToken<string>('AppId'); @@ -33,9 +29,6 @@ export function _appIdRandomProviderFactory() { /** * Providers that generate a random `APP_ID_TOKEN`. - * - * 生成随机 `APP_ID_TOKEN` 的提供者。 - * * @publicApi */ export const APP_ID_RANDOM_PROVIDER = { @@ -50,18 +43,12 @@ function _randomChar(): string { /** * A function that is executed when a platform is initialized. - * - * 平台初始化时执行的函数。 - * * @publicApi */ export const PLATFORM_INITIALIZER = new InjectionToken<Array<() => void>>('Platform Initializer'); /** * A token that indicates an opaque platform ID. - * - * 标识不透明平台 ID 的令牌。 - * * @publicApi */ export const PLATFORM_ID = new InjectionToken<Object>('Platform ID'); @@ -70,12 +57,8 @@ export const PLATFORM_ID = new InjectionToken<Object>('Platform ID'); * A [DI token](guide/glossary#di-token "DI token definition") that provides a set of callbacks to * be called for every component that is bootstrapped. * - * 一个 [DI 令牌](guide/glossary#di-token "DI 令牌定义"),该令牌提供了一组针对每个要引导的组件调用的回调。 - * * Each callback must take a `ComponentRef` instance and return nothing. * - * 每个回调都必须接受 `ComponentRef` 实例,并且不返回任何内容。 - * * `(componentRef: ComponentRef) => void` * * @publicApi @@ -86,9 +69,6 @@ export const APP_BOOTSTRAP_LISTENER = /** * A [DI token](guide/glossary#di-token "DI token definition") that indicates the root directory of * the application - * - * 一个 [DI 令牌](guide/glossary#di-token "DI 令牌定义"),指示应用程序的根目录 - * * @publicApi */ export const PACKAGE_ROOT_URL = new InjectionToken<string>('Application Packages Root URL'); diff --git a/packages/core/src/change_detection/change_detection_util.ts b/packages/core/src/change_detection/change_detection_util.ts index 05bd0e7d78..25ab4fd4ab 100644 --- a/packages/core/src/change_detection/change_detection_util.ts +++ b/packages/core/src/change_detection/change_detection_util.ts @@ -28,17 +28,11 @@ export function devModeEqual(a: any, b: any): boolean { * Indicates that the result of a {@link Pipe} transformation has changed even though the * reference has not changed. * - * 表示 {@link Pipe} 转换的值已经变化了 —— 即使其引用并没有变。 - * * Wrapped values are unwrapped automatically during the change detection, and the unwrapped value * is stored. * - * 包装过的值会在变更检测期间自动解包,并保存解包过的值。 - * * Example: * - * 例子: - * * ``` * if (this._latestValue === this._latestReturnedValue) { * return this._latestReturnedValue; @@ -50,25 +44,16 @@ export function devModeEqual(a: any, b: any): boolean { * * @publicApi * @deprecated from v10 stop using. (No replacement, deemed unnecessary.) - * - * 从 v10 开始停止使用。 (无替代品,没必要。) - * */ export class WrappedValue { - /** @deprecated from 5.3, use `unwrap()` instead - will switch to protected - * - * 从 5.3 之后将会变成受保护的属性,请用 `unwrap()` 代替 - */ + /** @deprecated from 5.3, use `unwrap()` instead - will switch to protected */ wrapped: any; constructor(value: any) { this.wrapped = value; } - /** Creates a wrapped value. - * - * 创建一个包装过的值。 - */ + /** Creates a wrapped value. */ static wrap(value: any): WrappedValue { return new WrappedValue(value); } @@ -76,17 +61,12 @@ export class WrappedValue { /** * Returns the underlying value of a wrapped value. * Returns the given `value` when it is not wrapped. - * - * 如果值(`value`)是包装过的,则返回它幕后的值;否则直接返回它本身。 **/ static unwrap(value: any): any { return WrappedValue.isWrapped(value) ? value.wrapped : value; } - /** Returns true if `value` is a wrapped value. - * - * 如果 `value` 是包装过的值,则返回 `true`。 - */ + /** Returns true if `value` is a wrapped value. */ static isWrapped(value: any): value is WrappedValue { return value instanceof WrappedValue; } diff --git a/packages/core/src/change_detection/change_detector_ref.ts b/packages/core/src/change_detection/change_detector_ref.ts index bef6be8aeb..ad805d2484 100644 --- a/packages/core/src/change_detection/change_detector_ref.ts +++ b/packages/core/src/change_detection/change_detector_ref.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ +import {InjectFlags} from '../di'; +import {InternalInjectFlags} from '../di/interface/injector'; import {TNode, TNodeType} from '../render3/interfaces/node'; import {isComponentHost} from '../render3/interfaces/type_checks'; import {DECLARATION_COMPONENT_VIEW, LView} from '../render3/interfaces/view'; @@ -25,69 +27,41 @@ const SWITCH_CHANGE_DETECTOR_REF_FACTORY: typeof injectChangeDetectorRef = * Use the methods to add and remove views from the tree, initiate change-detection, * and explicitly mark views as _dirty_, meaning that they have changed and need to be re-rendered. * - * Angular 各种视图的基础类,提供变更检测功能。 - * 变更检测树会收集要检查的所有视图。 - * 使用这些方法从树中添加或移除视图、初始化变更检测并显式地把这些视图标记为*脏的*,意思是它们变了、需要重新渲染。 - * * @see [Using change detection hooks](guide/lifecycle-hooks#using-change-detection-hooks) - * - * [使用变更检测钩子](guide/lifecycle-hooks#using-change-detection-hooks) - * * @see [Defining custom change detection](guide/lifecycle-hooks#defining-custom-change-detection) * - * [定义自定义变更检测](guide/lifecycle-hooks#defining-custom-change-detection) - * - * - *@usageNotes + * @usageNotes * * The following examples demonstrate how to modify default change-detection behavior * to perform explicit detection when needed. * - * 下面的例子演示了如何修改默认的变更检测行为,以便在需要时执行显式变更检测。 - * * ### Use `markForCheck()` with `CheckOnce` strategy * - * ### 使用 `markForCheck()` 和 `CheckOnce` 策略 - * * The following example sets the `OnPush` change-detection strategy for a component * (`CheckOnce`, rather than the default `CheckAlways`), then forces a second check * after an interval. See [live demo](https://plnkr.co/edit/GC512b?p=preview). * - * 下面的例子为组件设置了 `OnPush` 变更检测策略(`CheckOnce` 而不是默认的 `CheckAlways`),然后每隔一段时间强制进行第二轮检测。 - * 参见[在线例子](http://plnkr.co/edit/GC512b?p=preview)。 - * * <code-example path="core/ts/change_detect/change-detection.ts" * region="mark-for-check"></code-example> * * ### Detach change detector to limit how often check occurs * - * ### 分离开变更检测器以限制变更检测的发生频度 - * * The following example defines a component with a large list of read-only data * that is expected to change constantly, many times per second. * To improve performance, we want to check and update the list * less often than the changes actually occur. To do that, we detach * the component's change detector and perform an explicit local check every five seconds. * - * 下面的例子定义了一个带有只读数据的大型列表,这些数据预计每秒会变化很多次。 - * 为了提高性能,我们检测和更新列表的频率就应该比实际发生的变化少得多。 - * 要解决这个问题,就要分离开变更检测器,并每隔五秒钟显式执行一次变更检查。 - * * <code-example path="core/ts/change_detect/change-detection.ts" region="detach"></code-example> * * * ### Reattaching a detached component * - * ### 重新附加一个已分离的组件 - * * The following example creates a component displaying live data. * The component detaches its change detector from the main change detector tree * when the `live` property is set to false, and reattaches it when the property * becomes true. * - * 下面的例子创建了一个用来显示活动数据的组件。 - * 当 `live` 属性为 `false` 时,该组件就把它的变更检测器从主变更检测器树中分离出来,当该属性变为 `true` 时,则重新附加上它。 - * * <code-example path="core/ts/change_detect/change-detection.ts" region="reattach"></code-example> * * @publicApi @@ -98,14 +72,10 @@ export abstract class ChangeDetectorRef { * change detection strategy, explicitly marks the view as changed so that * it can be checked again. * - * 当视图使用 {@link ChangeDetectionStrategy#OnPush OnPush}(`checkOnce`)变更检测策略时,把该视图显式标记为已更改,以便它再次进行检查。 - * * Components are normally marked as dirty (in need of rerendering) when inputs * have changed or events have fired in the view. Call this method to ensure that * a component is checked even if these triggers have not occured. * - * 当输入已更改或视图中发生了事件时,组件通常会标记为脏的(需要重新渲染)。调用此方法会确保即使那些触发器没有被触发,也仍然检查该组件。 - * * <!-- TODO: Add a link to a chapter on OnPush components --> * */ @@ -116,15 +86,9 @@ export abstract class ChangeDetectorRef { * A detached view is not checked until it is reattached. * Use in combination with `detectChanges()` to implement local change detection checks. * - * 从变更检测树中分离开视图。 - * 已分离的视图在重新附加上去之前不会被检查。 - * 与 `detectChanges()` 结合使用,可以实现局部变更检测。 - * * Detached views are not checked during change detection runs until they are * re-attached, even if they are marked as dirty. * - * 即使已分离的视图已标记为脏的,它们在重新附加上去之前也不会被检查。 - * * <!-- TODO: Add a link to a chapter on detach/reattach/local digest --> * <!-- TODO: Add a live demo once ref.detectChanges is merged into master --> * @@ -136,8 +100,6 @@ export abstract class ChangeDetectorRef { * detach} * to implement local change detection checks. * - * 检查该视图及其子视图。与 {@link ChangeDetectorRef#detach detach} 结合使用可以实现局部变更检测。 - * * <!-- TODO: Add a link to a chapter on detach/reattach/local digest --> * <!-- TODO: Add a live demo once ref.detectChanges is merged into master --> * @@ -147,12 +109,8 @@ export abstract class ChangeDetectorRef { /** * Checks the change detector and its children, and throws if any changes are detected. * - * 检查变更检测器及其子检测器,如果检测到任何更改,则抛出异常。 - * * Use in development mode to verify that running change detection doesn't introduce * other changes. - * - * 在开发模式下可用来验证正在运行的变更检测器是否引入了其它变更。 */ abstract checkNoChanges(): void; @@ -160,9 +118,6 @@ export abstract class ChangeDetectorRef { * Re-attaches the previously detached view to the change detection tree. * Views are attached to the tree by default. * - * 把以前分离开的视图重新附加到变更检测树上。 - * 视图会被默认附加到这棵树上。 - * * <!-- TODO: Add a link to a chapter on detach/reattach/local digest --> * */ @@ -172,24 +127,17 @@ export abstract class ChangeDetectorRef { * @internal * @nocollapse */ - static __NG_ELEMENT_ID__: () => ChangeDetectorRef = SWITCH_CHANGE_DETECTOR_REF_FACTORY; - - /** - * This marker is need so that the JIT compiler can correctly identify this class as special. - * - * 使用此标记,以便 JIT 编译器可以正确地将此类标识为特殊类。 - * - * @internal - * @nocollapse - */ - static __ChangeDetectorRef__ = true; + static __NG_ELEMENT_ID__: + (flags: InjectFlags) => ChangeDetectorRef = SWITCH_CHANGE_DETECTOR_REF_FACTORY; } /** Returns a ChangeDetectorRef (a.k.a. a ViewRef) */ -export function injectChangeDetectorRef(isPipe = false): ChangeDetectorRef { - return createViewRef(getCurrentTNode()!, getLView(), isPipe); +export function injectChangeDetectorRef(flags: InjectFlags): ChangeDetectorRef { + return createViewRef( + getCurrentTNode()!, getLView(), + (flags & InternalInjectFlags.ForPipe) === InternalInjectFlags.ForPipe); } /** @@ -201,10 +149,7 @@ export function injectChangeDetectorRef(isPipe = false): ChangeDetectorRef { * @returns The ChangeDetectorRef to use */ function createViewRef(tNode: TNode, lView: LView, isPipe: boolean): ChangeDetectorRef { - // `isComponentView` will be true for Component and Directives (but not for Pipes). - // See https://github.com/angular/angular/pull/33072 for proper fix - const isComponentView = !isPipe && isComponentHost(tNode); - if (isComponentView) { + if (isComponentHost(tNode) && !isPipe) { // The LView represents the location where the component is declared. // Instead we want the LView for the component View and so we need to look it up. const componentView = getComponentLViewByIndex(tNode.index, lView); // look down diff --git a/packages/core/src/change_detection/constants.ts b/packages/core/src/change_detection/constants.ts index 840ab60efc..bc81f02f62 100644 --- a/packages/core/src/change_detection/constants.ts +++ b/packages/core/src/change_detection/constants.ts @@ -11,12 +11,8 @@ * The strategy that the default change detector uses to detect changes. * When set, takes effect the next time change detection is triggered. * - * 默认变更检测器用来检测更改的策略。设置后,将在下次触发变更检测时生效。 - * * @see {@link ChangeDetectorRef#usage-notes Change detection usage} * - * {@link ChangeDetectorRef#usage-notes 变更检测的用法} - * * @publicApi */ export enum ChangeDetectionStrategy { @@ -25,18 +21,12 @@ export enum ChangeDetectionStrategy { * until reactivated by setting the strategy to `Default` (`CheckAlways`). * Change detection can still be explicitly invoked. * This strategy applies to all child directives and cannot be overridden. - * - * 使用 `CheckOnce` 策略,这意味着把此策略设置为 `Default`( `CheckAlways` )将禁用自动变更检测,直到重新激活。变更检测仍然可以显式调用。此策略适用于所有子指令,并且不能被覆盖。 - * */ OnPush = 0, /** * Use the default `CheckAlways` strategy, in which change detection is automatic until * explicitly deactivated. - * - * 使用默认的 `CheckAlways` 策略,在该策略中,变更检测将自动执行,直到显式停用为止。 - * */ Default = 1, } 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 bda5af58de..60a1dacf8d 100644 --- a/packages/core/src/change_detection/differs/default_iterable_differ.ts +++ b/packages/core/src/change_detection/differs/default_iterable_differ.ts @@ -27,9 +27,6 @@ const trackByIdentity = (index: number, item: any) => item; /** * @deprecated v4.0.0 - Should not be part of public API. - * - * v4.0.0-不应成为公共 API 的一部分。 - * * @publicApi */ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChanges<V> { @@ -234,8 +231,6 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan * Set the previousIndexes of moved and added items to their currentIndexes * Reset the list of additions, moves and removals * - * 重置更改对象的状态以便不显示任何更改。这意味着将 previousKey 设置为 currentKey,并清除所有队列(添加、移动、移除)。将已移动和已添加条目的 previousIndexes 设置为其 currentIndexes。重置包含添加、移动和删除的列表 - * * @internal */ _reset() { @@ -266,21 +261,11 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan /** * This is the core function which handles differences between collections. * - * 这是处理集合之间差异的核心函数。 - * * - `record` is the record which we saw at this position last time. If null then it is a new * item. - * - * `record` 是我们上次在此位置看到的记录。如果为 null,则为新条目。 - * * - `item` is the current item in the collection - * - * `item` 是集合中的当前条目 - * * - `index` is the position of the item in the collection * - * `index` 是条目在集合中的位置 - * * @internal */ _mismatch(record: IterableChangeRecord_<V>|null, item: V, itemTrackBy: any, index: number): @@ -296,23 +281,24 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan this._remove(record); } - // Attempt to see if we have seen the item before. - record = this._linkedRecords === null ? null : this._linkedRecords.get(itemTrackBy, index); + // See if we have evicted the item, which used to be at some anterior position of _itHead list. + record = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy, null); if (record !== null) { - // We have seen this before, we need to move it forward in the collection. - // But first we need to check if identity changed, so we can update in view if necessary + // It is an item which we have evicted earlier: reinsert it back into the list. + // But first we need to check if identity changed, so we can update in view if necessary. if (!Object.is(record.item, item)) this._addIdentityChange(record, item); - this._moveAfter(record, previousRecord, index); + this._reinsertAfter(record, previousRecord, index); } else { - // Never seen it, check evicted list. - record = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy, null); + // Attempt to see if the item is at some posterior position of _itHead list. + record = this._linkedRecords === null ? null : this._linkedRecords.get(itemTrackBy, index); if (record !== null) { - // It is an item which we have evicted earlier: reinsert it back into the list. - // But first we need to check if identity changed, so we can update in view if necessary + // We have the item in _itHead at/after `index` position. We need to move it forward in the + // collection. + // But first we need to check if identity changed, so we can update in view if necessary. if (!Object.is(record.item, item)) this._addIdentityChange(record, item); - this._reinsertAfter(record, previousRecord, index); + this._moveAfter(record, previousRecord, index); } else { // It is a new item: add it. record = @@ -325,51 +311,20 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan /** * This check is only needed if an array contains duplicates. (Short circuit of nothing dirty) * - * 仅当数组包含重复项时才需要进行此检查。 (不脏时跳过) - * * Use case: `[a, a]` => `[b, a, a]` * - * 用例: `[a, a]` => `[b, a, a]` - * * If we did not have this check then the insertion of `b` would: - * - * 如果我们不做这项检查,则插入 `b` 时会: - * - * 1. evict first `a` - * - * 移出第一个 `a` - * - * 1. insert `b` at `0` index. - * - * 把 `b` 插入在 `0` 号索引处。 - * - * 1. leave `a` at index `1` as is. <-- this is wrong! - * - * 把 `a` 留在现在的 `1` 号索引处。<-- 这是错的! - * - * 1. reinsert `a` at index 2. <-- this is wrong! - * - * 重新把 `a` 插入在 `2` 号索引处。 <-- 这是错的! + * 1) evict first `a` + * 2) insert `b` at `0` index. + * 3) leave `a` at index `1` as is. <-- this is wrong! + * 3) reinsert `a` at index 2. <-- this is wrong! * * The correct behavior is: + * 1) evict first `a` + * 2) insert `b` at `0` index. + * 3) reinsert `a` at index 1. + * 3) move `a` at from `1` to `2`. * - * 正确的行为是: - * - * 1. evict first `a` - * - * 移出第一个 `a` - * - * 1. insert `b` at `0` index. - * - * 把 `b` 插入在 `0` 号索引处。 - * - * 1. reinsert `a` at index 1. - * - * 把 `a` 插入在 `1` 号索引处。 - * - * 1. move `a` at from `1` to `2`. - * - * 把 `a` 从 `1` 号位移到 `2` 号位。 * * Double check that we have not evicted a duplicate item. We need to check if the item type may * have already been removed: @@ -378,8 +333,6 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan * better way to think of it is as insert of 'b' rather then switch 'a' with 'b' and then add 'a' * at the end. * - * 再次确认我们没有移出重复的条目。我们需要检查此条目类型是否已被删除:插入 b 将移出第一个 “a”。如果我们现在不重新插入,它将重新在最后插入。这将表现为两个 “a” 调换了位置。这是不正确的,因为更好的方法是插入 “b”,而不是将 “a” 和 “b” 互换,然后在末尾添加 “a”。 - * * @internal */ _verifyReinsertion(record: IterableChangeRecord_<V>, item: V, itemTrackBy: any, index: number): @@ -398,11 +351,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan /** * Get rid of any excess {@link IterableChangeRecord_}s from the previous collection * - * 摆脱上一个集合中任何多余的 {@link IterableChangeRecord_} - * - * - `record` The first excess {@link IterableChangeRecord\_}. - * - * `record` 是指第一个多余的{@link IterableChangeRecord\_}。 + * - `record` The first excess {@link IterableChangeRecord_}. * * @internal */ diff --git a/packages/core/src/change_detection/differs/iterable_differs.ts b/packages/core/src/change_detection/differs/iterable_differs.ts index d6f8ea136f..edb6b1e3b0 100644 --- a/packages/core/src/change_detection/differs/iterable_differs.ts +++ b/packages/core/src/change_detection/differs/iterable_differs.ts @@ -16,8 +16,6 @@ import {DefaultIterableDifferFactory} from '../differs/default_iterable_differ'; /** * A type describing supported iterable types. * - * 描述受支持的可迭代类型的类型。 - * * @publicApi */ export type NgIterable<T> = Array<T>|Iterable<T>; @@ -26,25 +24,15 @@ export type NgIterable<T> = Array<T>|Iterable<T>; * A strategy for tracking changes over time to an iterable. Used by {@link NgForOf} to * respond to changes in an iterable by effecting equivalent changes in the DOM. * - * 用来跟踪一个迭代内的更改的策略。{@link NgForOf} 使用它通过对 DOM 进行等效更改来响应此迭代内的更改。 - * * @publicApi */ export interface IterableDiffer<V> { /** * Compute a difference between the previous state and the new `object` state. * - * 计算先前状态和新 `object` 状态之间的差异。 - * * @param object containing the new value. - * - * 包含新值。 - * * @returns an object describing the difference. The return value is only valid until the next * `diff()` invocation. - * - * 描述差异的对象。返回值仅在下一次 `diff()` 调用之前有效。 - * */ diff(object: NgIterable<V>|undefined|null): IterableChanges<V>|null; } @@ -53,17 +41,12 @@ export interface IterableDiffer<V> { * An object describing the changes in the `Iterable` collection since last time * `IterableDiffer#diff()` was invoked. * - * 本对象描述自上次调用 `IterableDiffer#diff()` 以来 `Iterable` 集合中的变更。 - * * @publicApi */ export interface IterableChanges<V> { /** * Iterate over all changes. `IterableChangeRecord` will contain information about changes * to each item. - * - * 遍历所有更改。`IterableChangeRecord` 将包含有关每个条目更改的信息。 - * */ forEachItem(fn: (record: IterableChangeRecord<V>) => void): void; @@ -71,30 +54,17 @@ export interface IterableChanges<V> { * Iterate over a set of operations which when applied to the original `Iterable` will produce the * new `Iterable`. * - * 遍历一组操作,将这些操作应用于原始 `Iterable`,将产生新的 `Iterable` 。 - * * NOTE: These are not necessarily the actual operations which were applied to the original * `Iterable`, rather these are a set of computed operations which may not be the same as the * ones applied. * - * 注意:这些不一定是应用于原始 `Iterable` 的实际操作,而是这些计算的操作集合,可能与所应用的操作不同。 - * * @param record A change which needs to be applied - * - * 需要应用的更改 - * * @param previousIndex The `IterableChangeRecord#previousIndex` of the `record` refers to the * original `Iterable` location, where as `previousIndex` refers to the transient location * of the item, after applying the operations up to this point. - * - * 此 `record` 的 `IterableChangeRecord#previousIndex` 是指原来 `Iterable` 中的位置,这个 `previousIndex` 是指应用此操作之后条目的瞬时位置。 - * * @param currentIndex The `IterableChangeRecord#currentIndex` of the `record` refers to the * original `Iterable` location, where as `currentIndex` refers to the transient location * of the item, after applying the operations up to this point. - * - * 此 `record` 的 `IterableChangeRecord#currentIndex` 是指原来 `Iterable` 中的位置,这个 `currentIndex` 是指应用此操作之后条目的瞬时位置。 - * */ forEachOperation( fn: @@ -104,42 +74,21 @@ export interface IterableChanges<V> { /** * Iterate over changes in the order of original `Iterable` showing where the original items * have moved. - * - * 按原始 `Iterable` 中的顺序遍历这些变更,以找出原始条目移动到的位置。 - * */ forEachPreviousItem(fn: (record: IterableChangeRecord<V>) => void): void; - /** - * Iterate over all added items. - * - * 遍历所有添加的条目。 - * - */ + /** Iterate over all added items. */ forEachAddedItem(fn: (record: IterableChangeRecord<V>) => void): void; - /** - * Iterate over all moved items. - * - * 遍历所有移动过的条目。 - * - */ + /** Iterate over all moved items. */ forEachMovedItem(fn: (record: IterableChangeRecord<V>) => void): void; - /** - * Iterate over all removed items. - * - * 遍历所有已删除的条目。 - * - */ + /** 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. - * - * 遍历所有更改过标识(由 `TrackByFunction` 计算)的条目。 - * */ forEachIdentityChange(fn: (record: IterableChangeRecord<V>) => void): void; } @@ -147,41 +96,19 @@ export interface IterableChanges<V> { /** * Record representing the item change information. * - * 代表条目变更信息的记录。 - * * @publicApi */ export interface IterableChangeRecord<V> { - /** - * Current index of the item in `Iterable` or null if removed. - * - * 此条目在 `Iterable` 中的当前索引,如果已删除则为 null。 - * - */ + /** Current index of the item in `Iterable` or null if removed. */ readonly currentIndex: number|null; - /** - * Previous index of the item in `Iterable` or null if added. - * - * 此条目在 `Iterable` 中以前的索引,如果是新添加的则为 null。 - * - */ + /** Previous index of the item in `Iterable` or null if added. */ readonly previousIndex: number|null; - /** - * The item. - * - * 本条目。 - * - */ + /** The item. */ readonly item: V; - /** - * Track by identity as computed by the `TrackByFunction`. - * - * 通过 `TrackByFunction` 计算出的标识进行跟踪。 - * - */ + /** Track by identity as computed by the `TrackByFunction`. */ readonly trackById: any; } @@ -191,8 +118,6 @@ export interface IterableChangeRecord<V> { * The function takes the iteration index and item ID. * When supplied, Angular tracks changes by the return value of the function. * - * 传给 `NgForOf` 指令的可选函数,该函数定义如何跟踪可迭代对象中条目的更改。该函数接受迭代索引和条目 ID 作为参数。提供后,Angular 将根据函数的返回值的变化进行跟踪。 - * * @publicApi */ export interface TrackByFunction<T> { @@ -202,8 +127,6 @@ export interface TrackByFunction<T> { /** * Provides a factory for {@link IterableDiffer}. * - * 提供 {@link IterableDiffer} 的工厂。 - * * @publicApi */ export interface IterableDifferFactory { @@ -211,26 +134,22 @@ export interface IterableDifferFactory { create<V>(trackByFn?: TrackByFunction<V>): IterableDiffer<V>; } +export function defaultIterableDiffersFactory() { + return new IterableDiffers([new DefaultIterableDifferFactory()]); +} + /** * A repository of different iterable diffing strategies used by NgFor, NgClass, and others. * - * NgFor、NgClass 等使用的不同迭代策略的存储库。 - * * @publicApi */ export class IterableDiffers { /** @nocollapse */ - static ɵprov = ɵɵdefineInjectable({ - token: IterableDiffers, - providedIn: 'root', - factory: () => new IterableDiffers([new DefaultIterableDifferFactory()]) - }); + static ɵprov = /** @pureOrBreakMyCode */ ɵɵdefineInjectable( + {token: IterableDiffers, providedIn: 'root', factory: defaultIterableDiffersFactory}); /** * @deprecated v4.0.0 - Should be private - * - * v4.0.0-应该是私有的 - * */ factories: IterableDifferFactory[]; constructor(factories: IterableDifferFactory[]) { @@ -251,20 +170,13 @@ export class IterableDiffers { * inherited {@link IterableDiffers} instance with the provided factories and return a new * {@link IterableDiffers} instance. * - * 接受一个 {@link IterableDifferFactory} 数组,并返回一个提供者,用于扩展继承的带有提供者工厂的 {@link IterableDiffers} 实例,并返回一个新的 {@link IterableDiffers} 实例。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * The following example shows how to extend an existing list of factories, * which will only be applied to the injector for this component and its children. * This step is all that's required to make a new {@link IterableDiffer} available. * - * 以下示例演示了如何扩展现有工厂列表,该列表仅适用于该组件及其子组件的注入器。这就是使新的 {@link IterableDiffer} 可用的全部步骤。 - * * ``` * @Component({ * viewProviders: [ @@ -276,14 +188,11 @@ export class IterableDiffers { static extend(factories: IterableDifferFactory[]): StaticProvider { return { provide: IterableDiffers, - useFactory: (parent: IterableDiffers) => { - if (!parent) { - // Typically would occur when calling IterableDiffers.extend inside of dependencies passed - // to - // bootstrap(), which would override default pipes instead of extending them. - throw new Error('Cannot extend IterableDiffers without a parent injector'); - } - return IterableDiffers.create(factories, parent); + useFactory: (parent: IterableDiffers|null) => { + // if parent is null, it means that we are in the root injector and we have just overridden + // the default injection mechanism for IterableDiffers, in such a case just assume + // `defaultIterableDiffersFactory`. + return IterableDiffers.create(factories, parent || defaultIterableDiffersFactory()); }, // Dependency technically isn't optional, but we can provide a better error message this way. deps: [[IterableDiffers, new SkipSelf(), new Optional()]] diff --git a/packages/core/src/change_detection/differs/keyvalue_differs.ts b/packages/core/src/change_detection/differs/keyvalue_differs.ts index 28ffadf68f..e6e2698d9a 100644 --- a/packages/core/src/change_detection/differs/keyvalue_differs.ts +++ b/packages/core/src/change_detection/differs/keyvalue_differs.ts @@ -13,42 +13,24 @@ import {DefaultKeyValueDifferFactory} from './default_keyvalue_differ'; /** * A differ that tracks changes made to an object over time. * - * 跟踪对象随时间变化的差异。 - * * @publicApi */ export interface KeyValueDiffer<K, V> { /** * Compute a difference between the previous state and the new `object` state. * - * 计算先前状态和新 `object` 状态之间的差异。 - * * @param object containing the new value. - * - * 包含新值。 - * * @returns an object describing the difference. The return value is only valid until the next * `diff()` invocation. - * - * 描述差异的对象。返回值仅在下一次 `diff()` 调用之前有效。 - * */ diff(object: Map<K, V>): KeyValueChanges<K, V>|null; /** * Compute a difference between the previous state and the new `object` state. * - * 计算先前状态和新 `object` 状态之间的差异。 - * * @param object containing the new value. - * - * 包含新值。 - * * @returns an object describing the difference. The return value is only valid until the next * `diff()` invocation. - * - * 描述差异的对象。返回值仅在下一次 `diff()` 调用之前有效。 - * */ diff(object: {[key: string]: V}): KeyValueChanges<string, V>|null; // TODO(TS2.1): diff<KP extends string>(this: KeyValueDiffer<KP, V>, object: Record<KP, V>): @@ -59,50 +41,33 @@ export interface KeyValueDiffer<K, V> { * An object describing the changes in the `Map` or `{[k:string]: string}` since last time * `KeyValueDiffer#diff()` was invoked. * - * 一个对象,描述自上次调用 `KeyValueDiffer#diff()` 以来的变化的 `Map` 或 `{[k:string]: string}`。 - * * @publicApi */ export interface KeyValueChanges<K, V> { /** * Iterate over all changes. `KeyValueChangeRecord` will contain information about changes * to each item. - * - * 遍历所有更改。`KeyValueChangeRecord` 将包含有关每个条目更改的信息。 - * */ forEachItem(fn: (r: KeyValueChangeRecord<K, V>) => void): void; /** * Iterate over changes in the order of original Map showing where the original items * have moved. - * - * 按照原始映射表中的顺序遍历更改,以显示原始条目移动过的位置。 - * */ forEachPreviousItem(fn: (r: KeyValueChangeRecord<K, V>) => void): void; /** * Iterate over all keys for which values have changed. - * - * 遍历所有更改了值的键名。 - * */ forEachChangedItem(fn: (r: KeyValueChangeRecord<K, V>) => void): void; /** * Iterate over all added items. - * - * 遍历所有已添加的条目。 - * */ forEachAddedItem(fn: (r: KeyValueChangeRecord<K, V>) => void): void; /** * Iterate over all removed items. - * - * 遍历所有已删除的条目。 - * */ forEachRemovedItem(fn: (r: KeyValueChangeRecord<K, V>) => void): void; } @@ -110,32 +75,21 @@ export interface KeyValueChanges<K, V> { /** * Record representing the item change information. * - * 代表条目变更信息的记录。 - * * @publicApi */ export interface KeyValueChangeRecord<K, V> { /** * Current key in the Map. - * - * 地图中的当前键名。 - * */ readonly key: K; /** * Current value for the key or `null` if removed. - * - * 键名的当前值;如果已删除,则为 `null` - * */ readonly currentValue: V|null; /** * Previous value for the key or `null` if added. - * - * 键名的先前值;如果是添加的,则为 `null` - * */ readonly previousValue: V|null; } @@ -143,48 +97,36 @@ export interface KeyValueChangeRecord<K, V> { /** * Provides a factory for {@link KeyValueDiffer}. * - * 提供 {@link KeyValueDiffer} 的工厂。 - * * @publicApi */ export interface KeyValueDifferFactory { /** * Test to see if the differ knows how to diff this kind of object. - * - * 测试看此差分器是否知道如何区分这种对象。 - * */ supports(objects: any): boolean; /** * Create a `KeyValueDiffer`. - * - * 创建一个 `KeyValueDiffer` 。 - * */ create<K, V>(): KeyValueDiffer<K, V>; } +export function defaultKeyValueDiffersFactory() { + return new KeyValueDiffers([new DefaultKeyValueDifferFactory()]); +} + /** * A repository of different Map diffing strategies used by NgClass, NgStyle, and others. * - * NgClass、NgStyle 等使用的不同映射表差异策略的存储库。 - * * @publicApi */ export class KeyValueDiffers { /** @nocollapse */ - static ɵprov = ɵɵdefineInjectable({ - token: KeyValueDiffers, - providedIn: 'root', - factory: () => new KeyValueDiffers([new DefaultKeyValueDifferFactory()]) - }); + static ɵprov = /** @pureOrBreakMyCode */ ɵɵdefineInjectable( + {token: KeyValueDiffers, providedIn: 'root', factory: defaultKeyValueDiffersFactory}); /** * @deprecated v4.0.0 - Should be private. - * - * v4.0.0-应该是私有的。 - * */ factories: KeyValueDifferFactory[]; @@ -205,20 +147,13 @@ export class KeyValueDiffers { * inherited {@link KeyValueDiffers} instance with the provided factories and return a new * {@link KeyValueDiffers} instance. * - * 接受 {@link KeyValueDifferFactory} 的数组,并返回一个提供者,用于使用提供的工厂扩展所继承的 {@link KeyValueDiffers} 实例,并返回一个新的 {@link KeyValueDiffers} 实例。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * The following example shows how to extend an existing list of factories, * which will only be applied to the injector for this component and its children. * This step is all that's required to make a new {@link KeyValueDiffer} available. * - * 以下示例显示如何扩展现有工厂列表,该列表仅适用于该组件及其子组件的注入器。这是使新的{@link KeyValueDiffer}可用的全部步骤。 - * * ``` * @Component({ * viewProviders: [ @@ -231,12 +166,10 @@ export class KeyValueDiffers { return { provide: KeyValueDiffers, useFactory: (parent: KeyValueDiffers) => { - if (!parent) { - // Typically would occur when calling KeyValueDiffers.extend inside of dependencies passed - // to bootstrap(), which would override default pipes instead of extending them. - throw new Error('Cannot extend KeyValueDiffers without a parent injector'); - } - return KeyValueDiffers.create(factories, parent); + // if parent is null, it means that we are in the root injector and we have just overridden + // the default injection mechanism for KeyValueDiffers, in such a case just assume + // `defaultKeyValueDiffersFactory`. + return KeyValueDiffers.create(factories, parent || defaultKeyValueDiffersFactory()); }, // Dependency technically isn't optional, but we can provide a better error message this way. deps: [[KeyValueDiffers, new SkipSelf(), new Optional()]] diff --git a/packages/core/src/change_detection/pipe_transform.ts b/packages/core/src/change_detection/pipe_transform.ts index 0d98f66350..44e55c2669 100644 --- a/packages/core/src/change_detection/pipe_transform.ts +++ b/packages/core/src/change_detection/pipe_transform.ts @@ -11,29 +11,21 @@ * Angular invokes the `transform` method with the value of a binding * as the first argument, and any parameters as the second argument in list form. * - * 一个需要由管道实现的接口,用于执行转换操作。 - * Angular 会调用它的 `transform` 方法,并把要绑定的值作为第一个参数传入,其它参数会依次从第二个参数的位置开始传入。 - * * @usageNotes * - * In the following example, `RepeatPipe` repeats a given value a given number of times. + * In the following example, `TruncatePipe` returns the shortened value with an added ellipses. * - * 在下面的例子中,`RepeatPipe` 会把指定的值(`value`)重复指定的次数(`times`): + * <code-example path="core/ts/pipes/simple_truncate.ts" header="simple_truncate.ts"></code-example> * - * ```ts - * import {Pipe, PipeTransform} from '@angular/core'; + * Invoking `{{ 'It was the best of times' | truncate }}` in a template will produce `It was...`. * - * @Pipe({name: 'repeat'}) - * export class RepeatPipe implements PipeTransform { - * transform(value: any, times: number) { - * return value.repeat(times); - * } - * } - * ``` + * In the following example, `TruncatePipe` takes parameters that sets the truncated length and the + * string to append with. * - * Invoking `{{ 'ok' | repeat:3 }}` in a template produces `okokok`. + * <code-example path="core/ts/pipes/truncate.ts" header="truncate.ts"></code-example> * - * 在模板中调用 `{{ 'ok' | repeat:3 }}` 的结果是 `okokok`。 + * Invoking `{{ 'It was the best of times' | truncate:4:'....' }}` in a template will produce `It + * was the best....`. * * @publicApi */ diff --git a/packages/core/src/compiler/compiler_facade_interface.ts b/packages/core/src/compiler/compiler_facade_interface.ts index d60b8be014..5343e4583c 100644 --- a/packages/core/src/compiler/compiler_facade_interface.ts +++ b/packages/core/src/compiler/compiler_facade_interface.ts @@ -29,23 +29,38 @@ export interface ExportedCompilerFacade { export interface CompilerFacade { compilePipe(angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3PipeMetadataFacade): any; + compilePipeDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, declaration: R3DeclarePipeFacade): any; compileInjectable( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3InjectableMetadataFacade): any; compileInjector( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3InjectorMetadataFacade): any; + compileInjectorDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, + declaration: R3DeclareInjectorFacade): any; compileNgModule( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3NgModuleMetadataFacade): any; + compileNgModuleDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, + declaration: R3DeclareNgModuleFacade): any; compileDirective( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3DirectiveMetadataFacade): any; + compileDirectiveDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, + declaration: R3DeclareDirectiveFacade): any; compileComponent( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3ComponentMetadataFacade): any; + compileComponentDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, + declaration: R3DeclareComponentFacade): any; compileFactory( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3FactoryDefMetadataFacade): any; + compileFactoryDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3DeclareFactoryFacade): any; createParseSourceSpan(kind: string, typeName: string, sourceUrl: string): ParseSourceSpan; - R3ResolvedDependencyType: typeof R3ResolvedDependencyType; - R3FactoryTarget: typeof R3FactoryTarget; + FactoryTarget: typeof FactoryTarget; ResourceLoader: {new(): ResourceLoader}; } @@ -67,14 +82,7 @@ export type StringMapWithRename = { export type Provider = any; -export enum R3ResolvedDependencyType { - Token = 0, - Attribute = 1, - ChangeDetectorRef = 2, - Invalid = 3, -} - -export enum R3FactoryTarget { +export enum FactoryTarget { Directive = 0, Component = 1, Injectable = 2, @@ -83,20 +91,27 @@ export enum R3FactoryTarget { } export interface R3DependencyMetadataFacade { - token: any; - resolved: R3ResolvedDependencyType; + token: unknown; + attribute: string|null; host: boolean; optional: boolean; self: boolean; skipSelf: boolean; } +export interface R3DeclareDependencyMetadataFacade { + token: unknown; + attribute?: boolean; + host?: boolean; + optional?: boolean; + self?: boolean; + skipSelf?: boolean; +} + export interface R3PipeMetadataFacade { name: string; type: any; - typeArgumentCount: number; pipeName: string; - deps: R3DependencyMetadataFacade[]|null; pure: boolean; } @@ -125,7 +140,6 @@ export interface R3NgModuleMetadataFacade { export interface R3InjectorMetadataFacade { name: string; type: any; - deps: R3DependencyMetadataFacade[]|null; providers: any[]; imports: any[]; } @@ -133,9 +147,7 @@ export interface R3InjectorMetadataFacade { export interface R3DirectiveMetadataFacade { name: string; type: any; - typeArgumentCount: number; typeSourceSpan: ParseSourceSpan; - deps: R3DependencyMetadataFacade[]|null; selector: string|null; queries: R3QueryMetadataFacade[]; host: {[key: string]: string}; @@ -162,6 +174,51 @@ export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade { changeDetection?: ChangeDetectionStrategy; } +export type OpaqueValue = unknown; + +export interface R3DeclareDirectiveFacade { + selector?: string; + type: Function; + inputs?: {[classPropertyName: string]: string|[string, string]}; + outputs?: {[classPropertyName: string]: string}; + host?: { + attributes?: {[key: string]: OpaqueValue}; + listeners?: {[key: string]: string}; + properties?: {[key: string]: string}; + classAttribute?: string; + styleAttribute?: string; + }; + queries?: R3DeclareQueryMetadataFacade[]; + viewQueries?: R3DeclareQueryMetadataFacade[]; + providers?: OpaqueValue; + exportAs?: string[]; + usesInheritance?: boolean; + usesOnChanges?: boolean; +} + +export interface R3DeclareComponentFacade extends R3DeclareDirectiveFacade { + template: string; + isInline?: boolean; + styles?: string[]; + components?: R3DeclareUsedDirectiveFacade[]; + directives?: R3DeclareUsedDirectiveFacade[]; + pipes?: {[pipeName: string]: OpaqueValue|(() => OpaqueValue)}; + viewProviders?: OpaqueValue; + animations?: OpaqueValue; + changeDetection?: ChangeDetectionStrategy; + encapsulation?: ViewEncapsulation; + interpolation?: [string, string]; + preserveWhitespaces?: boolean; +} + +export interface R3DeclareUsedDirectiveFacade { + selector: string; + type: OpaqueValue|(() => OpaqueValue); + inputs?: string[]; + outputs?: string[]; + exportAs?: string[]; +} + export interface R3UsedDirectiveMetadata { selector: string; inputs: string[]; @@ -175,8 +232,13 @@ export interface R3FactoryDefMetadataFacade { type: any; typeArgumentCount: number; deps: R3DependencyMetadataFacade[]|null; - injectFn: 'directiveInject'|'inject'; - target: R3FactoryTarget; + target: FactoryTarget; +} + +export interface R3DeclareFactoryFacade { + type: Function; + deps: R3DeclareDependencyMetadataFacade[]|null; + target: FactoryTarget; } export enum ViewEncapsulation { @@ -193,10 +255,43 @@ export interface R3QueryMetadataFacade { first: boolean; predicate: any|string[]; descendants: boolean; + emitDistinctChangesOnly: boolean; read: any|null; static: boolean; } +export interface R3DeclareQueryMetadataFacade { + propertyName: string; + first?: boolean; + predicate: OpaqueValue|string[]; + descendants?: boolean; + read?: OpaqueValue; + static?: boolean; + emitDistinctChangesOnly?: boolean; +} + +export interface R3DeclareInjectorFacade { + type: Function; + imports?: OpaqueValue[]; + providers?: OpaqueValue[]; +} + +export interface R3DeclareNgModuleFacade { + type: Function; + bootstrap?: OpaqueValue[]|(() => OpaqueValue[]); + declarations?: OpaqueValue[]|(() => OpaqueValue[]); + imports?: OpaqueValue[]|(() => OpaqueValue[]); + exports?: OpaqueValue[]|(() => OpaqueValue[]); + schemas?: OpaqueValue[]; + id?: OpaqueValue; +} + +export interface R3DeclarePipeFacade { + type: Function; + name: string; + pure?: boolean; +} + export interface ParseSourceSpan { start: any; end: any; diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts index e87609da44..bae931b12c 100644 --- a/packages/core/src/core_render3_private_export.ts +++ b/packages/core/src/core_render3_private_export.ts @@ -56,9 +56,6 @@ export { 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 { - ɵɵNgModuleDefWithMeta, -} from './metadata/ng_module'; export { SWITCH_COMPILE_NGMODULE__POST_R3__ as ɵSWITCH_COMPILE_NGMODULE__POST_R3__, } from './metadata/ng_module'; @@ -126,14 +123,14 @@ export { ɵɵclassMapInterpolate8, ɵɵclassMapInterpolateV, ɵɵclassProp, - ɵɵComponentDefWithMeta, + ɵɵComponentDeclaration, ɵɵcontentQuery, ɵɵCopyDefinitionFeature, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdefineNgModule, ɵɵdefinePipe, - ɵɵDirectiveDefWithMeta, + ɵɵDirectiveDeclaration, ɵɵdirectiveInject, ɵɵdisableBindings, ɵɵelement, @@ -143,9 +140,8 @@ export { ɵɵelementEnd, ɵɵelementStart, ɵɵenableBindings, - ɵɵFactoryDef, + ɵɵFactoryDeclaration, ɵɵgetCurrentView, - ɵɵgetFactoryOf, ɵɵgetInheritedFactory, ɵɵhostProperty, ɵɵi18n, @@ -157,7 +153,7 @@ export { ɵɵi18nStart, ɵɵInheritDefinitionFeature, ɵɵinjectAttribute, - ɵɵinjectPipeChangeDetectorRef, + ɵɵInjectorDeclaration, ɵɵinvalidFactory, ɵɵlistener, ɵɵloadQuery, @@ -165,8 +161,7 @@ export { ɵɵnamespaceMathML, ɵɵnamespaceSVG, ɵɵnextContext, - ɵɵngDeclareComponent, - ɵɵngDeclareDirective, + ɵɵNgModuleDeclaration, ɵɵNgOnChangesFeature, ɵɵpipe, ɵɵpipeBind1, @@ -174,7 +169,7 @@ export { ɵɵpipeBind3, ɵɵpipeBind4, ɵɵpipeBindV, - ɵɵPipeDefWithMeta, + ɵɵPipeDeclaration, ɵɵprojection, ɵɵprojectionDef, ɵɵproperty, @@ -208,8 +203,6 @@ export { ɵɵsetComponentScope, ɵɵsetNgModuleScope, - ɵɵstaticContentQuery, - ɵɵstaticViewQuery, ɵɵstyleMap, ɵɵstyleMapInterpolate1, ɵɵstyleMapInterpolate2, @@ -274,9 +267,19 @@ export { resetCompiledComponents as ɵresetCompiledComponents, transitiveScopesFor as ɵtransitiveScopesFor, } from './render3/jit/module'; +export { + FactoryTarget as ɵɵFactoryTarget, + ɵɵngDeclareComponent, + ɵɵngDeclareDirective, + ɵɵngDeclareFactory, + ɵɵngDeclareInjector, + ɵɵngDeclareNgModule, + ɵɵngDeclarePipe, +} from './render3/jit/partial'; export { compilePipe as ɵcompilePipe, } from './render3/jit/pipe'; +export { Profiler as ɵProfiler, ProfilerEvent as ɵProfilerEvent } from './render3/profiler'; export { publishDefaultGlobalUtils as ɵpublishDefaultGlobalUtils , diff --git a/packages/core/src/debug/debug_node.ts b/packages/core/src/debug/debug_node.ts index 7bd555df8b..1fb53711a0 100644 --- a/packages/core/src/debug/debug_node.ts +++ b/packages/core/src/debug/debug_node.ts @@ -776,8 +776,6 @@ export function removeDebugNodeFromIndex(node: DebugNode) { * A boolean-valued function over a value, possibly including context information * regarding that value's position in an array. * - * 根据参数值返回布尔值的函数,可能包括该值在数组中位置的上下文信息。 - * * @publicApi */ export interface Predicate<T> { diff --git a/packages/core/src/di/forward_ref.ts b/packages/core/src/di/forward_ref.ts index 7cfce888c1..80250986e0 100644 --- a/packages/core/src/di/forward_ref.ts +++ b/packages/core/src/di/forward_ref.ts @@ -15,14 +15,9 @@ import {stringify} from '../util/stringify'; /** * An interface that a function passed into {@link forwardRef} has to implement. * - * 要传给 {@link forwardRef} 的函数时必须实现的接口。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref_fn'} * @publicApi */ @@ -35,20 +30,12 @@ const __forward_ref__ = getClosureSafeProperty({__forward_ref__: getClosureSafeP /** * Allows to refer to references which are not yet defined. * - * 允许引用尚未定义的引用。 - * * For instance, `forwardRef` is used when the `token` which we need to refer to for the purposes of * DI is declared, but not yet defined. It is also used when the `token` which we use when creating * a query is not yet defined. * - * 例如,当我们需要为所声明的 DI 而引用此 `token`,但尚未定义该令牌时,将使用 `forwardRef`。当我们创建尚未定义的查询的 `token` 时,也会使用它。 - * * @usageNotes - * * ### Example - * - * ### 例子 - * * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref'} * @publicApi */ @@ -63,18 +50,11 @@ export function forwardRef(forwardRefFn: ForwardRefFn): Type<any> { /** * Lazily retrieves the reference value from a forwardRef. * - * 从 forwardRef 惰性检索引用值。 - * * Acts as the identity function when given a non-forward-ref value. * - * 给定非前向引用值时,充当标识函数。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='resolve_forward_ref'} * * @see `forwardRef` diff --git a/packages/core/src/di/inject_switch.ts b/packages/core/src/di/inject_switch.ts index 9614aa2ec1..531481a18c 100644 --- a/packages/core/src/di/inject_switch.ts +++ b/packages/core/src/di/inject_switch.ts @@ -7,6 +7,7 @@ */ import {AbstractType, Type} from '../interface/type'; +import {throwProviderNotFoundError} from '../render3/errors_di'; import {assertNotEqual} from '../util/assert'; import {stringify} from '../util/stringify'; import {InjectionToken} from './injection_token'; @@ -62,7 +63,7 @@ export function injectRootLimpMode<T>( } if (flags & InjectFlags.Optional) return null; if (notFoundValue !== undefined) return notFoundValue; - throw new Error(`Injector: NOT_FOUND [${stringify(token)}]`); + throwProviderNotFoundError(stringify(token), 'Injector'); } diff --git a/packages/core/src/di/injectable.ts b/packages/core/src/di/injectable.ts index bd62d91eff..7b1adf97c3 100644 --- a/packages/core/src/di/injectable.ts +++ b/packages/core/src/di/injectable.ts @@ -19,8 +19,6 @@ import {convertInjectableProviderToFactory} from './util'; /** * Injectable providers used in `@Injectable` decorator. * - * `@Injectable` 装饰器中使用的可注入对象提供者。 - * * @publicApi */ export type InjectableProvider = ValueSansProvider|ExistingSansProvider|StaticClassSansProvider| @@ -29,8 +27,6 @@ export type InjectableProvider = ValueSansProvider|ExistingSansProvider|StaticCl /** * Type of the Injectable decorator / constructor function. * - * Injectable 装饰器的类型和构造函数 - * * @publicApi */ export interface InjectableDecorator { @@ -38,29 +34,18 @@ export interface InjectableDecorator { * Decorator that marks a class as available to be * provided and injected as a dependency. * - * 标记性元数据,表示一个类可以由 `Injector` 进行创建。 - * * @see [Introduction to Services and DI](guide/architecture-services) - * - * [服务和 DI 简介](guide/architecture-services) - * * @see [Dependency Injection Guide](guide/dependency-injection) * - * [依赖注入指南](guide/dependency-injection) - * * @usageNotes * * Marking a class with `@Injectable` ensures that the compiler * will generate the necessary metadata to create the class's * dependencies when the class is injected. * - * 使用 `@Injectable` 标记一个类可确保编译器将在注入类时生成必要的元数据,以创建类的依赖项。 - * * The following example shows how a service class is properly * marked so that a supporting service can be injected upon creation. * - * 下面的例子展示了如何正确的把服务类标记为可注入的(Injectable)。 - * * <code-example path="core/di/ts/metadata_spec.ts" region="Injectable"></code-example> * */ @@ -75,8 +60,6 @@ export interface InjectableDecorator { /** * Type of the Injectable metadata. * - * Injectable 元数据的类型。 - * * @publicApi */ export interface Injectable { @@ -84,22 +67,12 @@ export interface Injectable { * Determines which injectors will provide the injectable, * by either associating it with an `@NgModule` or other `InjectorType`, * or by specifying that this injectable should be provided in one of the following injectors: - * - * 通过与 `@NgModule` 或其他 `InjectorType` 关联,或通过指定应在以下注入器之一中提供此可注入对象,来确定将提供该对象的注入器: - * * - 'root' : The application-level injector in most apps. - * - * 'root':在大多数应用程序中是指应用程序级注入器。 - * * - 'platform' : A special singleton platform injector shared by all * applications on the page. - * - * 'platform' :页面上所有应用程序共享的平台注入器的特殊单例。 - * * - 'any' : Provides a unique instance in each lazy loaded module while all eagerly loaded * modules share one instance. * - * 'any':在每个惰性加载的模块中提供一个唯一的实例,而所有急性加载的模块共享一个实例。 */ providedIn?: Type<any>|'root'|'platform'|'any'|null; } @@ -107,9 +80,7 @@ export interface Injectable { /** * Injectable decorator and metadata. * - * Injectable 的装饰器和元数据。 -* -* @Annotation + * @Annotation * @publicApi */ export const Injectable: InjectableDecorator = makeDecorator( diff --git a/packages/core/src/di/injection_token.ts b/packages/core/src/di/injection_token.ts index 3239e558a1..90386f2b1e 100644 --- a/packages/core/src/di/injection_token.ts +++ b/packages/core/src/di/injection_token.ts @@ -14,19 +14,13 @@ import {ɵɵdefineInjectable} from './interface/defs'; /** * Creates a token that can be used in a DI Provider. * - * 创建可用于 DI 提供者的令牌。 - * * Use an `InjectionToken` whenever the type you are injecting is not reified (does not have a * runtime representation) such as when injecting an interface, callable type, array or * parameterized type. * - * 每当你要注入的类型无法确定(没有运行时表示形式)时,例如在注入接口、可调用类型、数组或参数化类型时,都应使用 `InjectionToken`。 - * * `InjectionToken` is parameterized on `T` which is the type of object which will be returned by * the `Injector`. This provides additional level of type safety. * - * `InjectionToken` 在 `T` 上的参数化版本,`T` 是 `Injector` 返回的对象的类型。这提供了更高级别的类型安全性。 - * * ``` * interface MyInterface {...} * var myInterface = injector.get(new InjectionToken<MyInterface>('SomeToken')); @@ -39,24 +33,15 @@ import {ɵɵdefineInjectable} from './interface/defs'; * application's root injector. If the factory function, which takes zero arguments, needs to inject * dependencies, it can do so using the `inject` function. See below for an example. * - * 当创建 `InjectionToken` 时,可以选择指定一个工厂函数,该函数返回(可能通过创建)参数化类型 `T` 的默认值。这将使用工厂型提供者设置 `InjectionToken`,就像它是在应用程序的根注入器中显式定义的一样。如果使用需要注入依赖项的零参数工厂函数,则可以使用 `inject` 函数来这样做。参见以下示例。 - * * Additionally, if a `factory` is specified you can also specify the `providedIn` option, which * overrides the above behavior and marks the token as belonging to a particular `@NgModule`. As * mentioned above, `'root'` is the default value for `providedIn`. * - * 此外,如果指定了 `factory`,也可以指定 `providedIn` 选项,它会覆盖上述行为,并把这些令牌标记为属于特定 `@NgModule`。如上所述,`'root'` 是 `providedIn` 的默认值。 - * * @usageNotes - * * ### Basic Example * - * ### 基本范例 - * * ### Plain InjectionToken * - * ### 普通注入令牌 - * * {@example core/di/ts/injector_spec.ts region='InjectionToken'} * * ### Tree-shakable InjectionToken @@ -70,7 +55,7 @@ export class InjectionToken<T> { /** @internal */ readonly ngMetadataName = 'InjectionToken'; - readonly ɵprov: never|undefined; + readonly ɵprov: unknown; constructor(protected _desc: string, options?: { providedIn?: Type<any>|'root'|'platform'|'any'|null, factory: () => T @@ -97,5 +82,5 @@ export class InjectionToken<T> { } export interface InjectableDefToken<T> extends InjectionToken<T> { - ɵprov: never; + ɵprov: unknown; } diff --git a/packages/core/src/di/injector.ts b/packages/core/src/di/injector.ts index 9b691f4c4d..eff4badb3d 100644 --- a/packages/core/src/di/injector.ts +++ b/packages/core/src/di/injector.ts @@ -38,26 +38,17 @@ export const INJECTOR_IMPL = INJECTOR_IMPL__PRE_R3__; * with [providers](guide/glossary#provider) that associate * dependencies of various types with [injection tokens](guide/glossary#di-token). * - * 具体的注入器会实现此接口。配置有[某些提供者](guide/glossary#provider)的注入器,这些提供者会将各种类型的依赖项与[注入令牌](guide/glossary#di-token)相关联。 - * * @see ["DI Providers"](guide/dependency-injection-providers). - * - * [“DI 提供者”](guide/dependency-injection-providers) 。 - * * @see `StaticProvider` * * @usageNotes * * The following example creates a service injector instance. * - * 以下示例创建一个服务注入器实例。 - * * {@example core/di/ts/provider_spec.ts region='ConstructorProvider'} * * ### Usage example * - * ### 使用范例 - * * {@example core/di/ts/injector_spec.ts region='Injector'} * * `Injector` returns itself when given `Injector` as a token: @@ -72,34 +63,19 @@ export abstract class Injector { /** * Retrieves an instance from the injector based on the provided token. - * - * 根据提供的令牌从注入器中检索实例。 - * * @returns The instance from the injector if defined, otherwise the `notFoundValue`. - * - * 注入器的实例(如果已定义),否则为 `notFoundValue` 。 - * * @throws When the `notFoundValue` is `undefined` or `Injector.THROW_IF_NOT_FOUND`. - * - * 当 `notFoundValue` 为 `undefined` 或 `Injector.THROW_IF_NOT_FOUND` 时。 - * */ abstract get<T>( token: Type<T>|AbstractType<T>|InjectionToken<T>, notFoundValue?: T, flags?: InjectFlags): T; /** * @deprecated from v4.0.0 use Type<T>, AbstractType<T> or InjectionToken<T> - * - * 从 v4.0.0 开始,改用 Type<T>、AbstractType<T> 或 InjectionToken<T> - * * @suppress {duplicate} */ abstract get(token: any, notFoundValue?: any): any; /** * @deprecated from v5 use the new signature Injector.create(options) - * - * 从 v5 开始使用新的签名 Injector.create(options) - * */ static create(providers: StaticProvider[], parent?: Injector): Injector; @@ -107,27 +83,13 @@ export abstract class Injector { * Creates a new injector instance that provides one or more dependencies, * according to a given type or types of `StaticProvider`. * - * 创建一个新的注入器实例,该实例会根据指定的类型或 `StaticProvider` 的类型提供一个或多个依赖项。 - * * @param options An object with the following properties: - * - * 具有以下属性的对象: - * * * `providers`: An array of providers of the [StaticProvider type](api/core/StaticProvider). - * - * `providers` :一组 [StaticProvider 类型](api/core/StaticProvider)的提供者。 - * * * `parent`: (optional) A parent injector. - * - * `parent` :(可选)父注入器。 - * - * - `name`: (optional) A developer-defined identifying name for the new injector. - * - * `name` :(可选)新注入器的开发人员自定义的标识名称。 + * * `name`: (optional) A developer-defined identifying name for the new injector. * * @returns The new injector instance. * - * 新的注入器实例。 */ static create(options: {providers: StaticProvider[], parent?: Injector, name?: string}): Injector; @@ -143,7 +105,7 @@ export abstract class Injector { } /** @nocollapse */ - static ɵprov = ɵɵdefineInjectable({ + static ɵprov = /** @pureOrBreakMyCode */ ɵɵdefineInjectable({ token: Injector, providedIn: 'any' as any, factory: () => ɵɵinject(INJECTOR), diff --git a/packages/core/src/di/injector_compatibility.ts b/packages/core/src/di/injector_compatibility.ts index ca81293443..402621aef1 100644 --- a/packages/core/src/di/injector_compatibility.ts +++ b/packages/core/src/di/injector_compatibility.ts @@ -11,18 +11,25 @@ import '../util/ng_dev_mode'; import {AbstractType, Type} from '../interface/type'; import {getClosureSafeProperty} from '../util/property'; import {stringify} from '../util/stringify'; + import {resolveForwardRef} from './forward_ref'; import {getInjectImplementation, injectRootLimpMode} from './inject_switch'; import {InjectionToken} from './injection_token'; import {Injector} from './injector'; -import {InjectFlags} from './interface/injector'; +import {DecoratorFlags, InjectFlags, InternalInjectFlags} from './interface/injector'; import {ValueProvider} from './interface/provider'; -import {Inject, Optional, Self, SkipSelf} from './metadata'; const _THROW_IF_NOT_FOUND = {}; export const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND; +/* + * Name of a property (that we patch onto DI decorator), which is used as an annotation of which + * InjectFlag this decorator represents. This allows to avoid direct references to the DI decorators + * in the code, thus making them tree-shakable. + */ +const DI_DECORATOR_FLAG = '__NG_DI_FLAG__'; + export const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath'; const NG_TOKEN_PATH = 'ngTokenPath'; const NEW_LINE = /\n/gm; @@ -63,22 +70,13 @@ export function injectInjectorOnly<T>( /** * Generated instruction: Injects a token from the currently active injector. * - * 生成的方式:从当前活动的注入器注入令牌。 - * * 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. * - * 必须在工厂函数的上下文中使用,比如为 `InjectionToken` 定义的函数。如果未从这样的上下文中调用,则会引发错误。 - * * (Additional documentation moved to `inject`, as it is the public API, and an alias for this * instruction) * - * (其他文档移到了 `inject` ,因为它是公共 API,并且是此指令的别名) - * * @see inject - * - * 注入 - * * @codeGenApi * @publicApi This instruction has been emitted by ViewEngine for some time and is deployed to npm. */ @@ -117,39 +115,23 @@ Please check that 1) the type for the parameter at index ${ /** * Injects a token from the currently active injector. * - * 从当前活动的注入器中注入令牌。 - * * 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. * - * 必须在工厂函数的上下文中使用,比如为 `InjectionToken` 定义的函数。如果未从这样的上下文中调用,则会引发错误。 - * * Within such a factory function, using this function to request injection of a dependency * is faster and more type-safe than providing an additional array of dependencies * (as has been common with `useFactory` providers). * - * 在这样的工厂函数中,使用此函数来请求注入依赖项比提供额外的依赖项数组(在 `useFactory` 提供者中这很常见)要更快且类型安全性更高。 - * * @param token The injection token for the dependency to be injected. - * - * 用于注入依赖项的注入令牌。 - * * @param flags Optional flags that control how injection is executed. * The flags correspond to injection strategies that can be specified with * parameter decorators `@Host`, `@Self`, `@SkipSef`, and `@Optional`. - * - * 控制执行注入方式的可选标志。这些标志对应于可以使用参数装饰器 `@Host`、`@Self`、`@SkipSef` 和 `@Optional` 指定的注入策略。 - * - * @returns True if injection is successful, null otherwise. - * - * 如果注入成功,则为 true,否则为 null。 + * @returns the injected value if injection is successful, `null` otherwise. * * @usageNotes * * ### Example * - * ### 例子 - * * {@example core/di/ts/injector_spec.ts region='ShakableInjectionToken'} * * @publicApi @@ -169,15 +151,14 @@ export function injectArgs(types: (Type<any>|InjectionToken<any>|any[])[]): any[ for (let j = 0; j < arg.length; j++) { const meta = arg[j]; - if (meta instanceof Optional || meta.ngMetadataName === 'Optional' || meta === Optional) { - flags |= InjectFlags.Optional; - } else if ( - meta instanceof SkipSelf || meta.ngMetadataName === 'SkipSelf' || meta === SkipSelf) { - flags |= InjectFlags.SkipSelf; - } else if (meta instanceof Self || meta.ngMetadataName === 'Self' || meta === Self) { - flags |= InjectFlags.Self; - } else if (meta instanceof Inject || meta === Inject) { - type = meta.token; + const flag = getInjectFlag(meta); + if (typeof flag === 'number') { + // Special case when we handle @Inject decorator. + if (flag === DecoratorFlags.Inject) { + type = meta.token; + } else { + flags |= flag; + } } else { type = meta; } @@ -191,6 +172,30 @@ export function injectArgs(types: (Type<any>|InjectionToken<any>|any[])[]): any[ return args; } +/** + * Attaches a given InjectFlag to a given decorator using monkey-patching. + * Since DI decorators can be used in providers `deps` array (when provider is configured using + * `useFactory`) without initialization (e.g. `Host`) and as an instance (e.g. `new Host()`), we + * attach the flag to make it available both as a static property and as a field on decorator + * instance. + * + * @param decorator Provided DI decorator. + * @param flag InjectFlag that should be applied. + */ +export function attachInjectFlag(decorator: any, flag: InternalInjectFlags|DecoratorFlags): any { + decorator[DI_DECORATOR_FLAG] = flag; + decorator.prototype[DI_DECORATOR_FLAG] = flag; + return decorator; +} + +/** + * Reads monkey-patched property that contains InjectFlag attached to a decorator. + * + * @param token Token that may contain monkey-patched DI flags property. + */ +export function getInjectFlag(token: any): number|undefined { + return token[DI_DECORATOR_FLAG]; +} export function catchInjectorError( e: any, token: any, injectorErrorName: string, source: string|null): never { diff --git a/packages/core/src/di/injector_token.ts b/packages/core/src/di/injector_token.ts index a6bca8392c..b84fc4a261 100644 --- a/packages/core/src/di/injector_token.ts +++ b/packages/core/src/di/injector_token.ts @@ -15,13 +15,9 @@ import {InjectorMarkers} from './injector_marker'; /** * An InjectionToken that gets the current `Injector` for `createInjector()`-style injectors. * - * 一个 InjectionToken,用于获取当前 `Injector` 的 `createInjector()` 式的注入器。 - * * Requesting this token instead of `Injector` allows `StaticInjector` to be tree-shaken from a * project. * - * 请求此令牌而不是 `Injector` 可使 `StaticInjector` 能在项目中摇树优化掉。 - * * @publicApi */ export const INJECTOR = new InjectionToken<Injector>( diff --git a/packages/core/src/di/interface/defs.ts b/packages/core/src/di/interface/defs.ts index fbccd784f0..9c4395aff2 100644 --- a/packages/core/src/di/interface/defs.ts +++ b/packages/core/src/di/interface/defs.ts @@ -15,19 +15,13 @@ import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, S /** * Information about how a type or `InjectionToken` interfaces with the DI system. * - * 有关类型或 `InjectionToken` 如何接入 DI 体系的接口信息。 - * * At a minimum, this includes a `factory` which defines how to create the given type `T`, possibly * requesting injection of other types if necessary. * - * 至少,这要包括一个 `factory` ,该工厂定义如何创建给定类型 `T` ,如有必要,可能会请求注入其他类型。 - * * Optionally, a `providedIn` parameter specifies that the given type belongs to a particular - * `InjectorDef`, `NgModule`, or a special scope (e.g. `'root'`). A value of `null` indicates + * `Injector`, `NgModule`, or a special scope (e.g. `'root'`). A value of `null` indicates * that the injectable does not belong to any scope. * - * 可选的参数 `providedIn` 规定给定的类型属于某个特定的 `InjectorDef`、`NgModule` 还是一个特殊的范围(例如 `'root'`)。如果值为 `null` 表示可注入对象不属于任何范围。 - * * @codeGenApi * @publicApi The ViewEngine compiler emits code with this type for injectables. This code is * deployed to npm, and should be treated as public api. @@ -36,54 +30,28 @@ import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, S export interface ɵɵInjectableDef<T> { /** * Specifies that the given type belongs to a particular injector: - * - * 指定给定类型属于特定的注入器: - * * - `InjectorType` such as `NgModule`, - * - * `InjectorType` 例如 `NgModule` , - * * - `'root'` the root injector - * - * `'root'` 根注入器 - * * - `'any'` all injectors. - * - * `'any'` 所有注入器。 - * * - `null`, does not belong to any injector. Must be explicitly listed in the injector * `providers`. - * - * `null` ,不属于任何注入器。必须在注入器的 `providers` 列表中显式列出。 - * */ providedIn: InjectorType<any>|'root'|'platform'|'any'|null; /** * The token to which this definition belongs. * - * 此定义所属的令牌。 - * * Note that this may not be the same as the type that the `factory` will create. - * - * 请注意,这可能与 `factory` 将创建的类型不同。 - * */ token: unknown; /** * Factory method to execute to create an instance of the injectable. - * - * 本工厂方法用于创建可注入实例。 - * */ factory: (t?: Type<any>) => T; /** * In a case of no explicit injector, a location where the instance of the injectable is stored. - * - * 在没有显式注入器的情况下,存储可注入实例的位置。 - * */ value: T|undefined; } @@ -101,8 +69,6 @@ export interface ɵɵInjectableDef<T> { * @codeGenApi */ export interface ɵɵInjectorDef<T> { - factory: () => T; - // TODO(alxhub): Narrow down the type here once decorators properly change the return type of the // class they are decorating (to add the ɵprov property for example). providers: (Type<any>|ValueProvider|ExistingProvider|FactoryProvider|ConstructorProvider| @@ -114,48 +80,35 @@ export interface ɵɵInjectorDef<T> { /** * A `Type` which has an `InjectableDef` static field. * - * 具有 `InjectableDef` 静态字段的 `Type` - * - * `InjectableDefType`s contain their own Dependency Injection metadata and are usable in an + * `InjectableType`s contain their own Dependency Injection metadata and are usable in an * `InjectorDef`-based `StaticInjector. * - * `InjectableDefType` 包含其自己的依赖注入元数据,并且可在基于 `InjectorDef` 的 `StaticInjector` 中使用。 - * * @publicApi */ export interface InjectableType<T> extends Type<T> { /** * Opaque type whose structure is highly version dependent. Do not rely on any properties. - * - * 不透明类型,其结构高度依赖版本。不要依赖它的任何属性。 - * */ - ɵprov: never; + ɵprov: unknown; } /** * A type which has an `InjectorDef` static field. * - * 具有 `InjectorDef` 静态字段的类型。 + * `InjectorTypes` can be used to configure a `StaticInjector`. * - * `InjectorDefTypes` can be used to configure a `StaticInjector`. - * - * 可用于配置 `StaticInjector` 的 `InjectorDefTypes`。 + * This is an opaque type whose structure is highly version dependent. Do not rely on any + * properties. * * @publicApi */ export interface InjectorType<T> extends Type<T> { - /** - * Opaque type whose structure is highly version dependent. Do not rely on any properties. - * - * 不透明类型,其结构高度依赖版本。不要依赖它的任何属性。 - * - */ - ɵinj: never; + ɵfac?: unknown; + ɵinj: unknown; } /** - * Describes the `InjectorDef` equivalent of a `ModuleWithProviders`, an `InjectorDefType` with an + * Describes the `InjectorDef` equivalent of a `ModuleWithProviders`, an `InjectorType` with an * associated array of providers. * * Objects of this type can be listed in the imports section of an `InjectorDef`. @@ -173,49 +126,34 @@ export interface InjectorTypeWithProviders<T> { * Construct an `InjectableDef` which defines how a token will be constructed by the DI system, and * in which injectors (if any) it will be available. * - * 构造一个 `InjectableDef` ,它定义 DI 体系将如何构造令牌以及在哪些注入器中可用(如果有的话)。 - * * This should be assigned to a static `ɵprov` field on a type, which will then be an * `InjectableType`. * - * 应该将其赋值给静态的 `ɵprov` 字段,然后将其作为 `InjectableType` 。 - * * Options: - * - * 选项: - * * * `providedIn` determines which injectors will include the injectable, by either associating it * with an `@NgModule` or other `InjectorType`, or by specifying that this injectable should be * provided in the `'root'` injector, which will be the application-level injector in most apps. - * - * `providedIn` 决定哪些注入器应该包含此可注入对象:或者将其与 `@NgModule` 关联,或者将其与其他 `InjectorType` 关联,或者指定应该在 `'root'` 注入器(对大多数应用来说这是全应用级注入器)中提供它。 - * * * `factory` gives the zero argument function which will create an instance of the injectable. * The factory can call `inject` to access the `Injector` and request injection of dependencies. * - * `factory` 是一个零参数函数,该函数将创建可注入的实例。工厂可以调用 `inject` 来访问 `Injector` 并请求注入依赖项。 - * * @codeGenApi * @publicApi This instruction has been emitted by ViewEngine for some time and is deployed to npm. */ export function ɵɵdefineInjectable<T>(opts: { token: unknown, 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; +}): unknown { + return { + token: opts.token, + providedIn: opts.providedIn as any || null, + factory: opts.factory, + value: undefined, + } as ɵɵInjectableDef<T>; } /** * @deprecated in v8, delete after v10. This API should be used only by generated code, and that * code should now use ɵɵdefineInjectable instead. - * - * 在 v8 中弃用,在 v10 之后删除。此 API 仅应由生成的代码使用,并且该代码现在应改用 ɵɵdefineInjectable。 - * * @publicApi */ export const defineInjectable = ɵɵdefineInjectable; @@ -228,9 +166,6 @@ export const defineInjectable = ɵɵdefineInjectable; * * Options: * - * * `factory`: an `InjectorType` is an instantiable type, so a zero argument `factory` function to - * create the type must be provided. If that factory function needs to inject arguments, it can - * use the `inject` function. * * `providers`: an optional array of providers to add to the injector. Each provider must * either have a factory or point to a type which has a `ɵprov` static property (the * type must be an `InjectableType`). @@ -240,13 +175,8 @@ export const defineInjectable = ɵɵdefineInjectable; * * @codeGenApi */ -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; +export function ɵɵdefineInjector(options: {providers?: any[], imports?: any[]}): unknown { + return {providers: options.providers || [], imports: options.imports || []}; } /** diff --git a/packages/core/src/di/interface/injector.ts b/packages/core/src/di/interface/injector.ts index 72c78b2a27..eb923095a8 100644 --- a/packages/core/src/di/interface/injector.ts +++ b/packages/core/src/di/interface/injector.ts @@ -7,50 +7,75 @@ */ +/** + * Special flag indicating that a decorator is of type `Inject`. It's used to make `Inject` + * decorator tree-shakable (so we don't have to rely on the `instanceof` checks). + * Note: this flag is not included into the `InjectFlags` since it's an internal-only API. + */ +export const enum DecoratorFlags { + Inject = -1 +} + /** * Injection flags for DI. * - * DI 的注入标志。 - * * @publicApi */ export enum InjectFlags { - // TODO(alxhub): make this 'const' when ngc no longer writes exports of it into ngfactory files. + // TODO(alxhub): make this 'const' (and remove `InternalInjectFlags` enum) when ngc no longer + // writes exports of it into ngfactory files. - /** - * Check self and check parent injector if needed - * - * 检查自身并检查父注入器(如果需要) - * - */ + /** Check self and check parent injector if needed */ Default = 0b0000, + /** * Specifies that an injector should retrieve a dependency from any injector until reaching the * host element of the current component. (Only used with Element Injector) - * - * 指定注入器应从任何注入器中检索依赖项,直到到达当前组件的宿主元素为止。(仅与元素注入器一起使用) - * */ Host = 0b0001, - /** - * Don't ascend to ancestors of the node requesting injection. - * - * 不要上升到请求注入的节点的祖先去处理。 - * - */ + + /** Don't ascend to ancestors of the node requesting injection. */ Self = 0b0010, - /** - * Skip the node that is requesting injection. - * - * 跳过请求注入的节点。 - * - */ + + /** Skip the node that is requesting injection. */ SkipSelf = 0b0100, - /** - * Inject `defaultValue` instead if token not found. - * - * 如果找不到令牌,则注入 `defaultValue` - * - */ + + /** Inject `defaultValue` instead if token not found. */ Optional = 0b1000, } + +/** + * This enum is an exact copy of the `InjectFlags` enum above, but the difference is that this is a + * const enum, so actual enum values would be inlined in generated code. The `InjectFlags` enum can + * be turned into a const enum when ViewEngine is removed (see TODO at the `InjectFlags` enum + * above). The benefit of inlining is that we can use these flags at the top level without affecting + * tree-shaking (see "no-toplevel-property-access" tslint rule for more info). + * Keep this enum in sync with `InjectFlags` enum above. + */ +export const enum InternalInjectFlags { + /** Check self and check parent injector if needed */ + Default = 0b0000, + + /** + * Specifies that an injector should retrieve a dependency from any injector until reaching the + * host element of the current component. (Only used with Element Injector) + */ + Host = 0b0001, + + /** Don't ascend to ancestors of the node requesting injection. */ + Self = 0b0010, + + /** Skip the node that is requesting injection. */ + SkipSelf = 0b0100, + + /** Inject `defaultValue` instead if token not found. */ + Optional = 0b1000, + + /** + * This token is being injected into a pipe. + * + * This flag is intentionally not in the public facing `InjectFlags` because it is only added by + * the compiler and is not a developer applicable flag. + */ + ForPipe = 0b10000, +} diff --git a/packages/core/src/di/interface/provider.ts b/packages/core/src/di/interface/provider.ts index 104bebb118..5504767610 100644 --- a/packages/core/src/di/interface/provider.ts +++ b/packages/core/src/di/interface/provider.ts @@ -12,35 +12,23 @@ import {Type} from '../../interface/type'; * Configures the `Injector` to return a value for a token. * Base for `ValueProvider` decorator. * - * 配置 `Injector` 以返回令牌的值。是 `ValueProvider` 装饰器的基接口。 - * * @publicApi */ export interface ValueSansProvider { /** * The value to inject. - * - * 要注入的值。 - * */ useValue: any; } /** * Configures the `Injector` to return a value for a token. - * - * 配置此 `Injector` 以返回令牌的值。 - * * @see ["Dependency Injection Guide"](guide/dependency-injection). * - * [“依赖注入指南”](guide/dependency-injection) 。 - * * @usageNotes * * ### Example * - * ### 例子 - * * {@example core/di/ts/provider_spec.ts region='ValueProvider'} * * ### Multi-value example @@ -52,18 +40,12 @@ export interface ValueSansProvider { export interface ValueProvider extends ValueSansProvider { /** * An injection token. Typically an instance of `Type` or `InjectionToken`, but can be `any`. - * - * 注入令牌。通常是 `Type` 或 `InjectionToken` 的实例,但也可以是 `any` 实例。 - * */ provide: any; /** * When true, injector returns an array of instances. This is useful to allow multiple * providers spread across many files to provide configuration information to a common token. - * - * 如果为 true,则注入器返回实例数组。这对于允许多个提供者散布在多个文件中以向公共令牌提供配置信息很有用。 - * */ multi?: boolean; } @@ -72,39 +54,26 @@ export interface ValueProvider extends ValueSansProvider { * Configures the `Injector` to return an instance of `useClass` for a token. * Base for `StaticClassProvider` decorator. * - * 配置 `Injector` 以返回 `useClass` 的令牌实例。这是 `StaticClassProvider` 装饰器的基接口。 - * * @publicApi */ export interface StaticClassSansProvider { /** * An optional class to instantiate for the `token`. By default, the `provide` * class is instantiated. - * - * 供 `token` 实例化的可选类。默认情况下,会把 `provide` 类实例化。 - * */ useClass: Type<any>; /** * A list of `token`s to be resolved by the injector. The list of values is then * used as arguments to the `useClass` constructor. - * - * 由注入器解析的 `token` 列表。将这个值列表用作 `useClass` 构造函数的参数。 - * */ deps: any[]; } /** * Configures the `Injector` to return an instance of `useClass` for a token. - * - * 配置 `Injector` 以返回 `useClass` 的令牌实例。 - * * @see ["Dependency Injection Guide"](guide/dependency-injection). * - * [“依赖注入指南”](guide/dependency-injection) 。 - * * @usageNotes * * {@example core/di/ts/provider_spec.ts region='StaticClassProvider'} @@ -122,18 +91,12 @@ export interface StaticClassSansProvider { export interface StaticClassProvider extends StaticClassSansProvider { /** * An injection token. Typically an instance of `Type` or `InjectionToken`, but can be `any`. - * - * 注入令牌。通常是 `Type` 或 `InjectionToken` 的实例,但也可以是 `any` 实例。 - * */ provide: any; /** * When true, injector returns an array of instances. This is useful to allow multiple * providers spread across many files to provide configuration information to a common token. - * - * 如果为 true,则注入器返回实例数组。这在允许多个提供者散布在多个文件中,以向某个公共令牌提供配置信息时很有用。 - * */ multi?: boolean; } @@ -141,11 +104,8 @@ export interface StaticClassProvider extends StaticClassSansProvider { /** * Configures the `Injector` to return an instance of a token. * - * 配置此 `Injector` 以返回令牌的实例。 - * * @see ["Dependency Injection Guide"](guide/dependency-injection). * - * [“依赖注入指南”](guide/dependency-injection) 。 * @usageNotes * * ```ts @@ -158,9 +118,6 @@ export interface StaticClassProvider extends StaticClassSansProvider { export interface ConstructorSansProvider { /** * A list of `token`s to be resolved by the injector. - * - * 注入器要解析的 `token` 列表。 - * */ deps?: any[]; } @@ -168,12 +125,8 @@ export interface ConstructorSansProvider { /** * Configures the `Injector` to return an instance of a token. * - * 配置此 `Injector`,以返回令牌的实例。 - * * @see ["Dependency Injection Guide"](guide/dependency-injection). * - * [“依赖注入指南”](guide/dependency-injection) 。 - * * @usageNotes * * {@example core/di/ts/provider_spec.ts region='ConstructorProvider'} @@ -187,18 +140,12 @@ export interface ConstructorSansProvider { export interface ConstructorProvider extends ConstructorSansProvider { /** * An injection token. Typically an instance of `Type` or `InjectionToken`, but can be `any`. - * - * 注入令牌。通常是 `Type` 或 `InjectionToken` 的实例,但也可以是 `any` 实例。 - * */ provide: Type<any>; /** * When true, injector returns an array of instances. This is useful to allow multiple * providers spread across many files to provide configuration information to a common token. - * - * 如果为 true,则注入器返回实例数组。这对于允许多个提供者散布在多个文件中,以向某个公共令牌提供配置信息很有用。 - * */ multi?: boolean; } @@ -206,21 +153,14 @@ export interface ConstructorProvider extends ConstructorSansProvider { /** * Configures the `Injector` to return a value of another `useExisting` token. * - * 配置此 `Injector` 以返回另一个 `useExisting` 令牌的值。 - * * @see `ExistingProvider` * @see ["Dependency Injection Guide"](guide/dependency-injection). * - * [“依赖注入指南”](guide/dependency-injection) 。 - * * @publicApi */ export interface ExistingSansProvider { /** * Existing `token` to return. (Equivalent to `injector.get(useExisting)`) - * - * 返回现有的 `token`。 (等效于 `injector.get(useExisting)` ) - * */ useExisting: any; } @@ -228,12 +168,8 @@ export interface ExistingSansProvider { /** * Configures the `Injector` to return a value of another `useExisting` token. * - * 配置此 `Injector` 以返回另一个 `useExisting` 令牌的值。 - * * @see ["Dependency Injection Guide"](guide/dependency-injection). * - * [“依赖注入指南”](guide/dependency-injection) 。 - * * @usageNotes * * {@example core/di/ts/provider_spec.ts region='ExistingProvider'} @@ -247,18 +183,12 @@ export interface ExistingSansProvider { export interface ExistingProvider extends ExistingSansProvider { /** * An injection token. Typically an instance of `Type` or `InjectionToken`, but can be `any`. - * - * 注入令牌。通常是 `Type` 或 `InjectionToken` 的实例,但也可以是 `any` 实例。 - * */ provide: any; /** * When true, injector returns an array of instances. This is useful to allow multiple * providers spread across many files to provide configuration information to a common token. - * - * 如果为 true,则注入器返回实例数组。这对于允许多个提供者散布在多个文件中,以向某个公共令牌提供配置信息很有用。 - * */ multi?: boolean; } @@ -266,44 +196,29 @@ export interface ExistingProvider extends ExistingSansProvider { /** * Configures the `Injector` to return a value by invoking a `useFactory` function. * - * 把此 `Injector` 配置为调用 `useFactory` 函数返回一个值。 - * * @see `FactoryProvider` * @see ["Dependency Injection Guide"](guide/dependency-injection). * - * [“依赖注入指南”](guide/dependency-injection) 。 - * * @publicApi */ export interface FactorySansProvider { /** * A function to invoke to create a value for this `token`. The function is invoked with * resolved values of `token`s in the `deps` field. - * - * 供调用,来为此 `token` 创建值的函数。调用该函数时,会在 `deps` 字段中的传入 `token` 的解析结果。。 - * */ useFactory: Function; /** * A list of `token`s to be resolved by the injector. The list of values is then * used as arguments to the `useFactory` function. - * - * 供注入器解析的 `token` 列表。然后,将值列表将用作 `useFactory` 函数的参数。 - * */ deps?: any[]; } /** * Configures the `Injector` to return a value by invoking a `useFactory` function. - * - * 配置此 `Injector` 以便调用 `useFactory` 函数返回一个值。 - * * @see ["Dependency Injection Guide"](guide/dependency-injection). * - * [“依赖注入指南”](guide/dependency-injection) 。 - * * @usageNotes * * {@example core/di/ts/provider_spec.ts region='FactoryProvider'} @@ -321,18 +236,12 @@ export interface FactorySansProvider { export interface FactoryProvider extends FactorySansProvider { /** * An injection token. (Typically an instance of `Type` or `InjectionToken`, but can be `any`). - * - * 注入令牌。(通常是 `Type` 或 `InjectionToken` 的实例,但也可以是 `any` 实例)。 - * */ provide: any; /** * When true, injector returns an array of instances. This is useful to allow multiple * providers spread across many files to provide configuration information to a common token. - * - * 如果为 true,则注入器返回实例数组。这对于允许多个提供者散布在多个文件中,以向某个公共令牌提供配置信息很有用。 - * */ multi?: boolean; } @@ -341,13 +250,9 @@ export interface FactoryProvider extends FactorySansProvider { * Describes how an `Injector` should be configured as static (that is, without reflection). * A static provider provides tokens to an injector for various types of dependencies. * - * 描述如何将 `Injector` 配置为静态的(即不需要反射)。静态提供者为各种类型的依赖项提供令牌给注入器。 - * * @see [Injector.create()](/api/core/Injector#create). * @see ["Dependency Injection Guide"](guide/dependency-injection-providers). * - * [“依赖注入指南”](guide/dependency-injection-providers) 。 - * * @publicApi */ export type StaticProvider = @@ -357,17 +262,11 @@ export type StaticProvider = /** * Configures the `Injector` to return an instance of `Type` when `Type' is used as the token. * - * 配置此 `Injector`,以将“类型”用作令牌时返回 `Type` 的实例。 - * * Create an instance by invoking the `new` operator and supplying additional arguments. * This form is a short form of `TypeProvider`; * - * 通过调用 `new` 运算符并提供其他参数来创建实例。这种形式是 `TypeProvider` 的缩写形式; - * * For more details, see the ["Dependency Injection Guide"](guide/dependency-injection). * - * 欲知详情,请参见[“依赖项注入指南”](guide/dependency-injection) 。 - * * @usageNotes * * {@example core/di/ts/provider_spec.ts region='TypeProvider'} @@ -380,33 +279,21 @@ export interface TypeProvider extends Type<any> {} * Configures the `Injector` to return a value by invoking a `useClass` function. * Base for `ClassProvider` decorator. * - * 配置 `Injector` 以通过调用 `useClass` 函数返回某个值。是 `ClassProvider` 装饰器的基接口。 - * * @see ["Dependency Injection Guide"](guide/dependency-injection). * - * [“依赖注入指南”](guide/dependency-injection) 。 - * * @publicApi */ export interface ClassSansProvider { /** * Class to instantiate for the `token`. - * - * 用于将此 `token` 实例化的类。 - * */ useClass: Type<any>; } /** * Configures the `Injector` to return an instance of `useClass` for a token. - * - * 配置此 `Injector` 以便为令牌返回 `useClass` 的实例。 - * * @see ["Dependency Injection Guide"](guide/dependency-injection). * - * [“依赖注入指南”](guide/dependency-injection) 。 - * * @usageNotes * * {@example core/di/ts/provider_spec.ts region='ClassProvider'} @@ -424,31 +311,20 @@ export interface ClassSansProvider { export interface ClassProvider extends ClassSansProvider { /** * An injection token. (Typically an instance of `Type` or `InjectionToken`, but can be `any`). - * - * 注入令牌。(通常是 `Type` 或 `InjectionToken` 的实例,但也可以是 `any` 实例)。 - * */ provide: any; /** * When true, injector returns an array of instances. This is useful to allow multiple * providers spread across many files to provide configuration information to a common token. - * - * 如果为 true,则注入器返回实例数组。这对于允许多个提供者散布在多个文件中,以向某个公共令牌提供配置信息时很有用。 - * */ multi?: boolean; } /** * Describes how the `Injector` should be configured. - * - * 描述应该如何配置 `Injector`。 - * * @see ["Dependency Injection Guide"](guide/dependency-injection). * - * [“依赖注入指南”](guide/dependency-injection) 。 - * * @see `StaticProvider` * * @publicApi diff --git a/packages/core/src/di/jit/environment.ts b/packages/core/src/di/jit/environment.ts index a11e03998d..c77c0e1f1a 100644 --- a/packages/core/src/di/jit/environment.ts +++ b/packages/core/src/di/jit/environment.ts @@ -5,13 +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 {Type} from '../../interface/type'; -import {isForwardRef, resolveForwardRef} from '../forward_ref'; import {ɵɵinject, ɵɵinvalidFactoryDep} from '../injector_compatibility'; -import {getInjectableDef, getInjectorDef, ɵɵdefineInjectable, ɵɵdefineInjector} from '../interface/defs'; - - +import {ɵɵdefineInjectable, ɵɵdefineInjector} from '../interface/defs'; /** * A mapping of the @angular/core API surface used in generated expressions to the actual symbols. @@ -22,23 +17,5 @@ export const angularCoreDiEnv: {[name: string]: Function} = { 'ɵɵdefineInjectable': ɵɵdefineInjectable, 'ɵɵdefineInjector': ɵɵdefineInjector, 'ɵɵinject': ɵɵinject, - 'ɵɵgetFactoryOf': getFactoryOf, 'ɵɵinvalidFactoryDep': ɵɵinvalidFactoryDep, }; - -function getFactoryOf<T>(type: Type<any>): ((type?: Type<T>) => T)|null { - const typeAny = type as any; - - if (isForwardRef(type)) { - return (() => { - const factory = getFactoryOf<T>(resolveForwardRef(typeAny)); - return factory ? factory() : null; - }) as any; - } - - const def = getInjectableDef<T>(typeAny) || getInjectorDef<T>(typeAny); - if (!def || def.factory === undefined) { - return null; - } - return def.factory; -} diff --git a/packages/core/src/di/jit/injectable.ts b/packages/core/src/di/jit/injectable.ts index 702560897c..cec23913d7 100644 --- a/packages/core/src/di/jit/injectable.ts +++ b/packages/core/src/di/jit/injectable.ts @@ -54,8 +54,7 @@ export function compileInjectable(type: Type<any>, srcMeta?: Injectable): void { type: metadata.type, typeArgumentCount: metadata.typeArgumentCount, deps: reflectDependencies(type), - injectFn: 'inject', - target: compiler.R3FactoryTarget.Injectable + target: compiler.FactoryTarget.Injectable }); } return ngFactoryDef; diff --git a/packages/core/src/di/jit/util.ts b/packages/core/src/di/jit/util.ts index 7fa1880016..a19b72340c 100644 --- a/packages/core/src/di/jit/util.ts +++ b/packages/core/src/di/jit/util.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompilerFacade, getCompilerFacade, R3DependencyMetadataFacade, R3ResolvedDependencyType} from '../../compiler/compiler_facade'; +import {R3DependencyMetadataFacade} from '../../compiler/compiler_facade'; import {Type} from '../../interface/type'; import {ReflectionCapabilities} from '../../reflection/reflection_capabilities'; import {Host, Inject, Optional, Self, SkipSelf} from '../metadata'; @@ -23,25 +23,19 @@ export function reflectDependencies(type: Type<any>): R3DependencyMetadataFacade } export function convertDependencies(deps: any[]): R3DependencyMetadataFacade[] { - const compiler = getCompilerFacade(); - return deps.map(dep => reflectDependency(compiler, dep)); + return deps.map(dep => reflectDependency(dep)); } -function reflectDependency(compiler: CompilerFacade, dep: any|any[]): R3DependencyMetadataFacade { +function reflectDependency(dep: any|any[]): R3DependencyMetadataFacade { const meta: R3DependencyMetadataFacade = { token: null, + attribute: null, host: false, optional: false, - resolved: compiler.R3ResolvedDependencyType.Token, self: false, skipSelf: false, }; - function setTokenAndResolvedType(token: any): void { - meta.resolved = compiler.R3ResolvedDependencyType.Token; - meta.token = token; - } - if (Array.isArray(dep) && dep.length > 0) { for (let j = 0; j < dep.length; j++) { const param = dep[j]; @@ -66,20 +60,15 @@ function reflectDependency(compiler: CompilerFacade, dep: any|any[]): R3Dependen if (param.attributeName === undefined) { throw new Error(`Attribute name must be defined.`); } - meta.token = param.attributeName; - meta.resolved = compiler.R3ResolvedDependencyType.Attribute; - } else if (param.__ChangeDetectorRef__ === true) { - meta.token = param; - meta.resolved = compiler.R3ResolvedDependencyType.ChangeDetectorRef; + meta.attribute = param.attributeName; } else { - setTokenAndResolvedType(param); + meta.token = param; } } } else if (dep === undefined || (Array.isArray(dep) && dep.length === 0)) { - meta.token = undefined; - meta.resolved = R3ResolvedDependencyType.Invalid; + meta.token = null; } else { - setTokenAndResolvedType(dep); + meta.token = dep; } return meta; } diff --git a/packages/core/src/di/metadata.ts b/packages/core/src/di/metadata.ts index 9a3725af97..856ecd308c 100644 --- a/packages/core/src/di/metadata.ts +++ b/packages/core/src/di/metadata.ts @@ -8,12 +8,13 @@ import {makeParamDecorator} from '../util/decorators'; +import {attachInjectFlag} from './injector_compatibility'; +import {DecoratorFlags, InternalInjectFlags} from './interface/injector'; + /** * Type of the Inject decorator / constructor function. * - * 注入装饰器/构造函数的类型。 - * * @publicApi */ export interface InjectDecorator { @@ -21,27 +22,18 @@ export interface InjectDecorator { * Parameter decorator on a dependency parameter of a class constructor * that specifies a custom provider of the dependency. * - * 类构造函数中依赖项参数上的参数装饰器,用于指定依赖项的自定义提供者。 - * * @usageNotes - * * The following example shows a class constructor that specifies a * custom provider of a dependency using the parameter decorator. * - * 下面的示例显示了一个类构造函数,该构造函数使用参数装饰器指定了依赖项的自定义提供者。 - * * When `@Inject()` is not present, the injector uses the type annotation of the * parameter as the provider. * - * 如果有 `@Inject()`,则注入器将参数的类型注解用作提供者。 - * * <code-example path="core/di/ts/metadata_spec.ts" region="InjectWithoutDecorator"> * </code-example> * * @see ["Dependency Injection Guide"](guide/dependency-injection) * - * [“依赖注入指南”](guide/dependency-injection) - * */ (token: any): any; new(token: any): Inject; @@ -50,16 +42,11 @@ export interface InjectDecorator { /** * Type of the Inject metadata. * - * 注入元数据的类型。 - * * @publicApi */ export interface Inject { /** * A [DI token](guide/glossary#di-token) that maps to the dependency to be injected. - * - * 一个 [DI 令牌](guide/glossary#di-token),映射到要注入的依赖项。 - * */ token: any; } @@ -67,47 +54,36 @@ export interface Inject { /** * Inject decorator and metadata. * - * 注入装饰器和元数据。 - * * @Annotation * @publicApi */ -export const Inject: InjectDecorator = makeParamDecorator('Inject', (token: any) => ({token})); - +export const Inject: InjectDecorator = attachInjectFlag( + // Disable tslint because `DecoratorFlags` is a const enum which gets inlined. + // tslint:disable-next-line: no-toplevel-property-access + makeParamDecorator('Inject', (token: any) => ({token})), DecoratorFlags.Inject); /** * Type of the Optional decorator / constructor function. * - * 可选装饰器/构造函数的类型。 - * * @publicApi */ export interface OptionalDecorator { /** * Parameter decorator to be used on constructor parameters, * which marks the parameter as being an optional dependency. - * The DI framework provides null if the dependency is not found. - * - * 用于构造函数参数的参数装饰器,将参数标记为可选依赖项。如果找不到依赖项,则 DI 框架提供 null。 + * The DI framework provides `null` if the dependency is not found. * * Can be used together with other parameter decorators * that modify how dependency injection operates. * - * 可以与其他修改依赖注入方式的参数装饰器一起使用。 - * * @usageNotes * - * The following code allows the possibility of a null result: - * - * 以下代码允许结果为空的可能性: + * The following code allows the possibility of a `null` result: * * <code-example path="core/di/ts/metadata_spec.ts" region="Optional"> * </code-example> * * @see ["Dependency Injection Guide"](guide/dependency-injection). - * - * [“依赖注入指南”](guide/dependency-injection) 。 - * */ (): any; new(): Optional; @@ -116,8 +92,6 @@ export interface OptionalDecorator { /** * Type of the Optional metadata. * - * 可选元数据的类型。 - * * @publicApi */ export interface Optional {} @@ -125,18 +99,17 @@ export interface Optional {} /** * Optional decorator and metadata. * - * 可选的装饰器和元数据。 - * * @Annotation * @publicApi */ -export const Optional: OptionalDecorator = makeParamDecorator('Optional'); +export const Optional: OptionalDecorator = + // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. + // tslint:disable-next-line: no-toplevel-property-access + attachInjectFlag(makeParamDecorator('Optional'), InternalInjectFlags.Optional); /** * Type of the Self decorator / constructor function. * - * Self 装饰器/构造函数的类型。 - * * @publicApi */ export interface SelfDecorator { @@ -144,12 +117,8 @@ export interface SelfDecorator { * Parameter decorator to be used on constructor parameters, * which tells the DI framework to start dependency resolution from the local injector. * - * 将在构造函数参数上使用参数装饰器,该装饰器告诉 DI 框架从本地注入器开始解析依赖项。 - * * Resolution works upward through the injector hierarchy, so the children - * of this class must configure their own providers or be prepared for a null result. - * - * 解析器在注入器层次结构中向上查找,因此此类的子级必须配置其自己的提供者或为空结果做好准备。 + * of this class must configure their own providers or be prepared for a `null` result. * * @usageNotes * @@ -157,8 +126,6 @@ export interface SelfDecorator { * by the local injector when instantiating the class itself, but not * when instantiating a child. * - * 在以下示例中,依赖关系可以在实例化类本身时由本地注入器解析,而在实例化子代时不能解析。 - * * <code-example path="core/di/ts/metadata_spec.ts" region="Self"> * </code-example> * @@ -173,8 +140,6 @@ export interface SelfDecorator { /** * Type of the Self metadata. * - * Self 元数据的类型。 - * * @publicApi */ export interface Self {} @@ -182,19 +147,18 @@ export interface Self {} /** * Self decorator and metadata. * - * Self 装饰器和元数据。 - * * @Annotation * @publicApi */ -export const Self: SelfDecorator = makeParamDecorator('Self'); +export const Self: SelfDecorator = + // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. + // tslint:disable-next-line: no-toplevel-property-access + attachInjectFlag(makeParamDecorator('Self'), InternalInjectFlags.Self); /** * Type of the `SkipSelf` decorator / constructor function. * - * `SkipSelf` 装饰器/构造函数的类型。 - * * @publicApi */ export interface SkipSelfDecorator { @@ -204,22 +168,15 @@ export interface SkipSelfDecorator { * Resolution works upward through the injector hierarchy, so the local injector * is not checked for a provider. * - * 将在构造函数参数上使用的参数装饰器,该参数指示 DI 框架从父注入器启动依赖项解析。解析器在注入器层次结构中向上查找,因此不会检查本地注入器的提供者。 - * * @usageNotes * * In the following example, the dependency can be resolved when * instantiating a child, but not when instantiating the class itself. * - * 在以下示例中,可以在实例化子级时解析依赖项,但在实例化类本身时不解析。 - * * <code-example path="core/di/ts/metadata_spec.ts" region="SkipSelf"> * </code-example> * * @see [Dependency Injection guide](guide/dependency-injection-in-action#skip). - * - * [依赖注入指南](guide/dependency-injection-in-action#skip)。 - * * @see `Self` * @see `Optional` * @@ -231,8 +188,6 @@ export interface SkipSelfDecorator { /** * Type of the `SkipSelf` metadata. * - * `SkipSelf` 元数据的类型。 - * * @publicApi */ export interface SkipSelf {} @@ -240,18 +195,17 @@ export interface SkipSelf {} /** * `SkipSelf` decorator and metadata. * - * `SkipSelf` 装饰器和元数据。 - * * @Annotation * @publicApi */ -export const SkipSelf: SkipSelfDecorator = makeParamDecorator('SkipSelf'); +export const SkipSelf: SkipSelfDecorator = + // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. + // tslint:disable-next-line: no-toplevel-property-access + attachInjectFlag(makeParamDecorator('SkipSelf'), InternalInjectFlags.SkipSelf); /** * Type of the `Host` decorator / constructor function. * - * `Host` 装饰器/构造函数的类型。 - * * @publicApi */ export interface HostDecorator { @@ -260,22 +214,15 @@ export interface HostDecorator { * that tells the DI framework to resolve the view by checking injectors of child * elements, and stop when reaching the host element of the current component. * - * 类构造函数的视图提供者参数上的参数修饰器,用于指示 DI 框架通过检查子元素的注入器来解析视图,并在到达当前组件的宿主元素时停止。 - * * @usageNotes * - * The following shows use with the `@Optional` decorator, and allows for a null result. - * - * 以下显示了与 `@Optional` 装饰器一起使用的情况,并允许空结果。 + * The following shows use with the `@Optional` decorator, and allows for a `null` result. * * <code-example path="core/di/ts/metadata_spec.ts" region="Host"> * </code-example> * * For an extended example, see ["Dependency Injection * Guide"](guide/dependency-injection-in-action#optional). - * - * 有关扩展的示例,请参见[“依赖项注入指南”](guide/dependency-injection-in-action#optional) 。 - * */ (): any; new(): Host; @@ -284,8 +231,6 @@ export interface HostDecorator { /** * Type of the Host metadata. * - * 宿主元数据的类型。 - * * @publicApi */ export interface Host {} @@ -293,9 +238,10 @@ export interface Host {} /** * Host decorator and metadata. * - * 宿主装饰器和元数据。 - * * @Annotation * @publicApi */ -export const Host: HostDecorator = makeParamDecorator('Host'); +export const Host: HostDecorator = + // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. + // tslint:disable-next-line: no-toplevel-property-access + attachInjectFlag(makeParamDecorator('Host'), InternalInjectFlags.Host); diff --git a/packages/core/src/di/metadata_attr.ts b/packages/core/src/di/metadata_attr.ts index 7d564a4e65..bee9d66c42 100644 --- a/packages/core/src/di/metadata_attr.ts +++ b/packages/core/src/di/metadata_attr.ts @@ -13,8 +13,6 @@ import {makeParamDecorator} from '../util/decorators'; /** * Type of the Attribute decorator / constructor function. * - * 属性装饰器/构造函数的类型。 - * * @publicApi */ export interface AttributeDecorator { @@ -22,22 +20,16 @@ export interface AttributeDecorator { * Parameter decorator for a directive constructor that designates * a host-element attribute whose value is injected as a constant string literal. * - * 指令构造函数的参数修饰器,用于指定宿主元素属性,其值作为常量字符串文字注入。 - * * @usageNotes * * Suppose we have an `<input>` element and want to know its `type`. * - * 假设我们有一个 `<input>` 元素,并且想知道它的 `type` 。 - * * ```html * <input type="text"> * ``` * * The following example uses the decorator to inject the string literal `text` in a directive. * - * 以下示例使用装饰器将字符串文字 `text` 注入指令中。 - * * {@example core/ts/metadata/metadata.ts region='attributeMetadata'} * * The following example uses the decorator in a component constructor. @@ -52,16 +44,11 @@ export interface AttributeDecorator { /** * Type of the Attribute metadata. * - * 属性元数据的类型。 - * * @publicApi */ export interface Attribute { /** * The name of the attribute whose value can be injected. - * - * 可以注入其值的属性的名称。 - * */ attributeName: string; } @@ -82,8 +69,6 @@ const CREATE_ATTRIBUTE_DECORATOR_IMPL = CREATE_ATTRIBUTE_DECORATOR__PRE_R3__; /** * Attribute decorator and metadata. * - * 属性装饰器和元数据。 - * * @Annotation * @publicApi */ diff --git a/packages/core/src/di/r3_injector.ts b/packages/core/src/di/r3_injector.ts index 57e1d6187f..625f5f4012 100644 --- a/packages/core/src/di/r3_injector.ts +++ b/packages/core/src/di/r3_injector.ts @@ -13,6 +13,7 @@ import {AbstractType, Type} from '../interface/type'; import {FactoryFn, getFactoryDef} from '../render3/definition_factory'; import {throwCyclicDependencyError, throwInvalidProviderError, throwMixedMultiProviderError} from '../render3/errors_di'; import {deepForEach, newArray} from '../util/array_utils'; +import {EMPTY_ARRAY} from '../util/empty'; import {stringify} from '../util/stringify'; import {resolveForwardRef} from './forward_ref'; @@ -48,8 +49,6 @@ const NOT_YET = {}; */ const CIRCULAR = {}; -const EMPTY_ARRAY = [] as any[]; - /** * A lazily initialized NullInjector. */ @@ -75,8 +74,6 @@ interface Record<T> { /** * Create a new `Injector` which is configured using a `defType` of `InjectorType<any>`s. * - * 创建一个新的 `Injector`, 它是使用 `InjectorType<any>` 的 `defType` 配置的。 - * * @publicApi */ export function createInjector( @@ -352,7 +349,8 @@ export class R3Injector { // Track the InjectorType and add a provider for it. It's important that this is done after the // def's imports. this.injectorDefTypes.add(defType); - this.records.set(defType, makeRecord(def.factory, NOT_YET)); + const factory = getFactoryDef(defType) || (() => new defType()); + this.records.set(defType, makeRecord(factory, NOT_YET)); // Next, include providers listed on the definition itself. const defProviders = def.providers; @@ -440,13 +438,6 @@ function injectableDefOrInjectorDefFactory(token: Type<any>|AbstractType<any>| return factory; } - // If the token is an NgModule, it's also injectable but the factory is on its injector def - // (`ɵinj`) - const injectorDef = getInjectorDef(token); - if (injectorDef !== null) { - return injectorDef.factory; - } - // InjectionTokens should have an injectable def (ɵprov) and thus should be handled above. // If it's missing that, it's an error. if (token instanceof InjectionToken) { diff --git a/packages/core/src/di/reflective_errors.ts b/packages/core/src/di/reflective_errors.ts index 42a1416503..af4e04b2fb 100644 --- a/packages/core/src/di/reflective_errors.ts +++ b/packages/core/src/di/reflective_errors.ts @@ -70,7 +70,6 @@ function addKey(this: InjectionError, injector: ReflectiveInjector, key: Reflect * {@link Injector} does not have a {@link Provider} for the given key. * * @usageNotes - * * ### Example * * ```typescript @@ -92,7 +91,6 @@ export function noProviderError(injector: ReflectiveInjector, key: ReflectiveKey * Thrown when dependencies form a cycle. * * @usageNotes - * * ### Example * * ```typescript @@ -120,7 +118,6 @@ export function cyclicDependencyError( * this object to be instantiated. * * @usageNotes - * * ### Example * * ```typescript @@ -156,7 +153,6 @@ export function instantiationError( * creation. * * @usageNotes - * * ### Example * * ```typescript @@ -175,7 +171,6 @@ export function invalidProviderError(provider: any) { * need to be injected into the constructor. * * @usageNotes - * * ### Example * * ```typescript @@ -220,7 +215,6 @@ export function noAnnotationError(typeOrFunc: Type<any>|Function, params: any[][ * Thrown when getting an object by index. * * @usageNotes - * * ### Example * * ```typescript @@ -241,7 +235,6 @@ export function outOfBoundsError(index: number) { * Thrown when a multi provider and a regular provider are bound to the same token. * * @usageNotes - * * ### Example * * ```typescript diff --git a/packages/core/src/di/reflective_injector.ts b/packages/core/src/di/reflective_injector.ts index c77259ea4c..43192d1e16 100644 --- a/packages/core/src/di/reflective_injector.ts +++ b/packages/core/src/di/reflective_injector.ts @@ -22,28 +22,17 @@ const UNDEFINED = {}; * A ReflectiveDependency injection container used for instantiating objects and resolving * dependencies. * - * 一个 ReflectiveDependency 注入容器,用于实例化对象和解析依赖关系。 - * * An `Injector` is a replacement for a `new` operator, which can automatically resolve the * constructor dependencies. * - * `Injector` 替代了 `new` 操作符,该操作符可以自动解析构造函数的依赖关系。 - * * In typical use, application code asks for the dependencies in the constructor and they are * resolved by the `Injector`. * - * 在典型的用法中,应用程序代码会在构造函数中要求依赖项,并由 `Injector` 进行解析。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * The following example creates an `Injector` configured to create `Engine` and `Car`. * - * 以下示例创建一个配置为创建 `Engine` 和 `Car` 的 `Injector`。 - * * ```typescript * @Injectable() * class Engine { @@ -64,28 +53,18 @@ const UNDEFINED = {}; * resolve all of the object's dependencies automatically. * * @deprecated from v5 - slow and brings in a lot of code, Use `Injector.create` instead. - * - * 从 v5 开始 - 速度慢,并且引入了大量代码,请改用 `Injector.create` 。 - * * @publicApi */ export abstract class ReflectiveInjector implements Injector { /** * Turns an array of provider definitions into an array of resolved providers. * - * 将一组提供者定义转换为一组已解析的提供者。 - * * A resolution is a process of flattening multiple nested arrays and converting individual * providers into an array of `ResolvedReflectiveProvider`s. * - * 解析是展平多个嵌套数组并将其各个提供者转换为 `ResolvedReflectiveProvider` 数组的过程。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * ```typescript * @Injectable() * class Engine { @@ -117,19 +96,12 @@ export abstract class ReflectiveInjector implements Injector { /** * Resolves an array of providers and creates an injector from those providers. * - * 解析供应商数组,并从这些供应商创建注入器。 - * * The passed-in providers can be an array of `Type`, `Provider`, * or a recursive array of more providers. * - * 传入的提供者可以是 `Type`、`Provider` 或更多提供者的递归数组。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * ```typescript * @Injectable() * class Engine { @@ -152,18 +124,11 @@ export abstract class ReflectiveInjector implements Injector { /** * Creates an injector from previously resolved providers. * - * 从先前解析的提供者创建注入器。 - * * This API is the recommended way to construct injectors in performance-sensitive parts. * - * 建议使用此 API 在对性能敏感的部分构建注入器。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * ```typescript * @Injectable() * class Engine { @@ -188,8 +153,6 @@ export abstract class ReflectiveInjector implements Injector { /** * Parent of this injector. * - * 此注入器的父代。 - * * <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection. * --> */ @@ -198,22 +161,15 @@ export abstract class ReflectiveInjector implements Injector { /** * Resolves an array of providers and creates a child injector from those providers. * - * 解析一组提供者,并从这些提供者创建子注入器。 - * * <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection. * --> * * The passed-in providers can be an array of `Type`, `Provider`, * or a recursive array of more providers. * - * 传入的提供者可以是 `Type`、`Provider` 或更多提供者的递归数组。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * ```typescript * class ParentProvider {} * class ChildProvider {} @@ -231,21 +187,14 @@ export abstract class ReflectiveInjector implements Injector { /** * Creates a child injector from previously resolved providers. * - * 从先前解析的提供者中创建子注入器。 - * * <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection. * --> * * This API is the recommended way to construct injectors in performance-sensitive parts. * - * 建议使用此 API 在对性能敏感的部分构造注入器。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * ```typescript * class ParentProvider {} * class ChildProvider {} @@ -266,18 +215,11 @@ export abstract class ReflectiveInjector implements Injector { /** * Resolves a provider and instantiates an object in the context of the injector. * - * 解析提供者并在注入器的上下文中实例化对象。 - * * The created object does not get cached by the injector. * - * 注入器不会缓存创建的对象。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * ```typescript * @Injectable() * class Engine { @@ -300,18 +242,11 @@ export abstract class ReflectiveInjector implements Injector { /** * Instantiates an object using a resolved provider in the context of the injector. * - * 在注入器的上下文中使用解析的提供者实例化对象。 - * * The created object does not get cached by the injector. * - * 注入器不会缓存创建的对象。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * ```typescript * @Injectable() * class Engine { diff --git a/packages/core/src/di/reflective_key.ts b/packages/core/src/di/reflective_key.ts index 865d0aad01..a3323a8cfb 100644 --- a/packages/core/src/di/reflective_key.ts +++ b/packages/core/src/di/reflective_key.ts @@ -13,45 +13,25 @@ import {resolveForwardRef} from './forward_ref'; /** * A unique object used for retrieving items from the {@link ReflectiveInjector}. * - * 用于从 {@link ReflectiveInjector} 中检索项目的唯一对象。 - * * Keys have: - * - * 其键名有: - * * - a system-wide unique `id`. - * - * 系统范围内的唯一 `id` 。 - * * - a `token`. * - * `token`。 - * * `Key` is used internally by {@link ReflectiveInjector} because its system-wide unique `id` allows * the * injector to store created objects in a more efficient way. * - * `Key` 由 {@link ReflectiveInjector} 内部使用,因为它在系统范围内的唯一 `id` 允许注入器以更有效的方式存储所创建的对象。 - * * `Key` should not be created directly. {@link ReflectiveInjector} creates keys automatically when * resolving * providers. * - * `Key` 不应直接创建。{@link ReflectiveInjector} 在解析提供者时会自动创建键名。 - * * @deprecated No replacement - * - * 无替代品 - * * @publicApi */ export class ReflectiveKey { public readonly displayName: string; /** * Private - * - * 私人的 - * */ constructor(public token: Object, public id: number) { if (!token) { @@ -62,9 +42,6 @@ export class ReflectiveKey { /** * Retrieves a `Key` for a token. - * - * 根据令牌检索出一个 `Key`。 - * */ static get(token: Object): ReflectiveKey { return _globalKeyRegistry.get(resolveForwardRef(token)); @@ -72,9 +49,6 @@ export class ReflectiveKey { /** * @returns the number of keys registered in the system. - * - * 在系统中注册的 `Key` 数。 - * */ static get numberOfKeys(): number { return _globalKeyRegistry.numberOfKeys; diff --git a/packages/core/src/di/reflective_provider.ts b/packages/core/src/di/reflective_provider.ts index 7cda008a80..77e5cee81b 100644 --- a/packages/core/src/di/reflective_provider.ts +++ b/packages/core/src/di/reflective_provider.ts @@ -38,22 +38,13 @@ const _EMPTY_LIST: any[] = []; /** * An internal resolved representation of a `Provider` used by the `Injector`. * - * 供 `Injector` 使用的 `Provider` 的内部解析表示形式。 - * * @usageNotes - * * This is usually created automatically by `Injector.resolveAndCreate`. * - * 这通常是由 `Injector.resolveAndCreate` 自动创建的。 - * * It can be created manually, as follows: * - * 也可以手动创建,如下所示: - * * ### Example * - * ### 例子 - * * ```typescript * var resolvedProviders = Injector.resolve([{ provide: 'message', useValue: 'Hello' }]); * var injector = Injector.fromResolvedProviders(resolvedProviders); @@ -66,25 +57,16 @@ const _EMPTY_LIST: any[] = []; export interface ResolvedReflectiveProvider { /** * A key, usually a `Type<any>`. - * - * 一个 Key,通常是 `Type<any>` 。 - * */ key: ReflectiveKey; /** * Factory function which can return an instance of an object represented by a key. - * - * 可以返回 Key 表示的对象实例的工厂函数。 - * */ resolvedFactories: ResolvedReflectiveFactory[]; /** * Indicates if the provider is a multi-provider or a regular provider. - * - * 指示提供者是多重提供者还是常规提供者。 - * */ multiProvider: boolean; } @@ -101,9 +83,6 @@ export class ResolvedReflectiveProvider_ implements ResolvedReflectiveProvider { /** * An internal resolved representation of a factory function created by resolving `Provider`. - * - * `Provider` 创建的工厂函数的内部解析表示形式。 - * * @publicApi */ export class ResolvedReflectiveFactory { diff --git a/packages/core/src/di/util.ts b/packages/core/src/di/util.ts index 2070b057e3..1bb5537581 100644 --- a/packages/core/src/di/util.ts +++ b/packages/core/src/di/util.ts @@ -8,6 +8,7 @@ import {Type} from '../interface/type'; import {ReflectionCapabilities} from '../reflection/reflection_capabilities'; +import {EMPTY_ARRAY} from '../util/empty'; import {getClosureSafeProperty} from '../util/property'; import {resolveForwardRef} from './forward_ref'; @@ -16,7 +17,6 @@ import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, Factor const USE_VALUE = getClosureSafeProperty<ValueProvider>({provide: String, useValue: getClosureSafeProperty}); -const EMPTY_ARRAY: any[] = []; export function convertInjectableProviderToFactory( type: Type<any>, diff --git a/packages/core/src/error_handler.ts b/packages/core/src/error_handler.ts index 41e4596786..7f5ec2748e 100644 --- a/packages/core/src/error_handler.ts +++ b/packages/core/src/error_handler.ts @@ -13,20 +13,13 @@ import {getDebugContext, getErrorLogger, getOriginalError} from './errors'; /** * Provides a hook for centralized exception handling. * - * 提供用于集中式异常处理的挂钩。 - * * The default implementation of `ErrorHandler` prints error messages to the `console`. To * intercept error handling, write a custom exception handler that replaces this default as * appropriate for your app. * - * `ErrorHandler` 的默认实现将错误消息打印到 `console`。要拦截错误处理,请编写一个自定义的异常处理器,该异常处理器将把此默认行为改成你应用所需的。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * ``` * class MyErrorHandler implements ErrorHandler { * handleError(error) { diff --git a/packages/core/src/event_emitter.ts b/packages/core/src/event_emitter.ts index ea66c1b33e..b7a2bd2217 100644 --- a/packages/core/src/event_emitter.ts +++ b/packages/core/src/event_emitter.ts @@ -15,22 +15,16 @@ import {Subject, Subscription} from 'rxjs'; * synchronously or asynchronously, and register handlers for those events * by subscribing to an instance. * - * 用在带有 `@Output` 指令的组件中,以同步或异步方式发出自定义事件,并通过订阅实例来为这些事件注册处理器。 - * * @usageNotes * * Extends * [RxJS `Subject`](https://rxjs.dev/api/index/class/Subject) * for Angular by adding the `emit()` method. * - * 通过添加 `emit()` 方法来扩展 [Angular 的 RxJS `Subject`](https://rxjs.dev/api/index/class/Subject)。 - * * In the following example, a component defines two output properties * that create event emitters. When the title is clicked, the emitter * emits an open or close event to toggle the current visibility state. * - * 在以下示例中,组件定义了两个创建事件发射器的输出属性。单击标题后,发射器将发出打开或关闭事件以切换当前可见性状态。 - * * ```html * @Component({ * selector: 'zippy', @@ -65,9 +59,6 @@ import {Subject, Subscription} from 'rxjs'; * ``` * * @see [Observables in Angular](guide/observables-in-angular) - * - * [Angular 中的可观察对象](guide/observables-in-angular) - * * @publicApi */ export interface EventEmitter<T> extends Subject<T> { @@ -80,8 +71,6 @@ export interface EventEmitter<T> extends Subject<T> { * Creates an instance of this class that can * deliver events synchronously or asynchronously. * - * 创建此类的实例,该实例可以同步或异步发送事件。 - * * @param [isAsync=false] When true, deliver events asynchronously. * */ @@ -89,36 +78,28 @@ export interface EventEmitter<T> extends Subject<T> { /** * Emits an event containing a given value. - * - * 发出包含给定值的事件。 - * * @param value The value to emit. - * - * 要发出的值。 - * */ emit(value?: T): void; + /** * Registers handlers for events emitted by this instance. - * - * 注册此实例发出的事件的处理器。 - * - * @param generatorOrNext When supplied, a custom handler for emitted events. - * - * 如果提供,则为所发出事件的自定义处理器。 - * - * @param error When supplied, a custom handler for an error notification - * from this emitter. - * - * 如果提供,则为这里发出的错误通知的自定义处理器。 - * - * @param complete When supplied, a custom handler for a completion - * notification from this emitter. - * - * 如果提供,则为这里发出的完成通知的自定义处理器。 - * + * @param next When supplied, a custom handler for emitted events. + * @param error When supplied, a custom handler for an error notification from this emitter. + * @param complete When supplied, a custom handler for a completion notification from this + * emitter. */ - subscribe(generatorOrNext?: any, error?: any, complete?: any): Subscription; + subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): + Subscription; + /** + * Registers handlers for events emitted by this instance. + * @param observerOrNext When supplied, a custom handler for emitted events, or an observer + * object. + * @param error When supplied, a custom handler for an error notification from this emitter. + * @param complete When supplied, a custom handler for a completion notification from this + * emitter. + */ + subscribe(observerOrNext?: any, error?: any, complete?: any): Subscription; } class EventEmitter_ extends Subject<any> { @@ -133,38 +114,38 @@ class EventEmitter_ extends Subject<any> { super.next(value); } - subscribe(generatorOrNext?: any, error?: any, complete?: any): Subscription { + subscribe(observerOrNext?: any, error?: any, complete?: any): Subscription { let schedulerFn: (t: any) => any; let errorFn = (err: any): any => null; let completeFn = (): any => null; - if (generatorOrNext && typeof generatorOrNext === 'object') { + if (observerOrNext && typeof observerOrNext === 'object') { schedulerFn = this.__isAsync ? (value: any) => { - setTimeout(() => generatorOrNext.next(value)); + setTimeout(() => observerOrNext.next(value)); } : (value: any) => { - generatorOrNext.next(value); + observerOrNext.next(value); }; - if (generatorOrNext.error) { + if (observerOrNext.error) { errorFn = this.__isAsync ? (err) => { - setTimeout(() => generatorOrNext.error(err)); + setTimeout(() => observerOrNext.error(err)); } : (err) => { - generatorOrNext.error(err); + observerOrNext.error(err); }; } - if (generatorOrNext.complete) { + if (observerOrNext.complete) { completeFn = this.__isAsync ? () => { - setTimeout(() => generatorOrNext.complete()); + setTimeout(() => observerOrNext.complete()); } : () => { - generatorOrNext.complete(); + observerOrNext.complete(); }; } } else { schedulerFn = this.__isAsync ? (value: any) => { - setTimeout(() => generatorOrNext(value)); + setTimeout(() => observerOrNext(value)); } : (value: any) => { - generatorOrNext(value); + observerOrNext(value); }; if (error) { @@ -186,8 +167,8 @@ class EventEmitter_ extends Subject<any> { const sink = super.subscribe(schedulerFn, errorFn, completeFn); - if (generatorOrNext instanceof Subscription) { - generatorOrNext.add(sink); + if (observerOrNext instanceof Subscription) { + observerOrNext.add(sink); } return sink; diff --git a/packages/core/src/i18n/tokens.ts b/packages/core/src/i18n/tokens.ts index 46867fa81f..91a57caa94 100644 --- a/packages/core/src/i18n/tokens.ts +++ b/packages/core/src/i18n/tokens.ts @@ -13,18 +13,11 @@ import {InjectionToken} from '../di/injection_token'; * It is used for i18n extraction, by i18n pipes (DatePipe, I18nPluralPipe, CurrencyPipe, * DecimalPipe and PercentPipe) and by ICU expressions. * - * 提供此令牌以设置应用程序的语言环境。它通过 i18n 管道(DatePipe、I18nPluralPipe、CurrencyPipe、DecimalPipe 和 PercentPipe)和 ICU 表达式用于 i18n 提取。 - * * See the [i18n guide](guide/i18n#setting-up-locale) for more information. * - * 有关更多信息,请参见《 [i18n 指南》。](guide/i18n#setting-up-locale) - * * @usageNotes - * * ### Example * - * ### 例子 - * * ```typescript * import { LOCALE_ID } from '@angular/core'; * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; @@ -44,31 +37,19 @@ export const LOCALE_ID = new InjectionToken<string>('LocaleId'); * CurrencyPipe when there is no currency code passed into it. This is only used by * CurrencyPipe and has no relation to locale currency. Defaults to USD if not configured. * - * 如果没有传递任何货币代码,请提供此令牌来设置你的应用程序用于 CurrencyPipe 的默认货币代码。仅由 CurrencyPipe 使用,与语言环境的货币无关。如果未配置,则默认为 USD。 - * * See the [i18n guide](guide/i18n#setting-up-locale) for more information. * - * 有关更多信息,请参见[《i18n 指南》](guide/i18n#setting-up-locale)。 - * * <div class="alert is-helpful"> * * **Deprecation notice:** * - * **弃用通知:** - * * The default currency code is currently always `USD` but this is deprecated from v9. * - * 默认货币代码当前始终为 `USD` 但自 v9 起已弃用。 - * * **In v10 the default currency code will be taken from the current locale.** * - * **在 v10 中,默认货币代码将从当前语言环境中获取。** - * * If you need the previous behavior then set it by creating a `DEFAULT_CURRENCY_CODE` provider in * your application `NgModule`: * - * 如果你需要以前的行为,请通过应用 `NgModule` 中的 `DEFAULT_CURRENCY_CODE` 提供者来进行设置: - * * ```ts * {provide: DEFAULT_CURRENCY_CODE, useValue: 'USD'} * ``` @@ -76,11 +57,8 @@ export const LOCALE_ID = new InjectionToken<string>('LocaleId'); * </div> * * @usageNotes - * * ### Example * - * ### 例子 - * * ```typescript * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; * import { AppModule } from './app/app.module'; @@ -98,18 +76,11 @@ export const DEFAULT_CURRENCY_CODE = new InjectionToken<string>('DefaultCurrency * Use this token at bootstrap to provide the content of your translation file (`xtb`, * `xlf` or `xlf2`) when you want to translate your application in another language. * - * 当你想用另一种语言翻译应用程序时,可以在引导程序中使用此令牌来提供翻译文件的内容( `xtb`、`xlf` 或 `xlf2`) - * * See the [i18n guide](guide/i18n#merge) for more information. * - * 有关更多信息,请参见[《i18n 指南》](guide/i18n#setting-up-locale)。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * ```typescript * import { TRANSLATIONS } from '@angular/core'; * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; @@ -131,18 +102,11 @@ export const TRANSLATIONS = new InjectionToken<string>('Translations'); * Provide this token at bootstrap to set the format of your {@link TRANSLATIONS}: `xtb`, * `xlf` or `xlf2`. * - * 在引导程序中提供此令牌以设置 {@link TRANSLATIONS} 的格式: `xtb`、`xlf` 或 `xlf2`。 - * * See the [i18n guide](guide/i18n#merge) for more information. * - * 有关更多信息,请参见[《i18n 指南》](guide/i18n#setting-up-locale)。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * ```typescript * import { TRANSLATIONS_FORMAT } from '@angular/core'; * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; @@ -160,31 +124,14 @@ export const TRANSLATIONS_FORMAT = new InjectionToken<string>('TranslationsForma /** * Use this enum at bootstrap as an option of `bootstrapModule` to define the strategy * that the compiler should use in case of missing translations: - * - * 在系统启动时使用此枚举作为 `bootstrapModule` 的一个选项来定义策略,编译器应该在缺少翻译的情况下使用: - * * - Error: throw if you have missing translations. - * - * Error:如果缺少翻译,则抛出该错误。 - * * - Warning (default): show a warning in the console and/or shell. - * - * Warning(默认):在控制台和/或应用外壳中显示警告。 - * * - Ignore: do nothing. * - * Ignore:什么都不做。 - * * See the [i18n guide](guide/i18n#missing-translation) for more information. * - * 有关更多信息,请参见[《i18n 指南》](guide/i18n#setting-up-locale)。 - * * @usageNotes - * * ### Example - * - * ### 例子 - * * ```typescript * import { MissingTranslationStrategy } from '@angular/core'; * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; diff --git a/packages/core/src/interface/lifecycle_hooks.ts b/packages/core/src/interface/lifecycle_hooks.ts index fce7f09aee..e73a8791cd 100644 --- a/packages/core/src/interface/lifecycle_hooks.ts +++ b/packages/core/src/interface/lifecycle_hooks.ts @@ -13,22 +13,14 @@ import {SimpleChanges} from './simple_change'; * A lifecycle hook that is called when any data-bound property of a directive changes. * Define an `ngOnChanges()` method to handle the changes. * - * 一个生命周期钩子,当指令的任何一个可绑定属性发生变化时调用。 - * 定义一个 `ngOnChanges()` 方法来处理这些变更。 - * * @see `DoCheck` * @see `OnInit` * @see [Lifecycle hooks guide](guide/lifecycle-hooks) * - * [生命周期钩子](guide/lifecycle-hooks#onchanges) - * * @usageNotes - * * The following snippet shows how a component can implement this interface to * define an on-changes handler for an input property. * - * 下列代码片段展示了组件要如何实现本接口来定义一个输入属性的变更处理器。 - * * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='OnChanges'} * * @publicApi @@ -39,12 +31,7 @@ export interface OnChanges { * default change detector has checked data-bound properties * if at least one has changed, and before the view and content * children are checked. - * - * 如果至少发生了一次变更,则该回调方法会在默认的变更检测器检查完可绑定属性之后、视图子节点和内容子节点检查完之前调用。 - * * @param changes The changed properties. - * - * 那些发生了变化的属性。 */ ngOnChanges(changes: SimpleChanges): void; } @@ -55,21 +42,13 @@ export interface OnChanges { * all data-bound properties of a directive. * Define an `ngOnInit()` method to handle any additional initialization tasks. * - * 一个生命周期钩子,它会在 Angular 初始化完了该指令的所有数据绑定属性之后调用。 - * 定义 `ngOnInit()` 方法可以处理所有附加的初始化任务。 - * * @see `AfterContentInit` * @see [Lifecycle hooks guide](guide/lifecycle-hooks) * - * [生命周期钩子](guide/lifecycle-hooks#onchanges) - * * @usageNotes - * * The following snippet shows how a component can implement this interface to * define its own initialization method. * - * 下列片段展示了组件要如何实现此接口,以定义它自己的初始化方法。 - * * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='OnInit'} * * @publicApi @@ -81,9 +60,6 @@ export interface OnInit { * data-bound properties for the first time, * and before any of the view or content children have been checked. * It is invoked only once when the directive is instantiated. - * - * 它的调用时机在默认的变更检测器首次检查完该指令的所有数据绑定属性之后,任何子视图或投影内容检查完之前。 - * 它会且只会在指令初始化时调用一次。 */ ngOnInit(): void; } @@ -92,35 +68,22 @@ export interface OnInit { * A lifecycle hook that invokes a custom change-detection function for a directive, * in addition to the check performed by the default change-detector. * - * 一个生命周期钩子,除了使用默认的变更检查器执行检查之外,还会为指令执行自定义的变更检测函数。 - * * The default change-detection algorithm looks for differences by comparing * bound-property values by reference across change detection runs. You can use this * hook to check for and respond to changes by some other means. * - * 在变更检测期间,默认的变更检测算法会根据引用来比较可绑定属性,以查找差异。 - * 你可以使用此钩子来用其他方式检查和响应变更。 - * * When the default change detector detects changes, it invokes `ngOnChanges()` if supplied, * regardless of whether you perform additional change detection. * Typically, you should not use both `DoCheck` and `OnChanges` to respond to * changes on the same input. * - * 当默认的变更检测器检查更改时,它会执行 `ngOnChanges()`(如果有),而不在乎你是否进行了额外的变更检测。 - * 一般来说,你不应该同时使用 `DoCheck` 和 `OnChanges` 这两个钩子来响应在同一个输入上发生的更改。 - * * @see `OnChanges` * @see [Lifecycle hooks guide](guide/lifecycle-hooks) * - * [生命周期钩子](guide/lifecycle-hooks#onchanges) - * * @usageNotes - * * The following snippet shows how a component can implement this interface * to invoke it own change-detection cycle. * - * 下列代码片段展示了组件如何实现该接口,以执行自定义的变更检测周期。 - * * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='DoCheck'} * * For a more complete example and discussion, see @@ -135,9 +98,7 @@ export interface DoCheck { * See `KeyValueDiffers` and `IterableDiffers` for implementing * custom change checking for collections. * - * 一个回调方法。它会在默认的变更检测器执行之后调用,并进行变更检测。 - * 参见 `KeyValueDiffers` 和 `IterableDiffers`,以实现针对集合对象的自定义变更检测逻辑。 - */ + */ ngDoCheck(): void; } @@ -145,21 +106,12 @@ export interface DoCheck { * A lifecycle hook that is called when a directive, pipe, or service is destroyed. * Use for any custom cleanup that needs to occur when the * instance is destroyed. - * - * 一个生命周期钩子,它会在指令、管道或服务被销毁时调用。 - * 用于在实例被销毁时,执行一些自定义清理代码。 - * * @see [Lifecycle hooks guide](guide/lifecycle-hooks) * - * [生命周期钩子](guide/lifecycle-hooks#onchanges) - * * @usageNotes - * * The following snippet shows how a component can implement this interface * to define its own custom clean-up method. * - * 下列代码片段展示了组件如何实现该接口,以定义它自己的清理逻辑。 - * * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='OnDestroy'} * * @publicApi @@ -168,8 +120,6 @@ export interface OnDestroy { /** * A callback method that performs custom clean-up, invoked immediately * before a directive, pipe, or service instance is destroyed. - * - * 一个用于执行清理逻辑的回调方法,会在指令、管道、服务的实例被销毁后立即调用。 */ ngOnDestroy(): void; } @@ -180,22 +130,14 @@ export interface OnDestroy { * all content of a directive. * Define an `ngAfterContentInit()` method to handle any additional initialization tasks. * - * 一个生命周期钩子,它会在 Angular 完全实例化了指令的所有内容之后调用。 - * 定义一个 `ngAfterContentInit()` 方法来处理额外的初始化任务。 - * * @see `OnInit` * @see `AfterViewInit` * @see [Lifecycle hooks guide](guide/lifecycle-hooks) * - * [生命周期钩子](guide/lifecycle-hooks#onchanges) - * * @usageNotes - * * The following snippet shows how a component can implement this interface to * define its own content initialization method. * - * 下列代码展示了组件如何实现该接口,并定义它自己的内容初始化逻辑。 - * * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='AfterContentInit'} * * @publicApi @@ -206,10 +148,6 @@ export interface AfterContentInit { * Angular has completed initialization of all of the directive's * content. * It is invoked only once when the directive is instantiated. - * - * 一个回调方法,它会在 Angular 初始化完该指令的所有内容之后立即调用。 - * 在指令初始化完成之后,它只会调用一次。 - * */ ngAfterContentInit(): void; } @@ -219,20 +157,13 @@ export interface AfterContentInit { * A lifecycle hook that is called after the default change detector has * completed checking all content of a directive. * - * 一个生命周期钩子,它会在默认的变更检测器对指令的所有内容完成了变更检查之后调用。 - * * @see `AfterViewChecked` * @see [Lifecycle hooks guide](guide/lifecycle-hooks) * - * [生命周期钩子](guide/lifecycle-hooks#onchanges) - * * @usageNotes - * * The following snippet shows how a component can implement this interface to * define its own after-check functionality. * - * 下列代码展示了组件如何实现该接口,已定义它在检查后要执行的自定义功能。 - * * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='AfterContentChecked'} * * @publicApi @@ -242,8 +173,6 @@ export interface AfterContentChecked { * A callback method that is invoked immediately after the * default change detector has completed checking all of the directive's * content. - * - * 一个回调函数,会在默认的变更检测器对该指令下的所有内容完成了变更检测之后立即调用。 */ ngAfterContentChecked(): void; } @@ -254,22 +183,14 @@ export interface AfterContentChecked { * a component's view. * Define an `ngAfterViewInit()` method to handle any additional initialization tasks. * - * 一个生命周期钩子,会在 Angular 完全初始化了组件的视图后调用。 - * 定义一个 `ngAfterViewInit()` 方法来处理一些额外的初始化任务。 - * * @see `OnInit` * @see `AfterContentInit` * @see [Lifecycle hooks guide](guide/lifecycle-hooks) * - * [生命周期钩子](guide/lifecycle-hooks#onchanges) - * * @usageNotes - * * The following snippet shows how a component can implement this interface to * define its own view initialization method. * - * 下列代码片段展示了组件如何实现该接口,以定义自己的视图初始化逻辑。 - * * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='AfterViewInit'} * * @publicApi @@ -280,8 +201,6 @@ export interface AfterViewInit { * Angular has completed initialization of a component's view. * It is invoked only once when the view is instantiated. * - * 一个回调方法,它会在 Angular 完成了组件视图的初始化逻辑之后立即调用。 - * 在视图初始化完成之后,它只会调用一次。 */ ngAfterViewInit(): void; } @@ -291,20 +210,13 @@ export interface AfterViewInit { * A lifecycle hook that is called after the default change detector has * completed checking a component's view for changes. * - * 一个生命周期钩子,它会在默认的变更检测器完成了对组件视图的变更检测之后调用。 - * * @see `AfterContentChecked` * @see [Lifecycle hooks guide](guide/lifecycle-hooks) * - * [生命周期钩子](guide/lifecycle-hooks#onchanges) - * * @usageNotes - * * The following snippet shows how a component can implement this interface to * define its own after-check functionality. * - * 下面的代码片段展示了组件如何实现该接口,以定义在完成检查之后要执行的自定义逻辑。 - * * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='AfterViewChecked'} * * @publicApi @@ -314,8 +226,6 @@ export interface AfterViewChecked { * A callback method that is invoked immediately after the * default change detector has completed one change-check cycle * for a component's view. - * - * 一个回调方法,它会在默认的变更检测器对组件视图完成了一轮变更检测周期之后立即调用。 */ ngAfterViewChecked(): void; } diff --git a/packages/core/src/interface/simple_change.ts b/packages/core/src/interface/simple_change.ts index 21bc5b35b1..eb34f63233 100644 --- a/packages/core/src/interface/simple_change.ts +++ b/packages/core/src/interface/simple_change.ts @@ -11,8 +11,6 @@ * property on a directive instance. Passed as a value in a * {@link SimpleChanges} object to the `ngOnChanges` hook. * - * 表示指令实例上单个属性从先前值到新值的基本变更对象。在 {@link SimpleChanges} 对象中作为值传递给 `ngOnChanges` 挂钩。 - * * @see `OnChanges` * * @publicApi @@ -21,9 +19,6 @@ export class SimpleChange { constructor(public previousValue: any, public currentValue: any, public firstChange: boolean) {} /** * Check whether the new value is the first value assigned. - * - * 检查新值是否是首次赋值的。 - * */ isFirstChange(): boolean { return this.firstChange; @@ -35,8 +30,6 @@ export class SimpleChange { * at the declared property name they belong to on a Directive or Component. This is * the type passed to the `ngOnChanges` hook. * - * 用 {@link SimpleChange} 对象表示的变更的哈希表,这些对象以声明的属性名称存储在指令或组件上,这些属性属于它们。这是传递给 `ngOnChanges` 钩子的类型。 - * * @see `OnChanges` * * @publicApi diff --git a/packages/core/src/interface/type.ts b/packages/core/src/interface/type.ts index a02ee1d353..cbacc039ed 100644 --- a/packages/core/src/interface/type.ts +++ b/packages/core/src/interface/type.ts @@ -11,13 +11,9 @@ * * Represents a type that a Component or other object is instances of. * - * 表示 Component 或其他对象的类型。 - * * An example of a `Type` is `MyCustomComponent` class, which in JavaScript is represented by * the `MyCustomComponent` constructor function. * - * `Type` 的例子之一是 `MyCustomComponent` 类,该类在 JavaScript 中由 `MyCustomComponent` 构造函数表示。 - * * @publicApi */ export const Type = Function; @@ -32,8 +28,6 @@ export function isType(v: any): v is Type<any> { * Represents an abstract class `T`, if applied to a concrete class it would stop being * instantiable. * - * 表示抽象类 `T`,如果将其应用于具体类,它将无法被实例化。 - * * @publicApi */ export interface AbstractType<T> extends Function { diff --git a/packages/core/src/linker/compiler.ts b/packages/core/src/linker/compiler.ts index d76138f106..77a64ed8e3 100644 --- a/packages/core/src/linker/compiler.ts +++ b/packages/core/src/linker/compiler.ts @@ -23,9 +23,7 @@ import {NgModuleFactory} from './ng_module_factory'; /** - * Combination of NgModuleFactory and ComponentFactorys. - * - * NgModuleFactory 和一些 ComponentFactory 的组合。 + * Combination of NgModuleFactory and ComponentFactories. * * @publicApi */ @@ -90,14 +88,10 @@ const Compiler_compileModuleAndAllComponentsAsync = * to create {@link ComponentFactory}s, which * can later be used to create and render a Component instance. * - * 本底层服务用于供 Angular 编译器在运行期间创建 {@link ComponentFactory},该工厂以后可用于创建和渲染组件实例。 - * * Each `@NgModule` provides an own `Compiler` to its injector, * that will use the directives/pipes of the ng module for compilation * of components. * - * 每个 `@NgModule` 为其注入器提供一个自己的编译器,它将使用此 NgModule 的指令/管道来编译组件。 - * * @publicApi */ @Injectable() @@ -105,60 +99,39 @@ export class Compiler { /** * Compiles the given NgModule and all of its components. All templates of the components listed * in `entryComponents` have to be inlined. - * - * 编译给定的 NgModule 及其所有组件。必须内联 `entryComponents` 列出的组件的所有模板。 - * */ compileModuleSync: <T>(moduleType: Type<T>) => NgModuleFactory<T> = Compiler_compileModuleSync; /** * Compiles the given NgModule and all of its components - * - * 编译给定的 NgModule 及其所有组件 - * */ compileModuleAsync: <T>(moduleType: Type<T>) => Promise<NgModuleFactory<T>> = Compiler_compileModuleAsync; /** * Same as {@link #compileModuleSync} but also creates ComponentFactories for all components. - * - * 与 {@link #compileModuleSync} 相同,但还会为所有组件创建 ComponentFactory。 - * */ compileModuleAndAllComponentsSync: <T>(moduleType: Type<T>) => ModuleWithComponentFactories<T> = Compiler_compileModuleAndAllComponentsSync; /** * Same as {@link #compileModuleAsync} but also creates ComponentFactories for all components. - * - * 与 {@link #compileModuleAsync} 相同,但还会为所有组件创建 ComponentFactory。 - * */ compileModuleAndAllComponentsAsync: <T>(moduleType: Type<T>) => Promise<ModuleWithComponentFactories<T>> = Compiler_compileModuleAndAllComponentsAsync; /** * Clears all caches. - * - * 清除所有缓存。 - * */ clearCache(): void {} /** * Clears the cache for the given component/ngModule. - * - * 清除给定组件/ngModule 的缓存。 - * */ clearCacheFor(type: Type<any>) {} /** * Returns the id for a given NgModule, if one is defined and known to the compiler. - * - * 返回给定 NgModule 的 ID(如果已定义且对编译器已知)。 - * */ getModuleId(moduleType: Type<any>): string|undefined { return undefined; @@ -168,8 +141,6 @@ export class Compiler { /** * Options for creating a compiler * - * 用于创建编译器的选项 - * * @publicApi */ export type CompilerOptions = { @@ -183,8 +154,6 @@ export type CompilerOptions = { /** * Token to provide CompilerOptions in the platform injector. * - * 在平台注入器中提供 CompilerOptions 的令牌。 - * * @publicApi */ export const COMPILER_OPTIONS = new InjectionToken<CompilerOptions[]>('compilerOptions'); @@ -192,8 +161,6 @@ export const COMPILER_OPTIONS = new InjectionToken<CompilerOptions[]>('compilerO /** * A factory for creating a Compiler * - * 用于创建编译器的工厂 - * * @publicApi */ export abstract class CompilerFactory { diff --git a/packages/core/src/linker/component_factory.ts b/packages/core/src/linker/component_factory.ts index fe0be07b68..b20fd4184e 100644 --- a/packages/core/src/linker/component_factory.ts +++ b/packages/core/src/linker/component_factory.ts @@ -19,79 +19,50 @@ import {ViewRef} from './view_ref'; * Provides access to the component instance and related objects, * and provides the means of destroying the instance. * - * 表示由 `ComponentFactory` 创建的组件。提供对组件实例和相关对象的访问,并提供销毁实例的方法。 - * * @publicApi */ export abstract class ComponentRef<C> { /** * The host or anchor [element](guide/glossary#element) for this component instance. - * - * 此组件实例的宿主或锚点[元素](guide/glossary#element)。 - * */ abstract get location(): ElementRef; /** * The [dependency injector](guide/glossary#injector) for this component instance. - * - * 此组件实例的[依赖项注入器](guide/glossary#injector)。 - * */ abstract get injector(): Injector; /** * This component instance. - * - * 该组件实例。 - * */ abstract get instance(): C; /** * The [host view](guide/glossary#view-tree) defined by the template * for this component instance. - * - * 模板为此组件实例定义的[宿主视图](guide/glossary#view-tree)。 - * */ abstract get hostView(): ViewRef; /** * The change detector for this component instance. - * - * 此组件实例的变更检测器。 - * */ abstract get changeDetectorRef(): ChangeDetectorRef; /** * The type of this component (as created by a `ComponentFactory` class). - * - * 此组件的类型(由 `ComponentFactory` 类创建)。 - * */ abstract get componentType(): Type<any>; /** * Destroys the component instance and all of the data structures associated with it. - * - * 销毁组件实例以及与其关联的所有数据结构。 - * */ abstract destroy(): void; /** * A lifecycle hook that provides additional developer-defined cleanup * functionality for the component. - * - * 一个生命周期钩子,为组件提供其他由开发人员定义的清理功能。 - * * @param callback A handler function that cleans up developer-defined data * associated with this component. Called when the `destroy()` method is invoked. - * - * 一个处理器函数,用于清理与此组件关联的由开发人员定义的数据。在调用 `destroy()` 方法时调用。 - * */ abstract onDestroy(callback: Function): void; } @@ -101,55 +72,33 @@ export abstract class ComponentRef<C> { * Instantiate a factory for a given type of component with `resolveComponentFactory()`. * Use the resulting `ComponentFactory.create()` method to create a component of that type. * - * 可用来动态创建组件的工厂的基类。`resolveComponentFactory()` 实例化给定类型的组件的工厂。使用生成的 `ComponentFactory.create()` 方法创建该类型的组件。 - * * @see [Dynamic Components](guide/dynamic-component-loader) * - * [动态组件](guide/dynamic-component-loader) - * * @publicApi */ export abstract class ComponentFactory<C> { /** * The component's HTML selector. - * - * 组件的 HTML 选择器。 - * */ abstract get selector(): string; /** * The type of component the factory will create. - * - * 工厂将创建的组件的类型。 - * */ abstract get componentType(): Type<any>; /** * Selector for all <ng-content> elements in the component. - * - * 组件中所有 <ng-content> 元素的选择器。 - * */ abstract get ngContentSelectors(): string[]; /** * The inputs of the component. - * - * 组件的输入。 - * */ abstract get inputs(): {propName: string, templateName: string}[]; /** * The outputs of the component. - * - * 组件的输出。 - * */ abstract get outputs(): {propName: string, templateName: string}[]; /** * Creates a new component. - * - * 创建一个新组件。 - * */ abstract create( injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string|any, diff --git a/packages/core/src/linker/component_factory_resolver.ts b/packages/core/src/linker/component_factory_resolver.ts index 5cbbf4f6ea..7e8db8891f 100644 --- a/packages/core/src/linker/component_factory_resolver.ts +++ b/packages/core/src/linker/component_factory_resolver.ts @@ -39,25 +39,14 @@ class _NullComponentFactoryResolver implements ComponentFactoryResolver { * Use to obtain the factory for a given component type, * then use the factory's `create()` method to create a component of that type. * - * 一个简单的注册表,它将 `Components` 映射到生成的 `ComponentFactory` 类,该类可用于创建组件的实例。用于获取给定组件类型的工厂,然后使用工厂的 `create()` 方法创建该类型的组件。 - * * @see [Dynamic Components](guide/dynamic-component-loader) - * - * [动态组件](guide/dynamic-component-loader) - * * @publicApi */ export abstract class ComponentFactoryResolver { static NULL: ComponentFactoryResolver = new _NullComponentFactoryResolver(); /** * Retrieves the factory object that creates a component of the given type. - * - * 检索能创建给定类型的组件的工厂对象。 - * * @param component The component type. - * - * 组件类型。 - * */ abstract resolveComponentFactory<T>(component: Type<T>): ComponentFactory<T>; } diff --git a/packages/core/src/linker/element_ref.ts b/packages/core/src/linker/element_ref.ts index 824c35c498..c66ca02265 100644 --- a/packages/core/src/linker/element_ref.ts +++ b/packages/core/src/linker/element_ref.ts @@ -40,19 +40,13 @@ const SWITCH_ELEMENT_REF_FACTORY: typeof injectElementRef = SWITCH_ELEMENT_REF_F /** * A wrapper around a native element inside of a View. * - * 对视图中某个原生元素的包装器。 - * * An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM * element. * - * `ElementRef` 的背后是一个可渲染的具体元素。在浏览器中,它通常是一个 DOM 元素。 - * * @security Permitting direct access to the DOM can make your application more vulnerable to * XSS attacks. Carefully review any use of `ElementRef` in your code. For more detail, see the * [Security Guide](https://g.co/ng/security). * - * 允许直接访问 DOM 会导致你的应用在 XSS 攻击前面更加脆弱。要仔细评审对 `ElementRef` 的代码。欲知详情,参见[安全](http://g.co/ng/security)。 - * * @publicApi */ // Note: We don't expose things like `Injector`, `ViewContainer`, ... here, @@ -63,11 +57,8 @@ export class ElementRef<T = any> { * The underlying native element or `null` if direct access to native elements is not supported * (e.g. when the application runs in a web worker). * - * 背后的原生元素,如果不支持直接访问原生元素,则为 `null`(比如:在 Web Worker 环境下运行此应用的时候)。 - * * <div class="callout is-critical"> * <header>Use with caution</header> - * <header>当心!</header> * <p> * Use this API as the last resort when direct access to DOM is needed. Use templating and * data-binding provided by Angular instead. Alternatively you can take a look at {@link @@ -76,17 +67,10 @@ export class ElementRef<T = any> { * supported. * </p> * <p> - * 当需要直接访问 DOM 时,请把本 API 作为最后选择。优先使用 Angular 提供的模板和数据绑定机制。或者你还可以看看 {@link - * Renderer2},它提供了可安全使用的 API —— 即使环境没有提供直接访问原生元素的功能。 - * </p> - * <p> * Relying on direct DOM access creates tight coupling between your application and rendering * layers which will make it impossible to separate the two and deploy your application into a * web worker. * </p> - * <p> - * 如果依赖直接访问 DOM 的方式,就可能在应用和渲染层之间产生紧耦合。这将导致无法分开两者,也就无法将应用发布到 Web Worker 中。 - * </p> * </div> * */ @@ -102,3 +86,13 @@ export class ElementRef<T = any> { */ static __NG_ELEMENT_ID__: () => ElementRef = SWITCH_ELEMENT_REF_FACTORY; } + +/** + * Unwraps `ElementRef` and return the `nativeElement`. + * + * @param value value to unwrap + * @returns `nativeElement` if `ElementRef` otherwise returns value as is. + */ +export function unwrapElementRef<T, R>(value: T|ElementRef<R>): T|R { + return value instanceof ElementRef ? value.nativeElement : value; +} \ No newline at end of file diff --git a/packages/core/src/linker/ng_module_factory.ts b/packages/core/src/linker/ng_module_factory.ts index 96b39cc566..05e4c430e0 100644 --- a/packages/core/src/linker/ng_module_factory.ts +++ b/packages/core/src/linker/ng_module_factory.ts @@ -16,49 +16,32 @@ import {ComponentFactoryResolver} from './component_factory_resolver'; * Represents an instance of an `NgModule` created by an `NgModuleFactory`. * Provides access to the `NgModule` instance and related objects. * - * `NgModule` 创建的 `NgModuleFactory` 的实例。提供对 `NgModule` 实例和相关对象的访问。 - * * @publicApi */ export abstract class NgModuleRef<T> { /** * The injector that contains all of the providers of the `NgModule`. - * - * 包含 `NgModule` 所有提供者的注入器。 - * */ abstract get injector(): Injector; /** * The resolver that can retrieve the component factories * declared in the `entryComponents` property of the module. - * - * 此解析器可以检索本模块的 `entryComponents` 属性中声明的组件工厂。 - * */ abstract get componentFactoryResolver(): ComponentFactoryResolver; /** * The `NgModule` instance. - * - * `NgModule` 实例。 - * */ abstract get instance(): T; /** * Destroys the module instance and all of the data structures associated with it. - * - * 销毁模块实例以及与其关联的所有数据结构。 - * */ abstract destroy(): void; /** * Registers a callback to be executed when the module is destroyed. - * - * 注册一个销毁模块时要执行的回调。 - * */ abstract onDestroy(callback: () => void): void; } diff --git a/packages/core/src/linker/ng_module_factory_loader.ts b/packages/core/src/linker/ng_module_factory_loader.ts index bda867acd6..1586a4418c 100644 --- a/packages/core/src/linker/ng_module_factory_loader.ts +++ b/packages/core/src/linker/ng_module_factory_loader.ts @@ -16,14 +16,9 @@ import {getRegisteredNgModuleType} from './ng_module_factory_registration'; /** * Used to load ng module factories. * - * 用来加载 ng 模块工厂。 - * * @publicApi * @deprecated the `string` form of `loadChildren` is deprecated, and `NgModuleFactoryLoader` is * part of its implementation. See `LoadChildren` for more details. - * - * 不建议使用 `loadChildren` 的 `string` 形式,`NgModuleFactoryLoader` 是其实现的一部分。欲知详情,请参见 `LoadChildren` - * */ export abstract class NgModuleFactoryLoader { abstract load(path: string): Promise<NgModuleFactory<any>>; @@ -45,9 +40,6 @@ export function getModuleFactory__POST_R3__(id: string): NgModuleFactory<any> { * Returns the NgModuleFactory with the given id, if it exists and has been loaded. * Factories for modules that do not specify an `id` cannot be retrieved. Throws if the module * cannot be found. - * - * 返回具有给定 id 的 NgModuleFactory(如果存在并且已加载)。无法检索未指定过 `id` 的模块工厂。如果找不到模块,则抛出该异常。 - * * @publicApi */ export const getModuleFactory: (id: string) => NgModuleFactory<any> = getModuleFactory__PRE_R3__; diff --git a/packages/core/src/linker/ng_module_factory_registration.ts b/packages/core/src/linker/ng_module_factory_registration.ts index 2ca0565b3e..2462d86b1b 100644 --- a/packages/core/src/linker/ng_module_factory_registration.ts +++ b/packages/core/src/linker/ng_module_factory_registration.ts @@ -25,9 +25,6 @@ const modules = new Map<string, NgModuleFactory<any>|NgModuleType>(); /** * Registers a loaded module. Should only be called from generated NgModuleFactory code. - * - * 注册已加载的模块。仅应从生成的 NgModuleFactory 代码中调用。 - * * @publicApi */ export function registerModuleFactory(id: string, factory: NgModuleFactory<any>) { diff --git a/packages/core/src/linker/query_list.ts b/packages/core/src/linker/query_list.ts index 8d160dcee3..26c12e5b24 100644 --- a/packages/core/src/linker/query_list.ts +++ b/packages/core/src/linker/query_list.ts @@ -9,7 +9,7 @@ import {Observable} from 'rxjs'; import {EventEmitter} from '../event_emitter'; -import {flatten} from '../util/array_utils'; +import {arrayEquals, flatten} from '../util/array_utils'; import {getSymbolIterator} from '../util/symbol'; function symbolIterator<T>(this: QueryList<T>): Iterator<T> { @@ -20,33 +20,19 @@ function symbolIterator<T>(this: QueryList<T>): Iterator<T> { * An unmodifiable list of items that Angular keeps up to date when the state * of the application changes. * - * 一个不可修改的条目列表,当应用状态变化时,Angular 会保证它是最新的。 - * * The type of object that {@link ViewChildren}, {@link ContentChildren}, and {@link QueryList} * provide. * - * {@link ViewChildren}、{@link ContentChildren} 和 {@link QueryList} 所提供对象的类型。 - * * Implements an iterable interface, therefore it can be used in both ES6 * javascript `for (var i of items)` loops as well as in Angular templates with * `*ngFor="let i of myList"`. * - * 实现一个可迭代接口,因此它可以用于 ES6 JavaScript 的 `for (var i of items)` 循环,和 Angular 模板中的 `*ngFor="let i of myList"`。 - * * Changes can be observed by subscribing to the changes `Observable`. * - * 可以通过订阅 `changes` 这个 `Observable` 来监听这些变化。 - * * NOTE: In the future this class will implement an `Observable` interface. * - * 注意:将来此类将会实现 `Observable` 接口。 - * * @usageNotes - * * ### Example - * - * ### 例子 - * * ```typescript * @Component({...}) * class Container { @@ -59,15 +45,26 @@ function symbolIterator<T>(this: QueryList<T>): Iterator<T> { export class QueryList<T> implements Iterable<T> { public readonly dirty = true; private _results: Array<T> = []; - public readonly changes: Observable<any> = new EventEmitter(); + private _changesDetected: boolean = false; + private _changes: EventEmitter<QueryList<T>>|null = null; readonly length: number = 0; - // TODO(issue/24571): remove '!'. - readonly first!: T; - // TODO(issue/24571): remove '!'. - readonly last!: T; + readonly first: T = undefined!; + readonly last: T = undefined!; - constructor() { + /** + * Returns `Observable` of `QueryList` notifying the subscriber of changes. + */ + get changes(): Observable<any> { + return this._changes || (this._changes = new EventEmitter()); + } + + /** + * @param emitDistinctChangesOnly Whether `QueryList.changes` should fire only when actual change + * has occurred. Or if it should fire when query is recomputed. (recomputing could resolve in + * the same result) + */ + constructor(private _emitDistinctChangesOnly: boolean = false) { // This function should be declared on the prototype, but doing so there will cause the class // declaration to have side-effects and become not tree-shakable. For this reason we do it in // the constructor. @@ -79,9 +76,6 @@ export class QueryList<T> implements Iterable<T> { /** * Returns the QueryList entry at `index`. - * - * 返回位于 `index` 处的 QueryList 条目。 - * */ get(index: number): T|undefined { return this._results[index]; @@ -90,8 +84,6 @@ export class QueryList<T> implements Iterable<T> { /** * See * [Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) - * - * 参见 [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); @@ -100,8 +92,6 @@ export class QueryList<T> implements Iterable<T> { /** * See * [Array.filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) - * - * 参见 [Array.filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) */ filter(fn: (item: T, index: number, array: T[]) => boolean): T[] { return this._results.filter(fn); @@ -110,8 +100,6 @@ export class QueryList<T> implements Iterable<T> { /** * See * [Array.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) - * - * 参见 [Array.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) */ find(fn: (item: T, index: number, array: T[]) => boolean): T|undefined { return this._results.find(fn); @@ -120,8 +108,6 @@ export class QueryList<T> implements Iterable<T> { /** * See * [Array.reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) - * - * 参见 [Array.reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) */ reduce<U>(fn: (prevValue: U, curValue: T, curIndex: number, array: T[]) => U, init: U): U { return this._results.reduce(fn, init); @@ -130,8 +116,6 @@ export class QueryList<T> implements Iterable<T> { /** * See * [Array.forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) - * - * 参见 [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); @@ -140,8 +124,6 @@ export class QueryList<T> implements Iterable<T> { /** * See * [Array.some](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some) - * - * 参见 [Array.some](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some) */ some(fn: (value: T, index: number, array: T[]) => boolean): boolean { return this._results.some(fn); @@ -149,9 +131,6 @@ export class QueryList<T> implements Iterable<T> { /** * Returns a copy of the internal results list as an Array. - * - * 以数组形式返回内部结果列表的副本。 - * */ toArray(): T[] { return this._results.slice(); @@ -166,28 +145,33 @@ export class QueryList<T> implements Iterable<T> { * on change detection, it will not notify of changes to the queries, unless a new change * occurs. * - * 更新查询列表中存储的数据,并将 `dirty` 标志重置为 `false`,以便当检测到变更时,除非发生新变更,否则不会通知这些查询的变更。 - * * @param resultsTree The query results to store - * - * 要存储的查询结果 + * @param identityAccessor Optional function for extracting stable object identity from a value + * in the array. This function is executed for each element of the query result list while + * comparing current query list with the new one (provided as a first argument of the `reset` + * function) to detect if the lists are different. If the function is not provided, elements + * are compared as is (without any pre-processing). */ - 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]; + reset(resultsTree: Array<T|any[]>, identityAccessor?: (value: T) => unknown): void { + // Cast to `QueryListInternal` so that we can mutate fields which are readonly for the usage of + // QueryList (but not for QueryList itself.) + const self = this as QueryListInternal<T>; + (self as {dirty: boolean}).dirty = false; + const newResultFlat = flatten(resultsTree); + if (this._changesDetected = !arrayEquals(self._results, newResultFlat, identityAccessor)) { + self._results = newResultFlat; + self.length = newResultFlat.length; + self.last = newResultFlat[this.length - 1]; + self.first = newResultFlat[0]; + } } /** * Triggers a change event by emitting on the `changes` {@link EventEmitter}. - * - * 通过发出 `changes` {@link EventEmitter} 来触发变更事件。 - * */ notifyOnChanges(): void { - (this.changes as EventEmitter<any>).emit(this); + if (this._changes && (this._changesDetected || !this._emitDistinctChangesOnly)) + this._changes.emit(this); } /** internal */ @@ -208,3 +192,14 @@ export class QueryList<T> implements Iterable<T> { // over QueryLists to work correctly, since QueryList must be assignable to NgIterable. [Symbol.iterator]!: () => Iterator<T>; } + +/** + * Internal set of APIs used by the framework. (not to be made public) + */ +interface QueryListInternal<T> extends QueryList<T> { + reset(a: any[]): void; + notifyOnChanges(): void; + length: number; + last: T; + first: T; +} \ No newline at end of file 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 5052f0ecd4..625467febd 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 @@ -23,30 +23,19 @@ declare var System: any; * Configuration for SystemJsNgModuleLoader. * token. * - * SystemJsNgModuleLoader 的配置。令牌。 - * * @publicApi * @deprecated the `string` form of `loadChildren` is deprecated, and `SystemJsNgModuleLoaderConfig` * is part of its implementation. See `LoadChildren` for more details. - * - * 不推荐使用 `loadChildren` 的 `string` 形式 `SystemJsNgModuleLoaderConfig` 是其实现的一部分。有关更多详细信息,请参见 `LoadChildren` - * */ 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; /** * Suffix to add when computing the name of the factory module for a given module name. - * - * 计算给定模块名称的工厂模块名称时添加的后缀。 - * */ // TODO(issue/24571): remove '!'. factoryPathSuffix!: string; @@ -59,15 +48,9 @@ const DEFAULT_CONFIG: SystemJsNgModuleLoaderConfig = { /** * NgModuleFactoryLoader that uses SystemJS to load NgModuleFactory - * - * 使用 SystemJS 加载 NgModuleFactory 的 NgModuleFactoryLoader - * * @publicApi * @deprecated the `string` form of `loadChildren` is deprecated, and `SystemJsNgModuleLoader` is * part of its implementation. See `LoadChildren` for more details. - * - * 该 `string` 的形式 `loadChildren` 已被弃用, `SystemJsNgModuleLoader` 是其实现的一部分。有关更多详细信息,请参见 `LoadChildren` - * */ @Injectable() export class SystemJsNgModuleLoader implements NgModuleFactoryLoader { diff --git a/packages/core/src/linker/template_ref.ts b/packages/core/src/linker/template_ref.ts index b21dfd3e10..775815c494 100644 --- a/packages/core/src/linker/template_ref.ts +++ b/packages/core/src/linker/template_ref.ts @@ -28,47 +28,30 @@ const SWITCH_TEMPLATE_REF_FACTORY: typeof injectTemplateRef = SWITCH_TEMPLATE_RE * To instantiate embedded views based on a template, use the `ViewContainerRef` * method `createEmbeddedView()`. * - * 表示一个内嵌模板,它可用于实例化内嵌的视图。 - * 要想根据模板实例化内嵌的视图,请使用 `ViewContainerRef` 的 `createEmbeddedView()` 方法。 - * * Access a `TemplateRef` instance by placing a directive on an `<ng-template>` * element (or directive prefixed with `*`). The `TemplateRef` for the embedded view * is injected into the constructor of the directive, * using the `TemplateRef` token. * - * 通过把一个指令放在 `<ng-template>` 元素(或一个带 `*` 前缀的指令)上,可以访问 `TemplateRef` 的实例。 - * 内嵌视图的 `TemplateRef` 实例会以 `TemplateRef` 作为令牌,注入到该指令的构造函数中。 - * * You can also use a `Query` to find a `TemplateRef` associated with * a component or a directive. * - * 你还可以使用 `Query` 来找出与某个组件或指令相关的 `TemplateRef`。 - * * @see `ViewContainerRef` * @see [Navigate the Component Tree with DI](guide/dependency-injection-navtree) * - * [使用 DI 在组件树中导航](guide/dependency-injection-navtree) - * * @publicApi */ export abstract class TemplateRef<C> { /** * The anchor element in the parent view for this embedded view. * - * 内嵌视图在其所属视图中的位置。 - * * The data-binding and injection contexts of embedded views created from this `TemplateRef` * inherit from the contexts of this location. * - * 对于从这个 `TemplateRef` 创建的内嵌视图,其数据绑定和依赖注入的上下文是从当前位置的上下文中继承而来的。 - * * Typically new embedded views are attached to the view container of this location, but in * advanced use-cases, the view can be attached to a different container while keeping the * data-binding and injection context from the original location. * - * 通常,新的内嵌视图会被附加到当前位置的视图容器中,但是在一些高级用例中,该视图可能被附加到别的容器中, - * 同时还保留原位置的数据绑定和依赖注入上下文。 - * */ // TODO(i): rename to anchor or location abstract get elementRef(): ElementRef; @@ -76,18 +59,9 @@ export abstract class TemplateRef<C> { /** * Instantiates an embedded view based on this template, * and attaches it to the view container. - * - * 创建一个视图对象,并把它附着到父视图的视图容器上。 - * * @param context The data-binding context of the embedded view, as declared * in the `<ng-template>` usage. - * - * 这个新视图的上下文环境,继承自所附着的元素。 - * * @returns The new embedded view object. - * - * 这个新的视图对象。 - * */ abstract createEmbeddedView(context: C): EmbeddedViewRef<C>; diff --git a/packages/core/src/linker/view_container_ref.ts b/packages/core/src/linker/view_container_ref.ts index 1e11d47925..b37e423efc 100644 --- a/packages/core/src/linker/view_container_ref.ts +++ b/packages/core/src/linker/view_container_ref.ts @@ -40,19 +40,13 @@ const SWITCH_VIEW_CONTAINER_REF_FACTORY: typeof injectViewContainerRef = /** * Represents a container where one or more views can be attached to a component. * - * 表示可以将一个或多个视图附着到组件中的容器。 - * * Can contain *host views* (created by instantiating a * component with the `createComponent()` method), and *embedded views* * (created by instantiating a `TemplateRef` with the `createEmbeddedView()` method). * - * 可以包含*宿主视图*(当用 `createComponent()` 方法实例化组件时创建)和*内嵌视图*(当用 `createEmbeddedView()` 方法实例化 `TemplateRef` 时创建)。 - * * A view container instance can contain other view containers, * creating a [view hierarchy](guide/glossary#view-tree). * - * 视图容器的实例还可以包含其它视图容器,以创建[层次化视图](guide/glossary#view-tree)。 - * * @see `ComponentRef` * @see `EmbeddedViewRef` * @@ -64,90 +58,50 @@ export abstract class ViewContainerRef { * Each view container can have only one anchor element, and each anchor element * can have only a single view container. * - * 锚点元素用来指定本容器在父容器视图中的位置。 - * 每个视图容器都只能有一个锚点元素,每个锚点元素也只能属于一个视图容器。 - * * Root elements of views attached to this container become siblings of the anchor element in * the rendered view. * - * 视图的根元素会附着到该容器上,在渲染好的视图中会变成锚点元素的兄弟。 - * * Access the `ViewContainerRef` of an element by placing a `Directive` injected * with `ViewContainerRef` on the element, or use a `ViewChild` query. * - * 可以在元素上放置注入了 `ViewContainerRef` 的 `Directive` 来访问元素的 `ViewContainerRef`。也可以使用 `ViewChild` 进行查询。 - * * <!-- TODO: rename to anchorElement --> */ abstract get element(): ElementRef; /** * The [dependency injector](guide/glossary#injector) for this view container. - * - * 该视图容器的[依赖注入器](guide/glossary#injector)。 */ abstract get injector(): Injector; - /** - * @deprecated No replacement - * - * 无替代品 - * - */ + /** @deprecated No replacement */ abstract get parentInjector(): Injector; /** * Destroys all views in this container. - * - * 销毁本容器中的所有视图。 */ abstract clear(): void; /** * Retrieves a view from this container. - * - * 从该容器中获取一个视图 - * * @param index The 0-based index of the view to retrieve. - * - * 所要获取视图的从 0 开始的索引。 - * * @returns The `ViewRef` instance, or null if the index is out of range. - * - * `ViewRef` 实例,如果索引超出范围则为 0。 */ abstract get(index: number): ViewRef|null; /** * Reports how many views are currently attached to this container. - * - * 报告目前附加到本容器的视图的数量。 - * * @returns The number of views. - * - * 视图的数量。 */ abstract get length(): number; /** * Instantiates an embedded view and inserts it * into this container. - * - * 实例化一个内嵌视图,并把它插入到该容器中。 - * * @param templateRef The HTML template that defines the view. - * - * 用来定义视图的 HTML 模板。 - * * @param index The 0-based index at which to insert the new view into this container. * If not specified, appends the new view as the last entry. * - * 从 0 开始的索引,表示新视图要插入到当前容器的哪个位置。 - * 如果没有指定,就把新的视图追加到最后。 - * * @returns The `ViewRef` instance for the newly created view. - * - * 新创建的这个视图的 `ViewRef` 实例。 */ abstract createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number): EmbeddedViewRef<C>; @@ -155,30 +109,15 @@ export abstract class ViewContainerRef { /** * Instantiates a single component and inserts its host view into this container. * - * 实例化一个 {@link Component} 并把它的宿主视图插入到本容器的指定 `index` 处。 - * * @param componentFactory The factory to use. - * - * 要使用的工厂。 - * * @param index The index at which to insert the new component's host view into this container. * If not specified, appends the new view as the last entry. - * - * 从 0 开始的索引,表示新组件的宿主视图要插入到当前容器的哪个位置。 - * 如果没有指定,就把新的视图追加到最后。 - * * @param injector The injector to use as the parent for the new component. - * - * 一个注入器,将用作新组件的父注入器。 - * * @param projectableNodes * @param ngModule * * @returns The new component instance, containing the host view. * - * 新组件的实例,包含宿主视图。 - * - * */ abstract createComponent<C>( componentFactory: ComponentFactory<C>, index?: number, injector?: Injector, @@ -186,87 +125,42 @@ export abstract class ViewContainerRef { /** * Inserts a view into this container. - * - * 把一个视图插入到当前容器中。 - * * @param viewRef The view to insert. - * - * 要插入的视图。 - * * @param index The 0-based index at which to insert the view. * If not specified, appends the new view as the last entry. - * - * 从 0 开始的索引,表示该视图要插入到当前容器的哪个位置。 - * 如果没有指定,就把新的视图追加到最后。 - * * @returns The inserted `ViewRef` instance. * - * 插入后的 `ViewRef` 实例。 - * */ abstract insert(viewRef: ViewRef, index?: number): ViewRef; /** * Moves a view to a new location in this container. - * - * 把一个视图移到容器中的新位置。 - * * @param viewRef The view to move. - * - * 要移动的视图。 - * * @param index The 0-based index of the new location. - * - * 从 0 开始索引,用于表示新位置。 - * * @returns The moved `ViewRef` instance. - * - * 移动后的 `ViewRef` 实例。 - * */ abstract move(viewRef: ViewRef, currentIndex: number): ViewRef; /** * Returns the index of a view within the current container. - * - * 返回某个视图在当前容器中的索引。 - * * @param viewRef The view to query. - * - * 要查询的视图。 - * * @returns The 0-based index of the view's position in this container, * or `-1` if this container doesn't contain the view. - * - * 本视图在其容器中的从 0 开始的索引,如果没找到,则返回 `-1`。 */ abstract indexOf(viewRef: ViewRef): number; /** * Destroys a view attached to this container - * - * 销毁附着在该容器中的某个视图 - * * @param index The 0-based index of the view to destroy. * If not specified, the last view in the container is removed. - * - * 要销毁的视图的从 0 开始的索引。 - * 如果不指定 `index`,则移除容器中的最后一个视图。 */ abstract remove(index?: number): void; /** * Detaches a view from this container without destroying it. * Use along with `insert()` to move a view within the current container. - * - * 从当前容器中分离某个视图,但不会销毁它。 - * 通常会和 `insert()` 一起使用,在当前容器中移动一个视图。 - * * @param index The 0-based index of the view to detach. * If not specified, the last view in the container is detached. - * - * 要分离的视图的从 0 开始的索引。 - * 如果省略 `index` 参数,则拆出最后一个 {@link ViewRef}。 */ abstract detach(index?: number): ViewRef|null; @@ -413,7 +307,7 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef { addViewToContainer(tView, lContainer[T_HOST], renderer, lView, parentRNode, beforeNode); } - (viewRef as R3ViewRef<any>).attachToViewContainerRef(this); + (viewRef as R3ViewRef<any>).attachToViewContainerRef(); addToArray(getOrCreateViewRefs(lContainer), adjustedIdx, viewRef); return viewRef; diff --git a/packages/core/src/linker/view_ref.ts b/packages/core/src/linker/view_ref.ts index 11c9611567..315f86e6d7 100644 --- a/packages/core/src/linker/view_ref.ts +++ b/packages/core/src/linker/view_ref.ts @@ -11,8 +11,6 @@ import {ChangeDetectorRef} from '../change_detection/change_detector_ref'; /** * Represents an Angular [view](guide/glossary#view "Definition"). * - * 表示一个 Angular [视图](guide/glossary#view "Definition")。 - * * @see {@link ChangeDetectorRef#usage-notes Change detection usage} * * @publicApi @@ -20,35 +18,20 @@ import {ChangeDetectorRef} from '../change_detection/change_detector_ref'; export abstract class ViewRef extends ChangeDetectorRef { /** * Destroys this view and all of the data structures associated with it. - * - * 销毁该视图以及与之关联的所有数据结构。 - * */ abstract destroy(): void; /** * Reports whether this view has been destroyed. - * - * 报告此视图是否已被销毁。 - * * @returns True after the `destroy()` method has been called, false otherwise. - * - * 调用 `destroy()` 方法后为 true,否则为 false。 - * */ abstract get destroyed(): boolean; /** * A lifecycle hook that provides additional developer-defined cleanup * functionality for views. - * - * 生命周期钩子,为视图提供其他由开发人员定义的清理功能。 - * * @param callback A handler function that cleans up developer-defined data * associated with a view. Called when the `destroy()` method is invoked. - * - * 处理函数,用于清理与视图关联的由开发人员定义的数据。在调用 `destroy()` 方法时调用。 - * */ abstract onDestroy(callback: Function): any /** TODO #9100 */; } @@ -59,14 +42,10 @@ export abstract class ViewRef extends ChangeDetectorRef { * other than the hosting component whose template defines it, or it can be defined * independently by a `TemplateRef`. * - * 表示视图容器中的 Angular [视图](guide/glossary#view)。[嵌入视图](guide/glossary#view-tree)可以从在模板中定义它的宿主组件之外的组件中引用,也可以由 `TemplateRef` 进行独立定义。 - * * Properties of elements in a view can change, but the structure (number and order) of elements in * a view cannot. Change the structure of elements by inserting, moving, or * removing nested views in a view container. * - * 视图中元素的属性可以更改,但是视图中元素的结构(数量和顺序)不能更改。通过在视图容器中插入,移动或删除嵌套视图来更改元素的结构。 - * * @see `ViewContainerRef` * * @usageNotes @@ -74,8 +53,6 @@ export abstract class ViewRef extends ChangeDetectorRef { * The following template breaks down into two separate `TemplateRef` instances, * an outer one and an inner one. * - * 以下模板分为两个单独的 `TemplateRef` 实例,一个外部实例和一个内部实例。 - * * ``` * Count: {{items.length}} * <ul> @@ -85,8 +62,6 @@ export abstract class ViewRef extends ChangeDetectorRef { * * This is the outer `TemplateRef`: * - * 这是外部 `TemplateRef` : - * * ``` * Count: {{items.length}} * <ul> @@ -96,16 +71,12 @@ export abstract class ViewRef extends ChangeDetectorRef { * * This is the inner `TemplateRef`: * - * 这是内部的 `TemplateRef` : - * * ``` * <li>{{item}}</li> * ``` * * The outer and inner `TemplateRef` instances are assembled into views as follows: * - * 外部和内部 `TemplateRef` 实例按如下方式组装到视图中: - * * ``` * <!-- ViewRef: outer-0 --> * Count: 2 @@ -121,17 +92,11 @@ export abstract class ViewRef extends ChangeDetectorRef { export abstract class EmbeddedViewRef<C> extends ViewRef { /** * The context for this view, inherited from the anchor element. - * - * 该视图的上下文,继承自锚点元素。 - * */ - abstract get context(): C; + abstract context: C; /** * The root nodes for this embedded view. - * - * 此嵌入式视图的根节点。 - * */ abstract get rootNodes(): any[]; } diff --git a/packages/core/src/metadata/di.ts b/packages/core/src/metadata/di.ts index 3f60a7ee28..1a793bbde4 100644 --- a/packages/core/src/metadata/di.ts +++ b/packages/core/src/metadata/di.ts @@ -17,16 +17,12 @@ import {makePropDecorator} from '../util/decorators'; * All components that are referenced in the `useValue` value (either directly * or in a nested array or map) are added to the `entryComponents` property. * - * 可用于创建虚拟[提供者](guide/glossary#provider)的 DI 令牌,该虚拟提供者将基于其 `useValue` 属性值填充组件和 NgModule 的 `entryComponents` 字段。`useValue` 值中引用的所有组件(无论是直接还是在嵌套数组还是在映射表中)都将添加到 `entryComponents` 属性。 - * * @usageNotes * * The following example shows how the router can populate the `entryComponents` * field of an NgModule based on a router configuration that refers * to components. * - * 以下示例演示了路由器如何基于引用组件的路由器配置设置 `entryComponents` 字段。 - * * ```typescript * // helper function inside the router * function provideRoutes(routes) { @@ -50,42 +46,30 @@ import {makePropDecorator} from '../util/decorators'; * * @publicApi * @deprecated Since 9.0.0. With Ivy, this property is no longer necessary. - * - * 从 9.0.0 开始。使用 Ivy,不再需要此属性。 */ export const ANALYZE_FOR_ENTRY_COMPONENTS = new InjectionToken<any>('AnalyzeForEntryComponents'); /** * Type of the `Attribute` decorator / constructor function. * - * `Attribute` 装饰器/构造函数的类型。 - * * @publicApi */ export interface AttributeDecorator { /** * Specifies that a constant attribute value should be injected. * - * 指定应注入的常量属性值。 - * * The directive can inject constant string literals of host element attributes. * - * 该指令可以注入宿主元素属性的字符串字面常量。 - * * @usageNotes * * Suppose we have an `<input>` element and want to know its `type`. * - * 假设我们有一个 `<input>` 元素,并且想知道它的 `type` 。 - * * ```html * <input type="text"> * ``` * * A decorator can inject string literal `text` as in the following example. * - * 装饰器可以注入字符串字面量 `text` ,如下面的例子。 - * * {@example core/ts/metadata/metadata.ts region='attributeMetadata'} * * @publicApi @@ -98,16 +82,11 @@ export interface AttributeDecorator { /** * Type of the Attribute metadata. * - * 属性元数据的类型。 - * * @publicApi */ export interface Attribute { /** * The name of the attribute to be injected into the constructor. - * - * 要注入到构造函数中的属性的名称。 - * */ attributeName?: string; } @@ -115,12 +94,11 @@ export interface Attribute { /** * Type of the Query metadata. * - * 查询元数据的类型。 - * * @publicApi */ export interface Query { descendants: boolean; + emitDistinctChangesOnly: boolean; first: boolean; read: any; isViewQuery: boolean; @@ -128,11 +106,14 @@ export interface Query { static?: boolean; } +// Stores the default value of `emitDistinctChangesOnly` when the `emitDistinctChangesOnly` is not +// explicitly set. +export const emitDistinctChangesOnlyDefaultValue = true; + + /** * Base class for query metadata. * - * 查询元数据的基类。 - * * @see `ContentChildren`. * @see `ContentChild`. * @see `ViewChildren`. @@ -145,8 +126,6 @@ export abstract class Query {} /** * Type of the ContentChildren decorator / constructor function. * - * ContentChildren 装饰器/构造函数的类型。 - * * @see `ContentChildren`. * @publicApi */ @@ -154,45 +133,30 @@ export interface ContentChildrenDecorator { /** * Parameter decorator that configures a content query. * - * 用于配置内容查询的参数装饰器。 - * * Use to get the `QueryList` of elements or directives from the content DOM. * Any time a child element is added, removed, or moved, the query list will be * updated, and the changes observable of the query list will emit a new value. * - * 用于从内容 DOM 中获取元素或指令的 `QueryList`。每当添加、删除或移动子元素时,此查询列表都会更新,并且其可观察对象 changes 都会发出新值。 - * * Content queries are set before the `ngAfterContentInit` callback is called. * - * 在调用 `ngAfterContentInit` 回调之前设置的内容查询。 - * * Does not retrieve elements or directives that are in other components' templates, * since a component's template is always a black box to its ancestors. * - * 不检索其他组件模板中的元素或指令,因为组件模板对其祖先来说始终是黑匣子。 - * * **Metadata Properties**: * - * **元数据属性**: - * * * **selector** - The directive type or the name used for querying. - * - * **selector** - 要查询的指令类型或名称。 - * * * **descendants** - True to include all descendants, otherwise include only direct children. - * - * **后代** - 包含所有后代时为 true,否则仅包括直接子代。 - * + * * **emitDistinctChangesOnly** - The ` QueryList#changes` observable will emit new values only + * if the QueryList result has changed. When `false` the `changes` observable might emit even + * if the QueryList has not changed. + * ** Note: *** This config option is **deprecated**, it will be permanently set to `true` and + * removed in future versions of Angular. * * **read** - Used to read a different token from the queried elements. * - * **read** - 用于从查询到的元素中读取不同的令牌。 - * * @usageNotes * * Here is a simple demonstration of how the `ContentChildren` decorator can be used. * - * 这里是如何使用 `ContentChildren` 装饰器的简单演示。 - * * {@example core/di/ts/contentChildren/content_children_howto.ts region='HowTo'} * * ### Tab-pane example @@ -204,16 +168,18 @@ export interface ContentChildrenDecorator { * * @Annotation */ - (selector: Type<any>|InjectionToken<unknown>|Function|string, - opts?: {descendants?: boolean, read?: any}): any; + (selector: Type<any>|InjectionToken<unknown>|Function|string, opts?: { + descendants?: boolean, + emitDistinctChangesOnly?: boolean, + read?: any, + }): any; new(selector: Type<any>|InjectionToken<unknown>|Function|string, - opts?: {descendants?: boolean, read?: any}): Query; + opts?: {descendants?: boolean, emitDistinctChangesOnly?: boolean, read?: any}): Query; } /** * Type of the ContentChildren metadata. * - * ContentChildren 元数据的类型。 * * @Annotation * @publicApi @@ -223,61 +189,46 @@ export type ContentChildren = Query; /** * ContentChildren decorator and metadata. * - * ContentChildren 装饰器和元数据。 * * @Annotation * @publicApi */ export const ContentChildren: ContentChildrenDecorator = makePropDecorator( - 'ContentChildren', - (selector?: any, data: any = {}) => - ({selector, first: false, isViewQuery: false, descendants: false, ...data}), + 'ContentChildren', (selector?: any, data: any = {}) => ({ + selector, + first: false, + isViewQuery: false, + descendants: false, + emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue, + ...data + }), Query); /** * Type of the ContentChild decorator / constructor function. * - * ContentChild 装饰器/构造函数的类型。 - * * @publicApi */ export interface ContentChildDecorator { /** * Parameter decorator that configures a content query. * - * 用于配置内容查询的参数装饰器。 - * * Use to get the first element or the directive matching the selector from the content DOM. * If the content DOM changes, and a new child matches the selector, * the property will be updated. * - * 用于从内容 DOM 获取与此选择器匹配的第一个元素或指令。如果内容 DOM 发生了更改,并且有一个新的子项与选择器匹配,则该属性将被更新。 - * * Content queries are set before the `ngAfterContentInit` callback is called. * - * 在调用 `ngAfterContentInit` 之前设置的内容查询。 - * * Does not retrieve elements or directives that are in other components' templates, * since a component's template is always a black box to its ancestors. * - * 不检索其他组件模板中的元素或指令,因为组件模板对其祖先来说始终是黑匣子。 - * * **Metadata Properties**: * - * **元数据属性**: - * * * **selector** - The directive type or the name used for querying. - * - * **selector** - 要查询的指令类型或名称。 - * * * **read** - Used to read a different token from the queried element. - * - * **read** - 用于从查询到的元素读取不同的令牌。 - * * * **static** - True to resolve query results before change detection runs, * false to resolve after change detection. Defaults to false. * - * **static** - 如果为 true,则在变更检测运行之前解析查询结果,如果为 false,则在变更检测之后解析。默认为 false。 * @usageNotes * * {@example core/di/ts/contentChild/content_child_howto.ts region='HowTo'} @@ -297,8 +248,6 @@ export interface ContentChildDecorator { /** * Type of the ContentChild metadata. * - * ContentChild 元数据的类型。 - * * @publicApi */ export type ContentChild = Query; @@ -306,7 +255,6 @@ export type ContentChild = Query; /** * ContentChild decorator and metadata. * - * ContentChild 装饰器和元数据。 * * @Annotation * @@ -321,8 +269,6 @@ export const ContentChild: ContentChildDecorator = makePropDecorator( /** * Type of the ViewChildren decorator / constructor function. * - * ViewChildren 装饰器/构造函数的类型。 - * * @see `ViewChildren`. * * @publicApi @@ -331,29 +277,22 @@ export interface ViewChildrenDecorator { /** * Parameter decorator that configures a view query. * - * 用于配置视图查询的参数装饰器。 - * * Use to get the `QueryList` of elements or directives from the view DOM. * Any time a child element is added, removed, or moved, the query list will be updated, * and the changes observable of the query list will emit a new value. * - * 用于从视图 DOM 中获取元素或指令的 `QueryList`。每当添加、删除或移动子元素时,此查询列表都将更新,并且其可观察对象 changes 将发出新值。 - * * View queries are set before the `ngAfterViewInit` callback is called. * - * 在调用 `ngAfterViewInit` 前设置的视图查询。 - * * **Metadata Properties**: * - * **元数据属性**: - * * * **selector** - The directive type or the name used for querying. - * - * **selector** - 要查询的指令类型或名称。 - * * * **read** - Used to read a different token from the queried elements. + * * **emitDistinctChangesOnly** - The ` QueryList#changes` observable will emit new values only + * if the QueryList result has changed. When `false` the `changes` observable might emit even + * if the QueryList has not changed. + * ** Note: *** This config option is **deprecated**, it will be permanently set to `true` and + * removed in future versions of Angular. * - * **read** - 用于从查询的元素中读取不同的令牌。 * @usageNotes * * {@example core/di/ts/viewChildren/view_children_howto.ts region='HowTo'} @@ -364,16 +303,15 @@ export interface ViewChildrenDecorator { * * @Annotation */ - (selector: Type<any>|InjectionToken<unknown>|Function|string, opts?: {read?: any}): any; + (selector: Type<any>|InjectionToken<unknown>|Function|string, + opts?: {read?: any, emitDistinctChangesOnly?: boolean}): any; new(selector: Type<any>|InjectionToken<unknown>|Function|string, - opts?: {read?: any}): ViewChildren; + opts?: {read?: any, emitDistinctChangesOnly?: boolean}): ViewChildren; } /** * Type of the ViewChildren metadata. * - * ViewChildren 元数据的类型。 - * * @publicApi */ export type ViewChildren = Query; @@ -381,22 +319,23 @@ export type ViewChildren = Query; /** * ViewChildren decorator and metadata. * - * ViewChildren 装饰器和元数据。 - * * @Annotation * @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, + emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue, + ...data + }), Query); /** * Type of the ViewChild decorator / constructor function. * - * ViewChild 的装饰器类型和构造函数 - * * @see `ViewChild`. * @publicApi */ @@ -408,68 +347,33 @@ export interface ViewChildDecorator { * in the view DOM. If the view DOM changes, and a new child matches the selector, * the property is updated. * - * 属性装饰器,用于配置一个视图查询。 - * 变更检测器会在视图的 DOM 中查找能匹配上该选择器的第一个元素或指令。 - * 如果视图的 DOM 发生了变化,出现了匹配该选择器的新的子节点,该属性就会被更新。 - * * View queries are set before the `ngAfterViewInit` callback is called. * - * 在调用 `NgAfterViewInit` 回调函数之前就会设置这些视图查询。 - * * **Metadata Properties**: * - * **元数据属性**: - * * * **selector** - The directive type or the name used for querying. - * - * **selector** - 用于查询的指令类型或名字。 - * * * **read** - Used to read a different token from the queried elements. - * - * **read** - 从查询到的元素中读取另一个令牌。 - * * * **static** - True to resolve query results before change detection runs, * false to resolve after change detection. Defaults to false. * - * **static** - 如果为 true,则在变更检测运行之前解析查询结果,如果为 false,则在变更检测之后解析。默认为 false。 * * The following selectors are supported. - * - * 支持下列选择器: - * * * Any class with the `@Component` or `@Directive` decorator - * - * 任何带有 `@Component` 或 `@Directive` 装饰器的类 - * * * A template reference variable as a string (e.g. query `<my-component #cmp></my-component>` * with `@ViewChild('cmp')`) - * - * 字符串形式的模板引用变量(比如可以使用 `@ViewChild('cmp')` 来查询 `<my-component #cmp></my-component>` - * * * Any provider defined in the child component tree of the current component (e.g. * `@ViewChild(SomeService) someService: SomeService`) - * - * 组件树中任何当前组件的子组件所定义的提供者(比如 `@ViewChild(SomeService) someService: SomeService` ) - * * * Any provider defined through a string token (e.g. `@ViewChild('someToken') someTokenVal: * any`) - * - * 任何通过字符串令牌定义的提供者(比如 `@ViewChild('someToken') someTokenVal: - * any`) - * * * A `TemplateRef` (e.g. query `<ng-template></ng-template>` with `@ViewChild(TemplateRef) * template;`) * - * `TemplateRef`(比如可以用 `@ViewChild(TemplateRef) template;` 来查询 `<ng-template></ng-template>`) - * * @usageNotes * * {@example core/di/ts/viewChild/view_child_example.ts region='Component'} * * ### Example 2 * - * ### 例子 - * * {@example core/di/ts/viewChild/view_child_howto.ts region='HowTo'} * * @Annotation @@ -483,8 +387,6 @@ export interface ViewChildDecorator { /** * Type of the ViewChild metadata. * - * ViewChild 元数据的类型。 - * * @publicApi */ export type ViewChild = Query; @@ -492,8 +394,6 @@ export type ViewChild = Query; /** * ViewChild decorator and metadata. * - * ViewChild 装饰器和元数据。 - * * @Annotation * @publicApi */ diff --git a/packages/core/src/metadata/directives.ts b/packages/core/src/metadata/directives.ts index 1681fcbf26..660b1d72d7 100644 --- a/packages/core/src/metadata/directives.ts +++ b/packages/core/src/metadata/directives.ts @@ -20,9 +20,6 @@ import {ViewEncapsulation} from './view'; /** * Type of the Directive decorator / constructor function. - * - * 指令装饰器的类型和构造函数。 - * * @publicApi */ export interface DirectiveDecorator { @@ -30,27 +27,17 @@ export interface DirectiveDecorator { * Decorator that marks a class as an Angular directive. * You can define your own directives to attach custom behavior to elements in the DOM. * - * 将类标记为 Angular 指令的装饰器。你可以定义自己的指令,以将自定义行为附加到 DOM 中的元素。 - * * The options provide configuration metadata that determines * how the directive should be processed, instantiated and used at * runtime. * - * 把一个类标记为 Angular 指令。你可以定义自己的指令来为 DOM 中的元素添加自定义行为。 - * 该选项提供配置元数据,用于决定该指令在运行期间要如何处理、实例化和使用。 - * * Directive classes, like component classes, can implement * [life-cycle hooks](guide/lifecycle-hooks) to influence their configuration and behavior. * - * 像组件类一样,指令类也可以实现[生命周期钩子](guide/lifecycle-hooks),以影响它们的配置和行为。 - * * * @usageNotes - * * To define a directive, mark the class with the decorator and provide metadata. * - * 要想定义一个指令,请为该类加上此装饰器,并提供元数据。 - * * ```ts * import {Directive} from '@angular/core'; * @@ -64,22 +51,14 @@ export interface DirectiveDecorator { * * ### Declaring directives * - * ### 声明指令 - * * Directives are [declarables](guide/glossary#declarable). * They must be declared by an NgModule * in order to be usable in an app. * - * 指令是[可声明对象](guide/glossary#declarable)。 - * 它们必须在 NgModule 中声明之后,才能用在应用中。 - * * A directive must belong to exactly one NgModule. Do not re-declare * a directive imported from another module. * List the directive class in the `declarations` field of an NgModule. * - * 指令应当且只能属于一个 NgModule。不要重新声明那些从其它模块中导入的指令。 - * 请把该指令类列在 NgModule 的 `declarations` 字段中。 - * * ```ts * declarations: [ * AppComponent, @@ -93,8 +72,6 @@ export interface DirectiveDecorator { /** * See the `Directive` decorator. - * - * 参见 `Directive` 装饰器中。 */ new(obj?: Directive): Directive; } @@ -102,8 +79,6 @@ export interface DirectiveDecorator { /** * Directive decorator and metadata. * - * 指令装饰器和元数据。 - * * @Annotation * @publicApi */ @@ -112,46 +87,21 @@ export interface Directive { * The CSS selector that identifies this directive in a template * and triggers instantiation of the directive. * - * 这个 CSS 选择器用于在模板中标记出该指令,并触发该指令的实例化。 - * * Declare as one of the following: * - * 可使用下列形式之一: - * * - `element-name`: Select by element name. - * - * `element-name`:根据元素名选取。 - * * - `.class`: Select by class name. - * - * `.class`:根据类名选取。 - * * - `[attribute]`: Select by attribute name. - * - * `[attribute]`:根据属性名选取。 - * * - `[attribute=value]`: Select by attribute name and value. - * - * `[attribute=value]`:根据属性名和属性值选取。 - * * - `:not(sub_selector)`: Select only if the element does not match the `sub_selector`. - * - * `:not(sub_selector)`:只有当元素不匹配子选择器 `sub_selector` 的时候才选取。 - * * - `selector1, selector2`: Select if either `selector1` or `selector2` matches. * - * `selector1, selector2`:无论 `selector1` 还是 `selector2` 匹配时都选取。 - * * Angular only allows directives to apply on CSS selectors that do not cross * element boundaries. * - * Angular 的指令只允许那些不跨元素边界的 CSS 选择器。 - * * For the following template HTML, a directive with an `input[type=text]` selector, * would be instantiated only on the `<input type="text">` element. * - * 对于下列模板 HTML,带有 `input[type=text]` 选择器的指令只会在 `<input type="text">` 元素上实例化。 - * * ```html * <form> * <input type="text"> @@ -165,33 +115,19 @@ export interface Directive { /** * Enumerates the set of data-bound input properties for a directive * - * 列举某个指令的一组可供数据绑定的输入属性 - * * Angular automatically updates input properties during change detection. * The `inputs` property defines a set of `directiveProperty` to `bindingProperty` * configuration: * - * Angular 会在变更检测期间自动更新输入属性。 - * `inputs` 属性定义了一组从 `directiveProperty` 指向 `bindingProperty` 的配置项: - * * - `directiveProperty` specifies the component property where the value is written. - * - * `directiveProperty` 用于指定要写入值的指令内属性。 - * * - `bindingProperty` specifies the DOM property where the value is read from. * - * `bindingProperty` 用于指定要从中读取值的 DOM 属性。 - * * When `bindingProperty` is not provided, it is assumed to be equal to `directiveProperty`. * - * 当没有提供 `bindingProperty` 时,就假设它和 `directiveProperty` 一样。 - * * @usageNotes * * The following example creates a component with two data-bound properties. * - * 下面的例子创建了一个带有两个可绑定属性的组件。 - * * ```typescript * @Component({ * selector: 'bank-account', @@ -213,26 +149,15 @@ export interface Directive { /** * Enumerates the set of event-bound output properties. * - * 列举一组可供事件绑定的输出属性。 - * * When an output property emits an event, an event handler attached to that event * in the template is invoked. * - * 当输出属性发出一个事件时,就会调用模板中附加到它的一个事件处理器。 - * * The `outputs` property defines a set of `directiveProperty` to `bindingProperty` * configuration: * - * `outputs` 属性定义了一组从 `directiveProperty` 指向 `bindingProperty` 的配置项: - * * - `directiveProperty` specifies the component property that emits events. - * - * `directiveProperty` 用于指定要发出事件的指令属性。 - * * - `bindingProperty` specifies the DOM property the event handler is attached to. * - * `bindingProperty` 用于指定要附加事件处理器的 DOM 属性。 - * * @usageNotes * * ```typescript @@ -267,16 +192,12 @@ export interface Directive { * Configures the [injector](guide/glossary#injector) of this * directive or component with a [token](guide/glossary#di-token) * that maps to a [provider](guide/glossary#provider) of a dependency. - * - * 一组依赖注入令牌,它允许 DI 系统为这个指令或组件提供依赖。 */ providers?: Provider[]; /** * Defines the name that can be used in the template to assign this directive to a variable. * - * 定义一个名字,用于在模板中把该指令赋值给一个变量。 - * * @usageNotes * * ```ts @@ -301,20 +222,14 @@ export interface Directive { /** * Configures the queries that will be injected into the directive. * - * 配置一些查询,它们将被注入到该指令中。 - * * Content queries are set before the `ngAfterContentInit` callback is called. * View queries are set before the `ngAfterViewInit` callback is called. * - * 内容查询会在调用 `ngAfterContentInit` 回调之前设置好。 - * * @usageNotes * * The following example shows how queries are defined * and when their results are available in lifecycle hooks: * - * 下面的范例展示了如何定义这些查询以及到生命周期钩子中的哪个步骤才会有结果: - * * ```ts * @Component({ * selector: 'someDir', @@ -346,43 +261,23 @@ export interface Directive { * Maps class properties to host element bindings for properties, * attributes, and events, using a set of key-value pairs. * - * 使用一组键-值对,把类的属性映射到宿主元素的绑定(Property、Attribute 和事件)。 - * * Angular automatically checks host property bindings during change detection. * If a binding changes, Angular updates the directive's host element. * - * Angular 在变更检测期间会自动检查宿主 Property 绑定。 - * 如果绑定的值发生了变化,Angular 就会更新该指令的宿主元素。 - * * When the key is a property of the host element, the property value is * the propagated to the specified DOM property. * - * 当 key 是宿主元素的 Property 时,这个 Property 值就会传播到指定的 DOM 属性。 - * * When the key is a static attribute in the DOM, the attribute value * is propagated to the specified property in the host element. * - * 当 key 是 DOM 中的静态 Attribute 时,这个 Attribute 值就会传播到宿主元素上指定的 Property 去。 - * * For event handling: - * - * 对于事件处理: - * * - The key is the DOM event that the directive listens to. * To listen to global events, add the target to the event name. * The target can be `window`, `document` or `body`. - * - * 它的 key 就是该指令想要监听的 DOM 事件。 - * 要想监听全局事件,请把要监听的目标添加到事件名的前面。 - * 这个目标可以是 `window`、`document` 或 `body`。 - * * - The value is the statement to execute when the event occurs. If the * statement evaluates to `false`, then `preventDefault` is applied on the DOM * event. A handler method can refer to the `$event` local variable. * - * 它的 value 就是当该事件发生时要执行的语句。如果该语句返回 `false`,那么就会调用这个 DOM 事件的 `preventDefault` 函数。 - * 这个语句中可以引用局部变量 `$event` 来获取事件数据。 - * */ host?: {[key: string]: string}; @@ -391,9 +286,6 @@ export interface Directive { * It remains in distributed code, and the JIT compiler attempts to compile it * at run time, in the browser. * To ensure the correct behavior, the app must import `@angular/compiler`. - * - * 如果存在,则该指令/组件将被 AOT 编译器忽略。它会保留在发布代码中,并且 JIT 编译器会尝试在运行时在浏览器中对其进行编译。为了确保其行为正确,该应用程序必须导入 `@angular/compiler` 。 - * */ jit?: true; } @@ -401,8 +293,6 @@ export interface Directive { /** * Type of the Directive metadata. * - * 指令元数据的类型。 - * * @publicApi */ export const Directive: DirectiveDecorator = makeDecorator( @@ -411,7 +301,6 @@ export const Directive: DirectiveDecorator = makeDecorator( /** * Component decorator interface - * 组件装饰器的接口 * * @publicApi */ @@ -421,69 +310,44 @@ export interface ComponentDecorator { * metadata that determines how the component should be processed, * instantiated, and used at runtime. * - * 一个装饰器,用于把某个类标记为 Angular 组件,并为它配置一些元数据,以决定该组件在运行期间该如何处理、实例化和使用。 - * * Components are the most basic UI building block of an Angular app. * An Angular app contains a tree of Angular components. * - * 组件是 Angular 应用中最基本的 UI 构造块。Angular 应用中包含一棵组件树。 - * * Angular components are a subset of directives, always associated with a template. * Unlike other directives, only one component can be instantiated for a given element in a * template. * - * Angular 的组件是指令的一个子集,它总是有一个与之关联的模板。 - * 和其它指令不同,模板中的每个元素只能具有一个组件实例。 - * * A component must belong to an NgModule in order for it to be available * to another component or application. To make it a member of an NgModule, * list it in the `declarations` field of the `NgModule` metadata. * - * 组件必须从属于某个 NgModule 才能被其它组件或应用使用。 - * 要想让它成为某个 NgModule 中的一员,请把它列在 `@NgModule` 元数据的 `declarations` 字段中。 - * * Note that, in addition to these options for configuring a directive, * you can control a component's runtime behavior by implementing * life-cycle hooks. For more information, see the * [Lifecycle Hooks](guide/lifecycle-hooks) guide. * - * 注意,除了这些用来对指令进行配置的选项之外,你还可以通过实现生命周期钩子来控制组件的运行期行为。 - * 要了解更多,参见 [生命周期钩子](guide/lifecycle-hooks) 章。 - * * @usageNotes * * ### Setting component inputs * - * ### 设置组件的输入属性 - * * The following example creates a component with two data-bound properties, * specified by the `inputs` value. * - * 下免得例子创建了一个带有两个数据绑定属性的组件,它是通过 `inputs` 值来指定的。 - * * <code-example path="core/ts/metadata/directives.ts" region="component-input"></code-example> * * * ### Setting component outputs * - * ### 设置组件的输出属性 - * * The following example shows two event emitters that emit on an interval. One * emits an output every second, while the other emits every five seconds. * - * 下面的例子展示了两个事件发生器,它们定时发出事件。一个每隔一秒发出一个输出事件,另一个则隔五秒。 - * * {@example core/ts/metadata/directives.ts region='component-output-interval'} * * ### Injecting a class with a view provider * - * ### 使用视图提供者注入一个类 - * * The following simple example injects a class into a component * using the view provider specified in component metadata: * - * 下面的例子示范了如何在组件元数据中使用视图提供者来把一个类注入到组件中: - * * ```ts * class Greeter { * greet(name:string) { @@ -581,8 +445,6 @@ export interface ComponentDecorator { (obj: Component): TypeDecorator; /** * See the `Component` decorator. - * - * 参见 `@Component` 装饰器。 */ new(obj: Component): Component; } @@ -590,30 +452,17 @@ export interface ComponentDecorator { /** * Supplies configuration metadata for an Angular component. * - * 为 Angular 组件提供配置元数据。 - * * @publicApi */ export interface Component extends Directive { /** * The change-detection strategy to use for this component. * - * 用于当前组件的变更检测策略。 - * * When a component is instantiated, Angular creates a change detector, * which is responsible for propagating the component's bindings. * The strategy is one of: - * - * 当组件实例化之后,Angular 就会创建一个变更检测器,它负责传播组件各个绑定值的变化。 - * 该策略是下列值之一: - * * - `ChangeDetectionStrategy#OnPush` sets the strategy to `CheckOnce` (on demand). - * - * `ChangeDetectionStrategy#OnPush` 把策略设置为 `CheckOnce`(按需)。 - * * - `ChangeDetectionStrategy#Default` sets the strategy to `CheckAlways`. - * - * `ChangeDetectionStrategy#Default` 把策略设置为 `CheckAlways`。 */ changeDetection?: ChangeDetectionStrategy; @@ -621,8 +470,6 @@ export interface Component extends Directive { * Defines the set of injectable objects that are visible to its view DOM children. * See [example](#injecting-a-class-with-a-view-provider). * - * 定义一组可注入对象,它们在视图的各个子节点中可用。参见[例子](#injecting-a-class-with-a-view-provider)。 - * */ viewProviders?: Provider[]; @@ -632,8 +479,6 @@ export interface Component extends Directive { * SystemJS exposes the `__moduleName` variable within each module. * In CommonJS, this can be set to `module.id`. * - * 包含该组件的那个模块的 ID。该组件必须能解析模板和样式表中使用的相对 URL。 - * SystemJS 在每个模块中都导出了 `__moduleName` 变量。在 CommonJS 中,它可以设置为 `module.id`。 */ moduleId?: string; @@ -641,7 +486,6 @@ export interface Component extends Directive { * The relative path or absolute URL of a template file for an Angular component. * If provided, do not supply an inline template using `template`. * - * Angular 组件模板文件的 URL。如果提供了它,就不要再用 `template` 来提供内联模板了。 */ templateUrl?: string; @@ -649,23 +493,18 @@ export interface Component extends Directive { * An inline template for an Angular component. If provided, * do not supply a template file using `templateUrl`. * - * Angular 组件的内联模板。如果提供了它,就不要再用 `templateUrl` 提供模板了。 */ template?: string; /** * One or more relative paths or absolute URLs for files containing CSS stylesheets to use * in this component. - * - * 一个或多个 URL,指向包含本组件 CSS 样式表的文件。 */ styleUrls?: string[]; /** * One or more inline CSS stylesheets to use * in this component. - * - * 本组件用到的一个或多个内联 CSS 样式。 */ styles?: string[]; @@ -674,46 +513,27 @@ export interface Component extends Directive { * `state()` and `transition()` definitions. * See the [Animations guide](/guide/animations) and animations API documentation. * - * 一个或多个动画 `trigger()` 调用,包含一些 `state()` 和 `transition()` 定义。 - * 参见[动画](/guide/animations)和相关的 API 文档。 */ animations?: any[]; /** * An encapsulation policy for the template and CSS styles. One of: - * - * 供模板和 CSS 样式使用的样式封装策略。取值为: - * * - `ViewEncapsulation.Emulated`: Use shimmed CSS that * emulates the native behavior. - * - * `ViewEncapsulation.Emulated`:使用垫片(shimmed) CSS 来模拟原生行为。 - * * - `ViewEncapsulation.None`: Use global CSS without any * encapsulation. - * - * `ViewEncapsulation.None` :使用不带任何封装的全局 CSS。 - * * - `ViewEncapsulation.ShadowDom`: Use Shadow DOM v1 to encapsulate styles. * - * `ViewEncapsulation.ShadowDom`:使用Shadow DOM v1,封装样式。 - * * If not supplied, the value is taken from `CompilerOptions`. The default compiler option is * `ViewEncapsulation.Emulated`. * - * 如果没有提供,该值就会从 `CompilerOptions` 中获取它。默认的编译器选项是 `ViewEncapsulation.Emulated`。 - * * If the policy is set to `ViewEncapsulation.Emulated` and the component has no `styles` * or `styleUrls` specified, the policy is automatically switched to `ViewEncapsulation.None`. - * - * 如果该策略设置为 `ViewEncapsulation.Emulated`,并且该组件没有指定 `styles` 或 `styleUrls`,就会自动切换到 `ViewEncapsulation.None`。 */ encapsulation?: ViewEncapsulation; /** - * Overrides the default encapsulation start and end delimiters (`{{` and `}}`) - * - * 改写默认的插值表达式起止分界符(`{{` 和 `}}`) + * Overrides the default interpolation start and end delimiters (`{{` and `}}`). */ interpolation?: [string, string]; @@ -722,13 +542,7 @@ export interface Component extends Directive { * this component. For each component listed here, * Angular creates a {@link ComponentFactory} and stores it in the * {@link ComponentFactoryResolver}. - * - * 一个组件的集合,它应该和当前组件一起编译。对于这里列出的每个组件,Angular 都会创建一个 {@link ComponentFactory} 并保存进 {@link ComponentFactoryResolver} 中。 - * * @deprecated Since 9.0.0. With Ivy, this property is no longer necessary. - * - * 从 9.0.0 开始。使用 Ivy,不再需要此属性。 - * */ entryComponents?: Array<Type<any>|any[]>; @@ -737,9 +551,6 @@ export interface Component extends Directive { * from the compiled template. Whitespace characters are those matching the `\s` * character class in JavaScript regular expressions. Default is false, unless * overridden in compiler options. - * - * 为 `true` 则保留,为 `false` 则从编译后的模板中移除可能多余的空白字符。 - * 空白字符就是指那些能在 JavaScript 正则表达式中匹配 `\s` 的字符。默认为 `false`,除非通过编译器选项改写了它。 */ preserveWhitespaces?: boolean; } @@ -747,8 +558,6 @@ export interface Component extends Directive { /** * Component decorator and metadata. * - * 组件装饰器与元数据 - * * @Annotation * @publicApi */ @@ -760,8 +569,6 @@ export const Component: ComponentDecorator = makeDecorator( /** * Type of the Pipe decorator / constructor function. * - * Pipe 装饰器的类型和构造函数。 - * * @publicApi */ export interface PipeDecorator { @@ -769,39 +576,27 @@ export interface PipeDecorator { * * Decorator that marks a class as pipe and supplies configuration metadata. * - * 本装饰器用于将类标记为管道并提供配置元数据。 - * * A pipe class must implement the `PipeTransform` interface. * For example, if the name is "myPipe", use a template binding expression * such as the following: * - * 管道类必须实现 `PipeTransform` 接口。例如,如果其名称为 “myPipe”,则使用模板绑定表达式,例如: - * * ``` * {{ exp | myPipe }} * ``` * * The result of the expression is passed to the pipe's `transform()` method. * - * 此表达式的结果会传给管道的 `transform()` 方法。 - * * A pipe must belong to an NgModule in order for it to be available * to a template. To make it a member of an NgModule, * list it in the `declarations` field of the `NgModule` metadata. * - * 管道必须属于某个 NgModule,才能用于模板。要使其成为 NgModule 的成员,请把它加入 `NgModule` 元数据的 `declarations` 中。 - * * @see [Style Guide: Pipe Names](guide/styleguide#02-09) * - * [样式指南:管道名称](guide/styleguide#02-09) - * */ (obj: Pipe): TypeDecorator; /** * See the `Pipe` decorator. - * - * 参见 `Pipe` 装饰器。 */ new(obj: Pipe): Pipe; } @@ -809,8 +604,6 @@ export interface PipeDecorator { /** * Type of the Pipe metadata. * - * Pipe 元数据的类型。 - * * @publicApi */ export interface Pipe { @@ -818,9 +611,6 @@ export interface Pipe { * The pipe name to use in template bindings. * Typically uses [lowerCamelCase](guide/glossary#case-types) * because the name cannot contain hyphens. - * - * 在模板中绑定时使用的管道名。 - * 通常使用 [lowerCamelCase](guide/glossary#case-types) 拼写方式,因为名字中不允许包含减号(-)。 */ name: string; @@ -829,15 +619,10 @@ export interface Pipe { * `transform()` method is invoked only when its input arguments * change. Pipes are pure by default. * - * 为 `true` 时,该管道是纯管道,也就是说 `transform()` 方法只有在其输入参数变化时才会被调用。管道默认都是纯管道。 - * * If the pipe has internal state (that is, the result * depends on state other than its arguments), set `pure` to false. * In this case, the pipe is invoked on each change-detection cycle, * even if the arguments have not changed. - * - * 如果该管道具有内部状态(也就是说,其结果会依赖内部状态,而不仅仅依赖参数),就要把 `pure` 设置为 `false`。 - * 这种情况下,该管道会在每个变更检测周期中都被调用一次 —— 即使其参数没有发生任何变化。 */ pure?: boolean; } @@ -860,23 +645,15 @@ export interface InputDecorator { * 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. * - * 一个装饰器,用来把某个类字段标记为输入属性,并提供配置元数据。 - * 该输入属性会绑定到模板中的某个 DOM 属性。当变更检测时,Angular 会自动使用这个 DOM 属性的值来更新此数据属性。 - * - * @usageNotes + * @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({ @@ -907,8 +684,6 @@ export interface InputDecorator { * ``` * * @see [Input and Output properties](guide/inputs-outputs) - * - * [输入和输出属性](guide/inputs-outputs) */ (bindingPropertyName?: string): any; new(bindingPropertyName?: string): any; @@ -917,16 +692,11 @@ export interface InputDecorator { /** * Type of metadata for an `Input` property. * - * `Input` 属性的元数据类型。 - * * @publicApi */ export interface Input { /** * The name of the DOM property to which the input property is bound. - * - * 输入属性绑定到的 DOM 属性的名字, - * */ bindingPropertyName?: string; } @@ -941,8 +711,6 @@ export const Input: InputDecorator = /** * Type of the Output decorator / constructor function. * - * `Output` 装饰器的类型和构造函数。 - * * @publicApi */ export interface OutputDecorator { @@ -950,27 +718,17 @@ 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. * - * 一个装饰器,用于把一个类字段标记为输出属性,并提供配置元数据。 - * 凡是绑定到输出属性上的 DOM 属性,Angular 在变更检测期间都会自动进行更新。 - * - * @usageNotes + * @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. - * - * 参见 `@Input` 的例子了解如何指定一个绑定名。 + * See `Input` decorator for an example of providing a binding name. * * @see [Input and Output properties](guide/inputs-outputs) * - * [输入和输出属性](guide/inputs-outputs) - * */ (bindingPropertyName?: string): any; new(bindingPropertyName?: string): any; @@ -979,16 +737,11 @@ export interface OutputDecorator { /** * Type of the Output metadata. * - * `Output` 元数据的类型。 - * * @publicApi */ export interface Output { /** * The name of the DOM property to which the output property is bound. - * - * 输出属性绑定到的 DOM 属性的名称。 - * */ bindingPropertyName?: string; } @@ -1005,9 +758,6 @@ export const Output: OutputDecorator = /** * Type of the HostBinding decorator / constructor function. * - * HostBinding 装饰器的类型和构造函数。 - * - * * @publicApi */ export interface HostBindingDecorator { @@ -1017,16 +767,11 @@ export interface HostBindingDecorator { * Angular automatically checks host property bindings during change detection, and * if a binding changes it updates the host element of the directive. * - * 一个装饰器,用于把一个 DOM 属性标记为绑定到宿主的属性,并提供配置元数据。 - * Angular 在变更检测期间会自动检查宿主属性绑定,如果这个绑定变化了,它就会更新该指令所在的宿主元素。 - * * @usageNotes * * The following example creates a directive that sets the `valid` and `invalid` * properties on the DOM element that has an `ngModel` directive on it. * - * 下面的例子创建了一个指令,它会对具有 `ngModel` 指令的 DOM 元素设置 `valid` 和 `invalid` 属性。 - * * ```typescript * @Directive({selector: '[ngModel]'}) * class NgModelStatus { @@ -1052,16 +797,11 @@ export interface HostBindingDecorator { /** * Type of the HostBinding metadata. * - * HostBinding 元数据的类型。 - * * @publicApi */ export interface HostBinding { /** * The DOM property that is bound to a data property. - * - * 要绑定到数据属性的 DOM 属性。 - * */ hostPropertyName?: string; } @@ -1077,8 +817,6 @@ export const HostBinding: HostBindingDecorator = /** * Type of the HostListener decorator / constructor function. * - * HostListener 装饰器的类型和构造函数。 - * * @publicApi */ export interface HostListenerDecorator { @@ -1086,8 +824,10 @@ export interface HostListenerDecorator { * Decorator that declares a DOM event to listen for, * and provides a handler method to run when that event occurs. * - * 一个装饰器,用于声明要监听的 DOM 事件,并提供在该事件发生时要运行的处理器方法。 + * Angular invokes the supplied handler method when the host element emits the specified event, + * and updates the bound element with the result. * + * If the handler method returns false, applies `preventDefault` on the bound element. */ (eventName: string, args?: string[]): any; new(eventName: string, args?: string[]): any; @@ -1096,21 +836,15 @@ export interface HostListenerDecorator { /** * Type of the HostListener metadata. * - * HostListener 元数据的类型。 - * * @publicApi */ export interface HostListener { /** * The DOM event to listen for. - * - * 要监听的事件。 */ eventName?: string; /** * A set of arguments to pass to the handler method when the event occurs. - * - * 当该事件发生时传给处理器方法的一组参数。 */ args?: string[]; } @@ -1120,21 +854,13 @@ export interface HostListener { * Angular invokes the supplied handler method when the host element emits the specified event, * and updates the bound element with the result. * - * 将 DOM 事件绑定到宿主监听器并提供配置元数据的装饰器。当宿主元素发出指定事件时,Angular 就会调用所提供的处理器方法,并使用其结果更新绑定的元素。 - * * If the handler method returns false, applies `preventDefault` on the bound element. * - * 把一个事件绑定到一个宿主监听器,并提供配置元数据。 - * 当宿主元素发出特定的事件时,Angular 就会执行所提供的处理器方法,并使用其结果更新所绑定到的元素。 - * 如果该事件处理器返回 `false`,则在所绑定的元素上执行 `preventDefault`。 - * * @usageNotes * * The following example declares a directive * that attaches a click listener to a button and counts clicks. * - * 下面的例子声明了一个指令,它会为按钮附加一个 `click` 监听器,并统计点击次数。 - * * ```ts * @Directive({selector: 'button[counting]'}) * class CountClicks { diff --git a/packages/core/src/metadata/do_boostrap.ts b/packages/core/src/metadata/do_boostrap.ts index c7715a31c6..17190fceb2 100644 --- a/packages/core/src/metadata/do_boostrap.ts +++ b/packages/core/src/metadata/do_boostrap.ts @@ -14,14 +14,11 @@ import {ApplicationRef} from '../application_ref'; * Hook for manual bootstrapping of the application instead of using bootstrap array in @NgModule * annotation. * - * 挂钩以手动引导应用程序,而不是在 @NgModule 标记中的 bootstrap 数组。 - * * Reference to the current application is provided as a parameter. * * See ["Bootstrapping"](guide/bootstrapping) and ["Entry components"](guide/entry-components). * * @usageNotes - * * ```typescript * class AppModule implements DoBootstrap { * ngDoBootstrap(appRef: ApplicationRef) { diff --git a/packages/core/src/metadata/ng_module.ts b/packages/core/src/metadata/ng_module.ts index 1ab17ab2bc..f23f0dc29c 100644 --- a/packages/core/src/metadata/ng_module.ts +++ b/packages/core/src/metadata/ng_module.ts @@ -13,26 +13,14 @@ import {Type} from '../interface/type'; import {SchemaMetadata} from '../metadata/schema'; import {compileNgModule as render3CompileNgModule} from '../render3/jit/module'; import {makeDecorator, TypeDecorator} from '../util/decorators'; -import {NgModuleDef} from './ng_module_def'; - - -/** - * @publicApi - */ -export type ɵɵNgModuleDefWithMeta<T, Declarations, Imports, Exports> = NgModuleDef<T>; /** * A wrapper around an NgModule that associates it with [providers](guide/glossary#provider * "Definition"). Usage without a generic type is deprecated. * - * 对 NgModule 及其相关 [providers](guide/glossary#provider - * "Definition") 的包装。不带泛型的用法已弃用。 - * * @see [Deprecations](guide/deprecations#modulewithproviders-type-without-a-generic) * - * [弃用](guide/deprecations#modulewithproviders-type-without-a-generic) - * * @publicApi */ export interface ModuleWithProviders<T> { @@ -44,16 +32,11 @@ export interface ModuleWithProviders<T> { /** * Type of the NgModule decorator / constructor function. * - * - * NgModule 装饰器和构造函数的类型。 - * * @publicApi */ export interface NgModuleDecorator { /** * Decorator that marks a class as an NgModule and supplies configuration metadata. - * - * 把一个类标记为 NgModule,并提供配置元数据。 */ (obj?: NgModule): TypeDecorator; new(obj?: NgModule): NgModule; @@ -62,8 +45,6 @@ export interface NgModuleDecorator { /** * Type of the NgModule metadata. * - * NgModule 元数据的类型。 - * * @publicApi */ export interface NgModule { @@ -71,16 +52,9 @@ export interface NgModule { * The set of injectable objects that are available in the injector * of this module. * - * 在当前模块的注入器中可用的一组可注入对象。 - * * @see [Dependency Injection guide](guide/dependency-injection) - * - * [依赖注入指南](guide/dependency-injection) - * * @see [NgModule guide](guide/providers) * - * [NgModule 指南](guide/providers) - * * @usageNotes * * Dependencies whose providers are listed here become available for injection @@ -88,9 +62,6 @@ export interface NgModule { * The NgModule used for bootstrapping uses the root injector, and can provide dependencies * to any part of the app. * - * 在这里列出了提供者的依赖项可用于注入到任何组件、指令、管道或该注入器下的服务。 - * 引导用的 NgModule 使用的是根注入器,可以为应用中的任何部件提供依赖。 - * * A lazy-loaded module has its own injector, typically a child of the app root injector. * Lazy-loaded services are scoped to the lazy-loaded module's injector. * If a lazy-loaded module also provides the `UserService`, any component created @@ -98,21 +69,11 @@ export interface NgModule { * of the service, not the instance in the root injector. * Components in external modules continue to receive the instance provided by their injectors. * - * 惰性加载的模块有自己的注入器,通常它是根注入器的一个子注入器。 - * 惰性加载的服务,其作用范围局限于这个惰性加载模块的注入器。 - * 如果惰性加载模块也提供了 `UserService`,则任何在该模块的上下文中创建的组件(比如通过路由导航)都会获得该服务的一个局部实例, - * 而不是根注入器中的全局实例。 - * 而外部模块中的组件仍然会使用由它们的注入器提供的实例。 - * * ### Example * - * ### 范例 - * * The following example defines a class that is injected in * the HelloWorld NgModule: * - * 下面的例子定义了一个类,它被注册在 HelloWorld 这个 NgModule 的注入器下: - * * ``` * class Greeter { * greet(name:string) { @@ -140,32 +101,20 @@ export interface NgModule { * The set of components, directives, and pipes ([declarables](guide/glossary#declarable)) * that belong to this module. * - * 属于该模块的一组组件、指令和管道(统称[可声明对象](guide/glossary#declarable))。 - * * @usageNotes * * The set of selectors that are available to a template include those declared here, and * those that are exported from imported NgModules. * - * 在模板中可用的选择器(selector)包括那些直接声明在这里的可声明对象和导入的那些 NgModule 中所导出的可声明对象。 - * * Declarables must belong to exactly one module. * The compiler emits an error if you try to declare the same class in more than one module. * Be careful not to declare a class that is imported from another module. * - * 可声明对象必须属于也只能属于一个模块。 - * 如果你尝试把同一个类声明在多个模块中,那么编译器就会报错。 - * 要注意不能声明那些从其它模块中导入的类。 - * * ### Example * - * ### 范例 - * * The following example allows the CommonModule to use the `NgFor` * directive. * - * 下面的例子允许 CommonModule 使用 `NgFor` 指令。 - * * ```javascript * @NgModule({ * declarations: [NgFor] @@ -180,8 +129,6 @@ export interface NgModule { * The set of NgModules whose exported [declarables](guide/glossary#declarable) * are available to templates in this module. * - * 这里列出的 NgModule 所导出的[可声明对象](guide/glossary#declarable)可用在当前模块内的模板中。 - * * @usageNotes * * A template can use exported declarables from any @@ -191,18 +138,11 @@ export interface NgModule { * it, which makes the declarables from `ModuleB` available * wherever `ModuleA` is imported. * - * 模板可以使用来自任何导入模块中所导出的可声明对象,包括它们从别的模块导入后重新导出的。 - * 例如,`ModuleA` 导入了 `ModuleB` 并导出了它,就会让 `ModuleB` 中的可声明对象也同样在那些导入了 `ModuleA` 的模块中可用。 - * * ### Example * - * ### 范例 - * * The following example allows MainModule to use anything exported by * `CommonModule`: * - * 下列例子允许 `MainModule` 使用 `CommonModule` 中导入的任意可声明对象: - * * ```javascript * @NgModule({ * imports: [CommonModule] @@ -219,38 +159,23 @@ export interface NgModule { * NgModule that can be used in the template of any component that is part of an * NgModule that imports this NgModule. Exported declarations are the module's public API. * - * 此 NgModule 中声明的一组组件、指令和管道可以在导入了本模块的模块下任何组件的模板中使用。 - * 导出的这些可声明对象就是该模块的公共 API。 - * * A declarable belongs to one and only one NgModule. * A module can list another module among its exports, in which case all of that module's * public declaration are exported. * - * 可声明对象应该且只能属于一个 NgModule。 - * 一个模块可以列出在它的 `exports` 中列出一些其它模块,这些模块中所有公开的可声明对象也同样会导出。 - * * @usageNotes * * Declarations are private by default. * If this ModuleA does not export UserComponent, then only the components within this * ModuleA can use UserComponent. * - * 默认情况下,可声明对象是私有的。 - * 如果 ModuleA 不导出 UserComponent,那么只有这个 ModuleA 中的组件才能使用 UserComponent。 - * * ModuleA can import ModuleB and also export it, making exports from ModuleB * available to an NgModule that imports ModuleA. * - * 导出具有传递性。ModuleA 可以导入 ModuleB 并将其导出,这会让所有 ModuleB 中的导出同样可用在导入了 ModuleA 的那些模块中。 - * * ### Example * - * ### 范例 - * * The following example exports the `NgFor` directive from CommonModule. * - * 下面的例子导出了来自 `CommonModule` 的 `NgFor` 指令。 - * * ```javascript * @NgModule({ * exports: [NgFor] @@ -265,29 +190,16 @@ export interface NgModule { * The set of components to compile when this NgModule is defined, * so that they can be dynamically loaded into the view. * - * 定义此 NgModule 中要编译的组件集,这样它们才可以动态加载到视图中。 - * * For each component listed here, Angular creates a `ComponentFactory` * and stores it in the `ComponentFactoryResolver`. * - * 对于在这里列出的每个组件,Angular 都会为其创建一个 `ComponentFactory`,并将其保存到 `ComponentFactoryResolver` 中。 - * * Angular automatically adds components in the module's bootstrap * and route definitions into the `entryComponents` list. Use this * option to add components that are bootstrapped * using one of the imperative techniques, such as `ViewContainerRef.createComponent()`. * - * Angular 会自动把模块的 `bootstrap`(引导模块)和路由定义中引用的组件添加到 `entryComponents` 列表中。 - * 该选项用于添加那些需要写代码来创建的组件,比如 `ViewContainerRef.createComponent()`。 - * * @see [Entry Components](guide/entry-components) - * - * [入口组件](guide/entry-components) - * * @deprecated Since 9.0.0. With Ivy, this property is no longer necessary. - * - * 从 9.0.0 开始。使用 Ivy,不再需要此属性。 - * */ entryComponents?: Array<Type<any>|any[]>; @@ -295,8 +207,6 @@ export interface NgModule { * The set of components that are bootstrapped when * this module is bootstrapped. The components listed here * are automatically added to `entryComponents`. - * - * 当该模块引导时需要进行引导的组件。列在这里的所有组件都会自动添加到 `entryComponents` 中。 */ bootstrap?: Array<Type<any>|any[]>; @@ -305,17 +215,10 @@ export interface NgModule { * Elements and properties that are neither Angular components nor directives * must be declared in a schema. * - * 该 NgModule 中允许使用的声明元素的 schema(HTML 架构)。 - * 元素和属性(无论是 Angular 组件还是指令)都必须声明在 schema 中。 - * * Allowed value are `NO_ERRORS_SCHEMA` and `CUSTOM_ELEMENTS_SCHEMA`. * - * 允许的取值包括 `NO_ERRORS_SCHEMA` 和 `CUSTOM_ELEMENTS_SCHEMA`。 - * * @security When using one of `NO_ERRORS_SCHEMA` or `CUSTOM_ELEMENTS_SCHEMA` * you must ensure that allowed elements and properties securely escape inputs. - * - * 当使用 `NO_ERRORS_SCHEMA` 或 `CUSTOM_ELEMENTS_SCHEMA` 之一时,你必须确保所允许的元素和属性的所有输入都经过了安全转义。 */ schemas?: Array<SchemaMetadata|any[]>; @@ -323,9 +226,6 @@ export interface NgModule { * A name or path that uniquely identifies this NgModule in `getModuleFactory`. * If left `undefined`, the NgModule is not registered with * `getModuleFactory`. - * - * 当前 NgModule 在 `getModuleFactory` 中的名字或唯一标识符。 - * 如果为 `undefined`,则该模块不会被注册进 `getModuleFactory` 中。 */ id?: string; @@ -334,9 +234,6 @@ export interface NgModule { * It remains in distributed code, and the JIT compiler attempts to compile it * at run time, in the browser. * To ensure the correct behavior, the app must import `@angular/compiler`. - * - * 如果存在,则该指令/组件将被 AOT 编译器忽略。它会保留在发布代码中,并且 JIT 编译器会尝试在运行时在浏览器中对其进行编译。为了确保其行为正确,该应用程序必须导入 `@angular/compiler` 。 - * */ jit?: true; } @@ -346,30 +243,19 @@ export interface NgModule { * @publicApi */ export const NgModule: NgModuleDecorator = makeDecorator( - 'NgModule', (ngModule: NgModule) => ngModule, undefined, undefined, - /** - * Decorator that marks the following class as an NgModule, and supplies - * configuration metadata for it. - * - * 一个装饰器,用于把当前类标记为一个 NgModule,并为之提供配置元数据。 - * - * * The `declarations` and `entryComponents` options configure the compiler - * with information about what belongs to the NgModule. - * - * `declarations` 和 `entryComponents` 选项告诉编译器,哪些东西是属于本 NgModule 的。 - * - * * The `providers` options configures the NgModule's injector to provide - * dependencies the NgModule members. - * - * `providers` 选项会配置该 NgModule 的注入器,以便为该 NgModule 的所有成员提供依赖项。 - * - * * The `imports` and `exports` options bring in members from other modules, and make - * this module's members available to others. - * - * `imports` 选项用于从其它模块中带入成员,`exports` 选项用于把本模块的成员带给其它模块。 - * - */ - (type: Type<any>, meta: NgModule) => SWITCH_COMPILE_NGMODULE(type, meta)); + 'NgModule', (ngModule: NgModule) => ngModule, undefined, undefined, + /** + * Decorator that marks the following class as an NgModule, and supplies + * configuration metadata for it. + * + * * The `declarations` and `entryComponents` options configure the compiler + * with information about what belongs to the NgModule. + * * The `providers` options configures the NgModule's injector to provide + * dependencies the NgModule members. + * * The `imports` and `exports` options bring in members from other modules, and make + * this module's members available to others. + */ + (type: Type<any>, meta: NgModule) => SWITCH_COMPILE_NGMODULE(type, meta)); function preR3NgModuleCompile(moduleType: Type<any>, metadata?: NgModule): void { @@ -378,11 +264,10 @@ function preR3NgModuleCompile(moduleType: Type<any>, metadata?: NgModule): void imports = [...imports, metadata.exports]; } - (moduleType as InjectorType<any>).ɵinj = ɵɵdefineInjector({ - factory: convertInjectableProviderToFactory(moduleType, {useClass: moduleType}), - providers: metadata && metadata.providers, - imports: imports, - }); + const moduleInjectorType = moduleType as InjectorType<any>; + moduleInjectorType.ɵfac = convertInjectableProviderToFactory(moduleType, {useClass: moduleType}); + moduleInjectorType.ɵinj = + ɵɵdefineInjector({providers: metadata && metadata.providers, imports: imports}); } diff --git a/packages/core/src/metadata/schema.ts b/packages/core/src/metadata/schema.ts index 41d9c99ac2..9850f69ac0 100644 --- a/packages/core/src/metadata/schema.ts +++ b/packages/core/src/metadata/schema.ts @@ -10,14 +10,10 @@ /** * A schema definition associated with an NgModule. * - * 与 NgModule 关联的架构定义。 - * * @see `@NgModule`, `CUSTOM_ELEMENTS_SCHEMA`, `NO_ERRORS_SCHEMA` * * @param name The name of a defined schema. * - * 定义的架构的名称。 - * * @publicApi */ export interface SchemaMetadata { @@ -26,18 +22,10 @@ export interface SchemaMetadata { /** * Defines a schema that allows an NgModule to contain the following: - * - * 定义一个架构,该架构允许 NgModule 包含以下内容: - * * - Non-Angular elements named with dash case (`-`). - * - * 使用(`-`)命名法的非 Angular 元素。 - * * - Element properties named with dash case (`-`). * Dash case is the naming convention for custom elements. * - * 使用(`-`)命名的元素属性。中线命名法是自定义元素的命名约定。 - * * @publicApi */ export const CUSTOM_ELEMENTS_SCHEMA: SchemaMetadata = { @@ -47,8 +35,6 @@ export const CUSTOM_ELEMENTS_SCHEMA: SchemaMetadata = { /** * Defines a schema that allows any property on any element. * - * 定义一个架构,该架构允许任何元素上的任何属性。 - * * @publicApi */ export const NO_ERRORS_SCHEMA: SchemaMetadata = { diff --git a/packages/core/src/metadata/view.ts b/packages/core/src/metadata/view.ts index 4010d32b88..cdf97234fe 100644 --- a/packages/core/src/metadata/view.ts +++ b/packages/core/src/metadata/view.ts @@ -9,18 +9,11 @@ /** * Defines template and style encapsulation options available for Component's {@link Component}. * - * 定义可用于 Component 的 {@link Component} 的模板和样式封装选项。 - * * See {@link Component#encapsulation encapsulation}. * - * 请参阅 {@link Component#encapsulation encapsulation}。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * {@example core/ts/metadata/encapsulation.ts region='longform'} * * @publicApi @@ -32,11 +25,7 @@ export enum ViewEncapsulation { * {@link Component#styleUrls styleUrls}, and adding the new Host Element attribute to all * selectors. * - * 通过向宿主元素添加包含替代 ID 的属性并预处理通过 {@link Component#styles styles} 或 {@link Component#styleUrls styleUrls} 提供的样式规则,来模拟 `Native` 所有选择器。 - * * This is the default option. - * - * 这是默认选项。 */ Emulated = 0, @@ -44,23 +33,15 @@ export enum ViewEncapsulation { /** * Don't provide any template or style encapsulation. - * - * 不要提供任何模板或样式封装。 - * */ None = 2, /** * Use Shadow DOM to encapsulate styles. * - * 使用 Shadow DOM 封装样式。 - * * For the DOM this means using modern [Shadow - * DOM](https://w3c.github.io/webcomponents/spec/shadow/) and + * DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM) and * creating a ShadowRoot for Component's Host Element. - * - * 对于 DOM,这意味着使用现代的 [Shadow DOM](https://w3c.github.io/webcomponents/spec/shadow/) 并为组件的 Host 元素创建 ShadowRoot。 - * */ ShadowDom = 3 } diff --git a/packages/core/src/platform_core_providers.ts b/packages/core/src/platform_core_providers.ts index a8947e2ded..1324e4df67 100644 --- a/packages/core/src/platform_core_providers.ts +++ b/packages/core/src/platform_core_providers.ts @@ -23,8 +23,6 @@ const _CORE_PLATFORM_PROVIDERS: StaticProvider[] = [ /** * This platform has to be included in any other platform * - * 任何其他平台都必须包含此平台 - * * @publicApi */ export const platformCore = createPlatformFactory(null, 'core', _CORE_PLATFORM_PROVIDERS); diff --git a/packages/core/src/r3_symbols.ts b/packages/core/src/r3_symbols.ts index 480dd74075..7d2d0e74b9 100644 --- a/packages/core/src/r3_symbols.ts +++ b/packages/core/src/r3_symbols.ts @@ -22,11 +22,10 @@ */ export {ɵɵinject} from './di/injector_compatibility'; -export {ɵɵdefineInjectable, ɵɵdefineInjector, ɵɵInjectableDef, ɵɵInjectorDef} from './di/interface/defs'; -export {ɵɵNgModuleDefWithMeta} from './metadata/ng_module'; +export {ɵɵdefineInjectable, ɵɵdefineInjector, ɵɵInjectableDef} from './di/interface/defs'; export {NgModuleDef} from './metadata/ng_module_def'; export {ɵɵdefineNgModule} from './render3/definition'; -export {ɵɵFactoryDef} from './render3/interfaces/definition'; +export {ɵɵFactoryDeclaration, ɵɵInjectorDeclaration, ɵɵNgModuleDeclaration} from './render3/interfaces/public_definitions'; export {setClassMetadata} from './render3/metadata'; export {NgModuleFactory} from './render3/ng_module_ref'; export {noSideEffects as ɵnoSideEffects} from './util/closure'; diff --git a/packages/core/src/reflection/reflection_capabilities.ts b/packages/core/src/reflection/reflection_capabilities.ts index 75575ebb13..c4ad36405b 100644 --- a/packages/core/src/reflection/reflection_capabilities.ts +++ b/packages/core/src/reflection/reflection_capabilities.ts @@ -34,15 +34,22 @@ import {GetterFn, MethodFn, SetterFn} from './types'; * var _this = _super.apply(this, arguments) || this; * ``` * + * downleveled to ES5 with `downlevelIteration` for TypeScript < 4.2: * ``` * function MyClass() { * var _this = _super.apply(this, __spread(arguments)) || this; * ``` * + * or downleveled to ES5 with `downlevelIteration` for TypeScript >= 4.2: + * ``` + * function MyClass() { + * var _this = _super.apply(this, __spreadArray([], __read(arguments))) || this; + * ``` + * * More details can be found in: https://github.com/angular/angular/issues/38453. */ export const ES5_DELEGATE_CTOR = - /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*(arguments|[^()]+\(arguments\))\)/; + /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*(arguments|(?:[^()]+\(\[\],)?[^()]+\(arguments\))\)/; /** Regular expression that detects ES2015 classes which extend from other classes. */ export const ES2015_INHERITED_CLASS = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{/; /** diff --git a/packages/core/src/render/api.ts b/packages/core/src/render/api.ts index 313ef9200d..f5c42474fb 100644 --- a/packages/core/src/render/api.ts +++ b/packages/core/src/render/api.ts @@ -22,53 +22,27 @@ export const Renderer2Interceptor = new InjectionToken<Renderer2[]>('Renderer2In /** * Creates and initializes a custom renderer that implements the `Renderer2` base class. * - * 创建并初始化实现 `Renderer2` 基类的自定义渲染器。 - * * @publicApi */ export abstract class RendererFactory2 { /** * Creates and initializes a custom renderer for a host DOM element. - * - * 为宿主 DOM 元素创建并初始化自定义渲染器。 - * * @param hostElement The element to render. - * - * 要渲染的元素。 - * * @param type The base class to implement. - * - * 要实现的基类。 - * * @returns The new custom renderer instance. - * - * 新的自定义渲染器实例。 - * */ abstract createRenderer(hostElement: any, type: RendererType2|null): Renderer2; /** * A callback invoked when rendering has begun. - * - * 渲染开始时调用的回调。 - * */ abstract begin?(): void; /** * A callback invoked when rendering has completed. - * - * 渲染完成时调用的回调。 - * */ abstract end?(): void; /** * Use with animations test-only mode. Notifies the test when rendering has completed. - * - * 与动画的“仅测试”模式一起使用。渲染完成后通知该测试。 - * * @returns The asynchronous result of the developer-defined function. - * - * 由开发人员定义函数的异步结果。 - * */ abstract whenRenderingDone?(): Promise<any>; } @@ -79,23 +53,14 @@ export abstract class RendererFactory2 { * renders a template into DOM. You can use custom rendering to intercept * rendering calls, or to render to something other than DOM. * - * 扩展此基类以实现自定义渲染器。默认情况下,Angular 会把模板渲染成 DOM。 - * 你可以使用自定义渲染器来拦截渲染类调用,或用于渲染一些非 DOM 的东西。 - * * Create your custom renderer using `RendererFactory2`. * - * 使用 `RendererFactory2` 创建你的自定义渲染器。 - * * Use a custom renderer to bypass Angular's templating and * make custom UI changes that can't be expressed declaratively. * For example if you need to set a property or an attribute whose name is * not statically known, use the `setProperty()` or * `setAttribute()` method. * - * 使用自定义渲染器可以绕过 Angular 的模板机制,并进行无法以声明式语法表达的自定义 UI 变更。 - * 比如,如果你要设置无法静态得知名称的 Property 或 Attribute,可以使用 `setProperty()` 或 - * `setAttribute()` 方法。 - * * @publicApi */ export abstract class Renderer2 { @@ -103,366 +68,166 @@ export abstract class Renderer2 { * Use to store arbitrary developer-defined data on a renderer instance, * as an object containing key-value pairs. * This is useful for renderers that delegate to other renderers. - * - * 用于在渲染器实例上以 key-value 对象的形式保存任意自定义数据。 - * 这对于要委托给其它渲染器的渲染器很有用。 */ abstract get data(): {[key: string]: any}; /** * Implement this callback to destroy the renderer or the host element. - * - * 实现此回调以便销毁渲染器或其宿主元素。 */ abstract destroy(): void; /** * Implement this callback to create an instance of the host element. - * - * 实现此回调以便创建宿主元素的实例。 - * * @param name An identifying name for the new element, unique within the namespace. - * - * 对新元素的标识名,在指定的命名空间内应该是唯一的。 - * * @param namespace The namespace for the new element. - * - * 新元素的命名空间。 - * * @returns The new element. - * - * 新元素。 */ abstract createElement(name: string, namespace?: string|null): any; /** * Implement this callback to add a comment to the DOM of the host element. - * - * 实现此回调以便向宿主元素的 DOM 中添加一个注释。 - * * @param value The comment text. - * - * 注释文本。 - * * @returns The modified element. - * - * 修改后的元素。 */ abstract createComment(value: string): any; /** * Implement this callback to add text to the DOM of the host element. - * - * 实现此回调以便向宿主元素的 DOM 中添加文本。 - * * @param value The text string. - * - * 文本字符串。 - * * @returns The modified element. - * - * 修改后的元素。 */ abstract createText(value: string): any; /** * If null or undefined, the view engine won't call it. * This is used as a performance optimization for production mode. - * - * 如果为 null 或 undefined,视图引擎就不会调用它。 - * 用于在产品模式下进行优化。 */ // TODO(issue/24571): remove '!'. destroyNode!: ((node: any) => void)|null; /** * Appends a child to a given parent node in the host element DOM. - * - * 把子元素追加到宿主元素 DOM 中的指定父节点下。 - * * @param parent The parent node. - * - * 父节点。 - * * @param newChild The new child node. - * - * 新的子节点。 */ abstract appendChild(parent: any, newChild: any): void; /** * Implement this callback to insert a child node at a given position in a parent node * in the host element DOM. - * - * 实现此回调,以便往宿主元素中父节点的指定位置插入一个子节点。 - * * @param parent The parent node. - * - * 父节点。 - * * @param newChild The new child nodes. - * - * 新的子节点。 - * * @param refChild The existing child node before which `newChild` is inserted. - * - * 将会插入在这个新节点之前的现有节点。 - * * @param isMove Optional argument which signifies if the current `insertBefore` is a result of a * move. Animation uses this information to trigger move animations. In the past the Animation * would always assume that any `insertBefore` is a move. This is not strictly true because * with runtime i18n it is possible to invoke `insertBefore` as a result of i18n and it should * not trigger an animation move. - * - * 可选参数,指示当前的 `insertBefore` 是否是移动的结果。动画使用此信息来触发移动动画。过去,Animation 始终假定任何 `insertBefore` 都是一次移动。但严格来说这是不正确的,因为在支持 i18n 的运行环境中,可以调用 `insertBefore`,而不应触发移动动画。 - * */ abstract insertBefore(parent: any, newChild: any, refChild: any, isMove?: boolean): void; /** * Implement this callback to remove a child node from the host element's DOM. - * - * 实现此回调以便从宿主元素的 DOM 中移除一个子节点。 - * * @param parent The parent node. - * - * 父节点。 - * * @param oldChild The child node to remove. - * - * 要移除的子节点。 - * * @param isHostElement Optionally signal to the renderer whether this element is a host element * or not - * - * 可选值,用于告诉渲染器该元素是否宿主元素 - * */ abstract removeChild(parent: any, oldChild: any, isHostElement?: boolean): void; /** * Implement this callback to prepare an element to be bootstrapped * as a root element, and return the element instance. - * - * 实现此回调以准备将其作为根元素进行引导的元素,返回该元素的实例。 - * * @param selectorOrNode The DOM element. - * - * DOM 元素。 - * * @param preserveContent Whether the contents of the root element * should be preserved, or cleared upon bootstrap (default behavior). * Use with `ViewEncapsulation.ShadowDom` to allow simple native * content projection via `<slot>` elements. - * - * 根元素的内容是应该保留还是在启动期间清除(默认行为)。 - * 和 `ViewEncapsulation.ShadowDom` 联用以支持使用 `<slot>` 元素进行简单的原生内容投影。 - * * @returns The root element. - * - * 根元素。 */ abstract selectRootElement(selectorOrNode: string|any, preserveContent?: boolean): any; /** * Implement this callback to get the parent of a given node * in the host element's DOM. - * - * 实现此回调以获得宿主元素的 DOM 中指定节点的父节点。 - * * @param node The child node to query. - * - * 要查询的子节点。 - * * @returns The parent node, or null if there is no parent. * For WebWorkers, always returns true. * This is because the check is synchronous, * and the caller can't rely on checking for null. - * - * 它的父节点,如果没有父节点则为 null。 - * 对于 WebWorkers,总是返回 `true`。 - * 这是因为该检查是同步的,该调用者不能依赖于检查 null。 */ abstract parentNode(node: any): any; /** * Implement this callback to get the next sibling node of a given node * in the host element's DOM. - * - * 实现此回调,以获得宿主元素的 DOM 中指定节点的下一个兄弟节点。 - * * @returns The sibling node, or null if there is no sibling. * For WebWorkers, always returns a value. * This is because the check is synchronous, * and the caller can't rely on checking for null. - * - * 它的兄弟节点,如果没有兄弟节点则为 null。 - * 对于 WebWorkers,总是返回 `true`。 - * 这是因为该检查是同步的,该调用者不能依赖于检查 null。 */ abstract nextSibling(node: any): any; /** * Implement this callback to set an attribute value for an element in the DOM. - * - * 实现此回调以便在 DOM 中设置指定元素的属性值。 - * * @param el The element. - * - * 目标元素。 - * * @param name The attribute name. - * - * 属性名。 - * * @param value The new value. - * - * 新值。 - * * @param namespace The namespace. - * - * 命名空间。 */ abstract setAttribute(el: any, name: string, value: string, namespace?: string|null): void; /** * Implement this callback to remove an attribute from an element in the DOM. - * - * 实现此回调以便从 DOM 中某个元素上移除一个属性。 - * * @param el The element. - * - * 目标元素。 - * * @param name The attribute name. - * - * 属性名。 - * * @param namespace The namespace. - * - * 命名空间。 */ abstract removeAttribute(el: any, name: string, namespace?: string|null): void; /** * Implement this callback to add a class to an element in the DOM. - * - * 实现此回调,以便为 DOM 中的某个元素添加一个 CSS 类。 - * * @param el The element. - * - * 目标元素。 - * * @param name The class name. - * - * CSS 类名。 */ abstract addClass(el: any, name: string): void; /** * Implement this callback to remove a class from an element in the DOM. - * - * 实现此回调,以便从 DOM 中的某个元素上移除一个 CSS 类。 - * * @param el The element. - * - * 目标元素。 - * * @param name The class name. - * - * CSS 类名。 */ abstract removeClass(el: any, name: string): void; /** * Implement this callback to set a CSS style for an element in the DOM. - * - * 实现此回调函数,以便为 DOM 中的某个元素设置 CSS 样式。 - * * @param el The element. - * - * 目标元素。 - * * @param style The name of the style. - * - * 样式名。 - * * @param value The new value. - * - * 新值。 - * * @param flags Flags for style variations. No flags are set by default. - * - * 样式的修饰符标志。默认没有任何标志。 */ abstract setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2): void; /** * Implement this callback to remove the value from a CSS style for an element in the DOM. - * - * 实现此回调,以便从 DOM 中某个元素上移除一个 CSS 样式值。 - * * @param el The element. - * - * 目标元素。 - * * @param style The name of the style. - * - * 样式名。 - * * @param flags Flags for style variations to remove, if set. ??? - * - * 要移除的样式修饰符标志。 */ abstract removeStyle(el: any, style: string, flags?: RendererStyleFlags2): void; /** * Implement this callback to set the value of a property of an element in the DOM. - * - * 实现此回调,以便设置 DOM 中某个元素的属性值。 - * * @param el The element. - * - * 目标元素。 - * * @param name The property name. - * - * 属性名。 - * * @param value The new value. - * - * 新值。 - * */ abstract setProperty(el: any, name: string, value: any): void; /** * Implement this callback to set the value of a node in the host element. - * - * 实现本回调,以便在宿主元素中设置节点的值。 - * * @param node The node. - * - * 目标节点。 - * * @param value The new value. - * - * 新值。 - * */ abstract setValue(node: any, value: string): void; /** * Implement this callback to start an event listener. - * - * 实现此回调以启动事件监听器。 - * * @param target The context in which to listen for events. Can be * the entire window or document, the body of the document, or a specific * DOM element. - * - * 要监听事件的上下文。可以是整个窗口或文档、文档的 body 或指定的 DOM 元素。 - * * @param eventName The event to listen for. - * - * 要监听的事件。 - * * @param callback A handler function to invoke when the event occurs. - * - * 当该事件发生时要执行的处理器函数。 - * * @returns An "unlisten" function for disposing of this handler. - * - * 一个 "取消监听" 函数,用于解除该处理器。 */ abstract listen( target: 'window'|'document'|'body'|any, eventName: string, diff --git a/packages/core/src/render/api_flags.ts b/packages/core/src/render/api_flags.ts index b556114f97..486a55260b 100644 --- a/packages/core/src/render/api_flags.ts +++ b/packages/core/src/render/api_flags.ts @@ -12,59 +12,32 @@ import {ViewEncapsulation} from '../metadata/view'; /** * Used by `RendererFactory2` to associate custom rendering data and styles * with a rendering implementation. - * - * 供 `RendererFactory2` 用于将自定义渲染数据和样式与某个渲染器的实现相关联。 - * * @publicApi */ export interface RendererType2 { /** * A unique identifying string for the new renderer, used when creating * unique styles for encapsulation. - * - * 创建新的封装样式时使用的新渲染器的唯一标识字符串。 - * */ id: string; /** * The view encapsulation type, which determines how styles are applied to * DOM elements. One of - * - * 视图封装类型,它确定如何将样式应用于 DOM 元素。为下列值之一 - * * - `Emulated` (default): Emulate native scoping of styles. - * - * `Emulated`(默认):模拟样式的原生作用域。 - * * - `Native`: Use the native encapsulation mechanism of the renderer. - * - * `Native` :使用渲染器的原生封装机制。 - * * - `ShadowDom`: Use modern [Shadow * DOM](https://w3c.github.io/webcomponents/spec/shadow/) and * create a ShadowRoot for component's host element. - * - * `ShadowDom` :使用现代的 [Shadow DOM](https://w3c.github.io/webcomponents/spec/shadow/) 并为组件的宿主元素创建 ShadowRoot。 - * * - `None`: Do not provide any template or style encapsulation. - * - * `None` :不提供任何模板或样式封装。 - * */ encapsulation: ViewEncapsulation; /** * Defines CSS styles to be stored on a renderer instance. - * - * 定义要存储在渲染器实例上的 CSS 样式。 - * */ styles: (string|any[])[]; /** * Defines arbitrary developer-defined data to be stored on a renderer instance. * This is useful for renderers that delegate to other renderers. - * - * 定义要存储在渲染器实例上的任意由开发人员定义的数据。这对于要委托其他渲染器实现的渲染器很有用。 - * */ data: {[kind: string]: any}; } @@ -72,9 +45,6 @@ export interface RendererType2 { /** * Flags for renderer-specific style modifiers. - * - * 渲染器特有样式修饰符的标志。 - * * @publicApi */ export enum RendererStyleFlags2 { @@ -83,16 +53,10 @@ export enum RendererStyleFlags2 { // the tests. The work around is to have hard coded value in `node_manipulation.ts` for now. /** * Marks a style as important. - * - * 将样式标记为重要。 - * */ Important = 1 << 0, /** * Marks a style as using dash case naming (this-is-dash-case). - * - * 将样式标记为使用中线命名法(this-is-dash-case)。 - * */ DashCase = 1 << 1 } diff --git a/packages/core/src/render3/STORING_METADATA_IN_D.TS.md b/packages/core/src/render3/STORING_METADATA_IN_D.TS.md index a1bcb68de0..c5e73c7570 100644 --- a/packages/core/src/render3/STORING_METADATA_IN_D.TS.md +++ b/packages/core/src/render3/STORING_METADATA_IN_D.TS.md @@ -41,7 +41,7 @@ class MyAppComponent { }) class MyAppModule { // ngtsc generates this: - static ɵdir = ɵɵdefineNgModule(...); + static ɵmod = ɵɵdefineNgModule(...); } ``` @@ -52,6 +52,6 @@ We store the information in the `.d.ts` file like so. ```typescript class TooltipDirective { - static ɵdir: DirectiveDefWithMeta<TooltipDirective, '[tooltip]', '', {}, {}, []> + static ɵdir: DirectiveDeclaration<TooltipDirective, '[tooltip]', '', {}, {}, []> } ``` diff --git a/packages/core/src/render3/assert.ts b/packages/core/src/render3/assert.ts index 2c14b2be03..aecb0e1fd2 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -7,6 +7,7 @@ */ import {assertDefined, assertEqual, assertNumber, throwError} from '../util/assert'; + import {getComponentDef, getNgModuleDef} from './definition'; import {LContainer} from './interfaces/container'; import {DirectiveDef} from './interfaces/definition'; @@ -14,8 +15,7 @@ import {TIcu} from './interfaces/i18n'; import {NodeInjectorOffset} from './interfaces/injector'; import {TNode} from './interfaces/node'; import {isLContainer, isLView} from './interfaces/type_checks'; -import {HEADER_OFFSET, LView, TVIEW, TView} from './interfaces/view'; - +import {DECLARATION_COMPONENT_VIEW, HEADER_OFFSET, LView, T_HOST, TVIEW, TView} from './interfaces/view'; // [Assert functions do not constraint type when they are guarded by a truthy // expression.](https://github.com/microsoft/TypeScript/issues/37295) @@ -135,6 +135,20 @@ export function assertBetween(lower: number, upper: number, index: number) { } } +export function assertProjectionSlots(lView: LView, errMessage?: string) { + assertDefined(lView[DECLARATION_COMPONENT_VIEW], 'Component views should exist.'); + assertDefined( + lView[DECLARATION_COMPONENT_VIEW][T_HOST]!.projection, + errMessage || + 'Components with projection nodes (<ng-content>) must have projection slots defined.'); +} + +export function assertParentView(lView: LView|null, errMessage?: string) { + assertDefined( + lView, + errMessage || 'Component views should always have a parent view (component\'s host view)'); +} + /** * This is a basic sanity check that the `injectorIndex` seems to point to what looks like a diff --git a/packages/core/src/render3/collect_native_nodes.ts b/packages/core/src/render3/collect_native_nodes.ts index 9786b43e27..8b5881232a 100644 --- a/packages/core/src/render3/collect_native_nodes.ts +++ b/packages/core/src/render3/collect_native_nodes.ts @@ -6,15 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ -import {assertDefined} from '../util/assert'; - +import {assertParentView} from './assert'; import {icuContainerIterate} from './i18n/i18n_tree_shaking'; import {CONTAINER_HEADER_OFFSET} from './interfaces/container'; -import {TElementNode, TIcuContainerNode, TNode, TNodeType} from './interfaces/node'; +import {TIcuContainerNode, TNode, TNodeType} from './interfaces/node'; import {RNode} from './interfaces/renderer_dom'; import {isLContainer} from './interfaces/type_checks'; import {DECLARATION_COMPONENT_VIEW, LView, T_HOST, TVIEW, TView} from './interfaces/view'; import {assertTNodeType} from './node_assert'; +import {getProjectionNodes} from './node_manipulation'; import {getLViewParent} from './util/view_traversal_utils'; import {unwrapRNode} from './util/view_utils'; @@ -58,23 +58,12 @@ export function collectNativeNodes( result.push(rNode); } } else if (tNodeType & TNodeType.Projection) { - const componentView = lView[DECLARATION_COMPONENT_VIEW]; - const componentHost = componentView[T_HOST] as TElementNode; - const slotIdx = tNode.projection as number; - ngDevMode && - assertDefined( - componentHost.projection, - 'Components with projection nodes (<ng-content>) must have projection slots defined.'); - - const nodesInSlot = componentHost.projection![slotIdx]; + const nodesInSlot = getProjectionNodes(lView, tNode); if (Array.isArray(nodesInSlot)) { result.push(...nodesInSlot); } else { - const parentView = getLViewParent(componentView)!; - ngDevMode && - assertDefined( - parentView, - 'Component views should always have a parent view (component\'s host view)'); + const parentView = getLViewParent(lView[DECLARATION_COMPONENT_VIEW])!; + ngDevMode && assertParentView(parentView); collectNativeNodes(parentView[TVIEW], parentView, nodesInSlot, result, true); } } diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index 5d63f4e0db..f2ba3baee4 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -12,7 +12,9 @@ import {Injector} from '../di/injector'; import {Type} from '../interface/type'; import {Sanitizer} from '../sanitization/sanitizer'; import {assertDefined, assertIndexInRange} from '../util/assert'; + import {assertComponentType} from './assert'; +import {readPatchedLView} from './context_discovery'; import {getComponentDef} from './definition'; import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di'; import {throwProviderNotFoundError} from './errors_di'; @@ -31,7 +33,6 @@ import {setUpAttributes} from './util/attrs_utils'; import {publishDefaultGlobalUtils} from './util/global_utils'; import {defaultScheduler} from './util/misc_utils'; import {getRootContext} from './util/view_traversal_utils'; -import {readPatchedLView} from './util/view_utils'; @@ -178,7 +179,7 @@ export function createRootComponentView( ngDevMode && assertIndexInRange(rootView, index); rootView[index] = rNode; // '#host' is added here as we don't know the real host DOM name (we don't want to read it) and at - // the same time we want to communicate the the debug `TNode` that this is a special `TNode` + // the same time we want to communicate the debug `TNode` that this is a special `TNode` // representing a host element. const tNode: TElementNode = getOrCreateTNode(tView, index, TNodeType.Element, '#host', null); const mergedAttrs = tNode.mergedAttrs = def.hostAttrs; diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index 96b118dc7e..0c4ebfb686 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -72,8 +72,6 @@ function getNamespace(elementName: string): string|null { /** * A change detection scheduler token for {@link RootContext}. This token is the default value used * for the default `RootContext` found in the {@link ROOT_CONTEXT} token. - * - * 供 {@link RootContext} 使用的变更检测调度器的令牌。该令牌是供 {@link ROOT_CONTEXT} 对应的默认 `RootContext` 使用的默认值。 */ export const SCHEDULER = new InjectionToken<((fn: () => void) => void)>('SCHEDULER_TOKEN', { providedIn: 'root', @@ -104,8 +102,6 @@ function createChainedInjector(rootViewInjector: Injector, moduleInjector: Injec /** * Render3 implementation of {@link viewEngine_ComponentFactory}. - * - * {@link viewEngine_ComponentFactory} 的 Render3 实现。 */ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> { selector: string; @@ -246,17 +242,12 @@ export function injectComponentFactoryResolver(): viewEngine_ComponentFactoryRes /** * Represents an instance of a Component created via a {@link ComponentFactory}. * - * 表示通过 {@link ComponentFactory} 创建的组件的实例。 - * * `ComponentRef` provides access to the Component Instance as well other objects related to this * Component Instance and allows you to destroy the Component Instance via the {@link #destroy} * method. * - * `ComponentRef` 提供了对该组件实例及其相关对象的访问能力,并允许你通过 {@link #destroy} 方法销毁该实例。 - * */ export class ComponentRef<T> extends viewEngine_ComponentRef<T> { - destroyCbs: (() => void)[]|null = []; instance: T; hostView: ViewRef<T>; changeDetectorRef: ViewEngine_ChangeDetectorRef; @@ -277,16 +268,10 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> { } destroy(): void { - if (this.destroyCbs) { - this.destroyCbs.forEach(fn => fn()); - this.destroyCbs = null; - !this.hostView.destroyed && this.hostView.destroy(); - } + this.hostView.destroy(); } onDestroy(callback: () => void): void { - if (this.destroyCbs) { - this.destroyCbs.push(callback); - } + this.hostView.onDestroy(callback); } } diff --git a/packages/core/src/render3/context_discovery.ts b/packages/core/src/render3/context_discovery.ts index 096d4f2056..43f4207eb7 100644 --- a/packages/core/src/render3/context_discovery.ts +++ b/packages/core/src/render3/context_discovery.ts @@ -7,14 +7,14 @@ */ import '../util/ng_dev_mode'; -import {assertDomNode} from '../util/assert'; +import {assertDefined, assertDomNode} from '../util/assert'; -import {EMPTY_ARRAY} from './empty'; -import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context'; +import {EMPTY_ARRAY} from '../util/empty'; +import {LContext} from './interfaces/context'; import {TNode, TNodeFlags} from './interfaces/node'; import {RElement, RNode} from './interfaces/renderer_dom'; import {CONTEXT, HEADER_OFFSET, HOST, LView, TVIEW} from './interfaces/view'; -import {getComponentLViewByIndex, readPatchedData, unwrapRNode} from './util/view_utils'; +import {getComponentLViewByIndex, unwrapRNode} from './util/view_utils'; @@ -170,14 +170,37 @@ export function getComponentViewByInstance(componentInstance: {}): LView { return view; } +/** + * This property will be monkey-patched on elements, components and directives. + */ +const MONKEY_PATCH_KEY_NAME = '__ngContext__'; + /** * 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) { + ngDevMode && assertDefined(target, 'Target expected'); target[MONKEY_PATCH_KEY_NAME] = data; } +/** + * Returns the monkey-patch value data present on the target (which could be + * a component, directive or a DOM node). + */ +export function readPatchedData(target: any): LView|LContext|null { + ngDevMode && assertDefined(target, 'Target expected'); + return target[MONKEY_PATCH_KEY_NAME] || null; +} + +export function readPatchedLView(target: any): LView|null { + const value = readPatchedData(target); + if (value) { + return Array.isArray(value) ? value : (value as LContext).lView; + } + return null; +} + export function isComponentInstance(instance: any): boolean { return instance && instance.constructor && instance.constructor.ɵcmp; } diff --git a/packages/core/src/render3/definition.ts b/packages/core/src/render3/definition.ts index 0b75b5c836..865b8aa5b3 100644 --- a/packages/core/src/render3/definition.ts +++ b/packages/core/src/render3/definition.ts @@ -12,9 +12,9 @@ import {NgModuleDef, NgModuleType} from '../metadata/ng_module_def'; import {SchemaMetadata} from '../metadata/schema'; import {ViewEncapsulation} from '../metadata/view'; import {noSideEffects} from '../util/closure'; +import {EMPTY_ARRAY, EMPTY_OBJ} from '../util/empty'; import {initNgDevMode} from '../util/ng_dev_mode'; import {stringify} from '../util/stringify'; -import {EMPTY_ARRAY, EMPTY_OBJ} from './empty'; import {NG_COMP_DEF, NG_DIR_DEF, NG_LOC_ID_DEF, NG_MOD_DEF, NG_PIPE_DEF} from './fields'; import {ComponentDef, ComponentDefFeature, ComponentTemplate, ComponentType, ContentQueriesFunction, DirectiveDef, DirectiveDefFeature, DirectiveTypesOrFactory, HostBindingsFunction, PipeDef, PipeTypesOrFactory, ViewQueriesFunction} from './interfaces/definition'; import {AttributeMarker, TAttributes, TConstantsOrFactory} from './interfaces/node'; @@ -287,67 +287,65 @@ export function ɵɵdefineComponent<T>(componentDefinition: { * The set of schemas that declare elements to be allowed in the component's template. */ schemas?: SchemaMetadata[] | null; -}): never { +}): unknown { return noSideEffects(() => { - // Initialize ngDevMode. This must be the first statement in ɵɵdefineComponent. - // See the `initNgDevMode` docstring for more information. - (typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode(); + // Initialize ngDevMode. This must be the first statement in ɵɵdefineComponent. + // See the `initNgDevMode` docstring for more information. + (typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode(); - const type = componentDefinition.type; - const typePrototype = type.prototype; - const declaredInputs: {[key: string]: string} = {} as any; - const def: Mutable<ComponentDef<any>, keyof ComponentDef<any>> = { - type: type, - providersResolver: null, - decls: componentDefinition.decls, - vars: componentDefinition.vars, - factory: null, - template: componentDefinition.template || null!, - consts: componentDefinition.consts || null, - ngContentSelectors: componentDefinition.ngContentSelectors, - hostBindings: componentDefinition.hostBindings || null, - hostVars: componentDefinition.hostVars || 0, - hostAttrs: componentDefinition.hostAttrs || null, - contentQueries: componentDefinition.contentQueries || null, - declaredInputs: declaredInputs, - inputs: null!, // assigned in noSideEffects - outputs: null!, // assigned in noSideEffects - exportAs: componentDefinition.exportAs || null, - onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush, - 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, - data: componentDefinition.data || {}, - // TODO(misko): convert ViewEncapsulation into const enum so that it can be used - // directly in the next line. Also `None` should be 0 not 2. - encapsulation: componentDefinition.encapsulation || ViewEncapsulation.Emulated, - id: 'c', - styles: componentDefinition.styles || EMPTY_ARRAY, - _: null as never, - setInput: null, - schemas: componentDefinition.schemas || null, - tView: null, - }; - const directiveTypes = componentDefinition.directives!; - const feature = componentDefinition.features; - const pipeTypes = componentDefinition.pipes!; - def.id += _renderCompCount++; - def.inputs = invertObject(componentDefinition.inputs, declaredInputs), - def.outputs = invertObject(componentDefinition.outputs), - feature && feature.forEach((fn) => fn(def)); - def.directiveDefs = directiveTypes ? - () => (typeof directiveTypes === 'function' ? directiveTypes() : directiveTypes) - .map(extractDirectiveDef) : - null; - def.pipeDefs = pipeTypes ? - () => - (typeof pipeTypes === 'function' ? pipeTypes() : pipeTypes).map(extractPipeDef) : - null; + const type = componentDefinition.type; + const declaredInputs: {[key: string]: string} = {} as any; + const def: Mutable<ComponentDef<any>, keyof ComponentDef<any>> = { + type: type, + providersResolver: null, + decls: componentDefinition.decls, + vars: componentDefinition.vars, + factory: null, + template: componentDefinition.template || null!, + consts: componentDefinition.consts || null, + ngContentSelectors: componentDefinition.ngContentSelectors, + hostBindings: componentDefinition.hostBindings || null, + hostVars: componentDefinition.hostVars || 0, + hostAttrs: componentDefinition.hostAttrs || null, + contentQueries: componentDefinition.contentQueries || null, + declaredInputs: declaredInputs, + inputs: null!, // assigned in noSideEffects + outputs: null!, // assigned in noSideEffects + exportAs: componentDefinition.exportAs || null, + onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush, + 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, + data: componentDefinition.data || {}, + // TODO(misko): convert ViewEncapsulation into const enum so that it can be used + // directly in the next line. Also `None` should be 0 not 2. + encapsulation: componentDefinition.encapsulation || ViewEncapsulation.Emulated, + id: 'c', + styles: componentDefinition.styles || EMPTY_ARRAY, + _: null, + setInput: null, + schemas: componentDefinition.schemas || null, + tView: null, + }; + const directiveTypes = componentDefinition.directives!; + const feature = componentDefinition.features; + const pipeTypes = componentDefinition.pipes!; + def.id += _renderCompCount++; + def.inputs = invertObject(componentDefinition.inputs, declaredInputs), + def.outputs = invertObject(componentDefinition.outputs), + feature && feature.forEach((fn) => fn(def)); + def.directiveDefs = directiveTypes ? + () => (typeof directiveTypes === 'function' ? directiveTypes() : directiveTypes) + .map(extractDirectiveDef) : + null; + def.pipeDefs = pipeTypes ? + () => (typeof pipeTypes === 'function' ? pipeTypes() : pipeTypes).map(extractPipeDef) : + null; - return def as never; - }) as never; + return def; + }); } /** @@ -411,7 +409,7 @@ export function ɵɵdefineNgModule<T>(def: { /** Unique ID for the module that is used with `getModuleFactory`. */ id?: string | null; -}): never { +}): unknown { const res: NgModuleDef<T> = { type: def.type, bootstrap: def.bootstrap || EMPTY_ARRAY, @@ -427,7 +425,7 @@ export function ɵɵdefineNgModule<T>(def: { autoRegisterModuleById[def.id!] = def.type as unknown as NgModuleType; }); } - return res as never; + return res; } /** @@ -452,13 +450,13 @@ export function ɵɵsetNgModuleScope(type: any, scope: { * module. */ exports?: Type<any>[] | (() => Type<any>[]); -}): void { +}): unknown { 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; + }); } /** @@ -717,14 +715,14 @@ export function ɵɵdefinePipe<T>(pipeDef: { /** Whether the pipe is pure. */ pure?: boolean -}): never { +}): unknown { 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 + }); } /** diff --git a/packages/core/src/render3/definition_factory.ts b/packages/core/src/render3/definition_factory.ts index 02b790ecb3..381445c9e3 100644 --- a/packages/core/src/render3/definition_factory.ts +++ b/packages/core/src/render3/definition_factory.ts @@ -19,7 +19,7 @@ export type FactoryFn<T> = { * Subclasses without an explicit constructor call through to the factory of their base * definition, providing it with their own constructor to instantiate. */ - <U extends T>(t: Type<U>): U; + <U extends T>(t?: Type<U>): U; /** * If no constructor to instantiate is provided, an instance of type T itself is created. diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index 277dcff6aa..0a634787bd 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -11,7 +11,7 @@ import {injectRootLimpMode, setInjectImplementation} from '../di/inject_switch'; import {InjectionToken} from '../di/injection_token'; import {Injector} from '../di/injector'; import {InjectorMarkers} from '../di/injector_marker'; -import {getInjectorDef} from '../di/interface/defs'; +import {getInjectableDef} from '../di/interface/defs'; import {InjectFlags} from '../di/interface/injector'; import {AbstractType, Type} from '../interface/type'; import {assertDefined, assertEqual, assertIndexInRange} from '../util/assert'; @@ -87,6 +87,13 @@ export function setIncludeViewProviders(v: boolean): boolean { const BLOOM_SIZE = 256; const BLOOM_MASK = BLOOM_SIZE - 1; +/** + * The number of bits that is represented by a single bloom bucket. JS bit operations are 32 bits, + * so each bucket represents 32 distinct tokens which accounts for log2(32) = 5 bits of a bloom hash + * number. + */ +const BLOOM_BUCKET_BITS = 5; + /** Counter used to generate unique IDs for directives. */ let nextNgElementId = 0; @@ -116,27 +123,17 @@ export function bloomAdd( // We only have BLOOM_SIZE (256) slots in our bloom filter (8 buckets * 32 bits each), // so all unique IDs must be modulo-ed into a number from 0 - 255 to fit into the filter. - const bloomBit = id & BLOOM_MASK; + const bloomHash = id & BLOOM_MASK; // Create a mask that targets the specific bit associated with the directive. // 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. - const mask = 1 << bloomBit; + const mask = 1 << bloomHash; - // Use the raw bloomBit number to determine which bloom filter bucket we should check - // e.g: bf0 = [0 - 31], bf1 = [32 - 63], bf2 = [64 - 95], bf3 = [96 - 127], etc - const b7 = bloomBit & 0x80; - const b6 = bloomBit & 0x40; - const b5 = bloomBit & 0x20; - const tData = tView.data as number[]; - - if (b7) { - b6 ? (b5 ? (tData[injectorIndex + 7] |= mask) : (tData[injectorIndex + 6] |= mask)) : - (b5 ? (tData[injectorIndex + 5] |= mask) : (tData[injectorIndex + 4] |= mask)); - } else { - b6 ? (b5 ? (tData[injectorIndex + 3] |= mask) : (tData[injectorIndex + 2] |= mask)) : - (b5 ? (tData[injectorIndex + 1] |= mask) : (tData[injectorIndex] |= mask)); - } + // Each bloom bucket in `tData` represents `BLOOM_BUCKET_BITS` number of bits of `bloomHash`. + // Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset that the mask + // should be written to. + (tView.data as number[])[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)] |= mask; } /** @@ -274,23 +271,13 @@ export function diPublicInInjector( /** * Inject static attribute value into directive constructor. * - * 将静态属性值注入到指令构造函数中。 - * * This method is used with `factory` functions which are generated as part of * `defineDirective` or `defineComponent`. The method retrieves the static value * of an attribute. (Dynamic attributes are not supported since they are not resolved * at the time of injection and can change over time.) * - * 本方法与 `factory` 函数一起使用,这些工厂函数是 `defineDirective` 或 `defineComponent` 生成物的一部分。该方法会检索属性的静态值。(不支持动态属性,因为它们在注入时尚无法解析,并且会随着时间变化。) - * * # Example - * - * # 例 - * * Given: - * - * 给定: - * * ``` * @Component(...) * class MyComponent { @@ -298,17 +285,11 @@ export function diPublicInInjector( * } * ``` * When instantiated with - * - * 当使用下列方式实例化时 - * * ``` * <my-component title="Hello"></my-component> * ``` * * Then factory method generated is: - * - * 所生成的工厂方法是: - * * ``` * MyComponent.ɵcmp = defineComponent({ * factory: () => new MyComponent(injectAttribute('title')) @@ -440,7 +421,7 @@ export function getOrCreateInjectable<T>( lookupTokenUsingModuleInjector<T>(lView, token, flags, notFoundValue); } try { - const value = bloomHash(); + const value = bloomHash(flags); if (value == null && !(flags & InjectFlags.Optional)) { throwProviderNotFoundError(token); } else { @@ -691,22 +672,11 @@ export function bloomHasToken(bloomHash: number, injectorIndex: number, injector // 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. const mask = 1 << bloomHash; - const b7 = bloomHash & 0x80; - const b6 = bloomHash & 0x40; - const b5 = bloomHash & 0x20; - // Our bloom filter size is 256 bits, which is eight 32-bit bloom filter buckets: - // bf0 = [0 - 31], bf1 = [32 - 63], bf2 = [64 - 95], bf3 = [96 - 127], etc. - // Get the bloom filter value from the appropriate bucket based on the directive's bloomBit. - let value: number; - - if (b7) { - value = b6 ? (b5 ? injectorView[injectorIndex + 7] : injectorView[injectorIndex + 6]) : - (b5 ? injectorView[injectorIndex + 5] : injectorView[injectorIndex + 4]); - } else { - value = b6 ? (b5 ? injectorView[injectorIndex + 3] : injectorView[injectorIndex + 2]) : - (b5 ? injectorView[injectorIndex + 1] : injectorView[injectorIndex]); - } + // Each bloom bucket in `injectorView` represents `BLOOM_BUCKET_BITS` number of bits of + // `bloomHash`. Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset + // that should be used. + const value = injectorView[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)]; // If the bloom filter value has the bit corresponding to the directive's bloomBit flipped on, // this injector is a potential match. @@ -728,40 +698,19 @@ export class NodeInjector implements Injector { } } -/** - * @codeGenApi - */ -export function ɵɵgetFactoryOf<T>(type: Type<any>): FactoryFn<T>|null { - const typeAny = type as any; - - if (isForwardRef(type)) { - return (() => { - const factory = ɵɵgetFactoryOf<T>(resolveForwardRef(typeAny)); - return factory ? factory() : null; - }) as any; - } - - let factory = getFactoryDef<T>(typeAny); - if (factory === null) { - const injectorDef = getInjectorDef<T>(typeAny); - factory = injectorDef && injectorDef.factory; - } - return factory || null; -} - /** * @codeGenApi */ export function ɵɵgetInheritedFactory<T>(type: Type<any>): (type: Type<T>) => T { return noSideEffects(() => { const ownConstructor = type.prototype.constructor; - const ownFactory = ownConstructor[NG_FACTORY_DEF] || ɵɵgetFactoryOf(ownConstructor); + const ownFactory = ownConstructor[NG_FACTORY_DEF] || getFactoryOf(ownConstructor); const objectPrototype = Object.prototype; let parent = Object.getPrototypeOf(type.prototype).constructor; // Go up the prototype until we hit `Object`. while (parent && parent !== objectPrototype) { - const factory = parent[NG_FACTORY_DEF] || ɵɵgetFactoryOf(parent); + const factory = parent[NG_FACTORY_DEF] || getFactoryOf(parent); // If we hit something that has a factory and the factory isn't the same as the type, // we've found the inherited factory. Note the check that the factory isn't the type's @@ -782,3 +731,13 @@ export function ɵɵgetInheritedFactory<T>(type: Type<any>): (type: Type<T>) => return t => new t(); }); } + +function getFactoryOf<T>(type: Type<any>): ((type?: Type<T>) => T | null)|null { + if (isForwardRef(type)) { + return () => { + const factory = getFactoryOf<T>(resolveForwardRef(type)); + return factory && factory(); + }; + } + return getFactoryDef<T>(type); +} diff --git a/packages/core/src/render3/empty.ts b/packages/core/src/render3/empty.ts deleted file mode 100644 index 52382fcf72..0000000000 --- a/packages/core/src/render3/empty.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -import {initNgDevMode} from '../util/ng_dev_mode'; - -/** - * This file contains reuseable "empty" symbols that can be used as default return values - * in different parts of the rendering code. Because the same symbols are returned, this - * allows for identity checks against these values to be consistently used by the framework - * code. - */ - -export const EMPTY_OBJ: {} = {}; -export const EMPTY_ARRAY: any[] = []; - -// freezing the values prevents any code from accidentally inserting new values in -if ((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode()) { - // These property accesses can be ignored because ngDevMode will be set to false - // when optimizing code and the whole if statement will be dropped. - // tslint:disable-next-line:no-toplevel-property-access - Object.freeze(EMPTY_OBJ); - // tslint:disable-next-line:no-toplevel-property-access - Object.freeze(EMPTY_ARRAY); -} diff --git a/packages/core/src/render3/error_code.ts b/packages/core/src/render3/error_code.ts index b68d111b07..80e478d166 100644 --- a/packages/core/src/render3/error_code.ts +++ b/packages/core/src/render3/error_code.ts @@ -6,6 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ +// Base URL for the error details page. +// Keep this value in sync with a similar const in +// `packages/compiler-cli/src/ngtsc/diagnostics/src/error_code.ts`. +const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.io/errors'; + export const enum RuntimeErrorCode { // Internal Errors @@ -38,8 +43,33 @@ export class RuntimeError extends Error { } } +// Contains a set of error messages that have details guides at angular.io. +// Full list of available error guides can be found at https://angular.io/errors +/* tslint:disable:no-toplevel-property-access */ +export const RUNTIME_ERRORS_WITH_GUIDES = new Set([ + RuntimeErrorCode.EXPRESSION_CHANGED_AFTER_CHECKED, + RuntimeErrorCode.CYCLIC_DI_DEPENDENCY, + RuntimeErrorCode.PROVIDER_NOT_FOUND, + RuntimeErrorCode.MULTIPLE_COMPONENTS_MATCH, + RuntimeErrorCode.EXPORT_NOT_FOUND, + RuntimeErrorCode.PIPE_NOT_FOUND, +]); +/* tslint:enable:no-toplevel-property-access */ + /** Called to format a runtime error */ export function formatRuntimeError(code: RuntimeErrorCode, message: string): string { const fullCode = code ? `NG0${code}: ` : ''; - return `${fullCode}${message}`; + + let errorMessage = `${fullCode}${message}`; + + // Some runtime errors are still thrown without `ngDevMode` (for example + // `throwProviderNotFoundError`), so we add `ngDevMode` check here to avoid pulling + // `RUNTIME_ERRORS_WITH_GUIDES` symbol into prod bundles. + // TODO: revisit all instances where `RuntimeError` is thrown and see if `ngDevMode` can be added + // there instead to tree-shake more devmode-only code (and eventually remove `ngDevMode` check + // from this code). + if (ngDevMode && RUNTIME_ERRORS_WITH_GUIDES.has(code)) { + errorMessage = `${errorMessage}. Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/NG0${code}`; + } + return errorMessage; } diff --git a/packages/core/src/render3/errors.ts b/packages/core/src/render3/errors.ts index 75b9392936..a8e5300492 100644 --- a/packages/core/src/render3/errors.ts +++ b/packages/core/src/render3/errors.ts @@ -22,7 +22,7 @@ export function throwMultipleComponentError(tNode: TNode): never { /** Throws an ExpressionChangedAfterChecked error if checkNoChanges mode is on. */ export function throwErrorIfNoChangesMode( - creationMode: boolean, oldValue: any, currValue: any, propName?: string): never|void { + creationMode: boolean, oldValue: any, currValue: any, propName?: string): never { const field = propName ? ` for '${propName}'` : ''; let msg = `ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value${ diff --git a/packages/core/src/render3/features/inherit_definition_feature.ts b/packages/core/src/render3/features/inherit_definition_feature.ts index 891a2b9a92..cbbc242829 100644 --- a/packages/core/src/render3/features/inherit_definition_feature.ts +++ b/packages/core/src/render3/features/inherit_definition_feature.ts @@ -7,8 +7,8 @@ */ import {Type, Writable} from '../../interface/type'; +import {EMPTY_ARRAY, EMPTY_OBJ} from '../../util/empty'; import {fillProperties} from '../../util/property'; -import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty'; import {ComponentDef, ContentQueriesFunction, DirectiveDef, DirectiveDefFeature, HostBindingsFunction, RenderFlags, ViewQueriesFunction} from '../interfaces/definition'; import {TAttributes} from '../interfaces/node'; import {isComponentDef} from '../interfaces/type_checks'; diff --git a/packages/core/src/render3/features/ng_onchanges_feature.ts b/packages/core/src/render3/features/ng_onchanges_feature.ts index a4b82c0df8..86d25476a6 100644 --- a/packages/core/src/render3/features/ng_onchanges_feature.ts +++ b/packages/core/src/render3/features/ng_onchanges_feature.ts @@ -8,7 +8,7 @@ import {OnChanges} from '../../interface/lifecycle_hooks'; import {SimpleChange, SimpleChanges} from '../../interface/simple_change'; -import {EMPTY_OBJ} from '../empty'; +import {EMPTY_OBJ} from '../../util/empty'; import {DirectiveDef, DirectiveDefFeature} from '../interfaces/definition'; /** diff --git a/packages/core/src/render3/hooks.ts b/packages/core/src/render3/hooks.ts index b7b41ac026..55fb0363bf 100644 --- a/packages/core/src/render3/hooks.ts +++ b/packages/core/src/render3/hooks.ts @@ -13,6 +13,7 @@ import {NgOnChangesFeatureImpl} from './features/ng_onchanges_feature'; import {DirectiveDef} from './interfaces/definition'; import {TNode} from './interfaces/node'; import {FLAGS, HookData, InitPhaseState, LView, LViewFlags, PREORDER_HOOK_FLAGS, PreOrderHookFlags, TView} from './interfaces/view'; +import {profiler, ProfilerEvent} from './profiler'; import {isInCheckNoChangesMode} from './state'; @@ -212,9 +213,10 @@ function callHooks( (currentView[PREORDER_HOOK_FLAGS] & PreOrderHookFlags.IndexOfTheNextPreOrderHookMaskMask) : 0; const nodeIndexLimit = currentNodeIndex != null ? currentNodeIndex : -1; + const max = arr.length - 1; // Stop the loop at length - 1, because we look for the hook at i + 1 let lastNodeIndexFound = 0; - for (let i = startIndex; i < arr.length; i++) { - const hook = arr[i + 1] as () => void; + for (let i = startIndex; i < max; i++) { + const hook = arr[i + 1] as number | (() => void); if (typeof hook === 'number') { lastNodeIndexFound = arr[i] as number; if (currentNodeIndex != null && lastNodeIndexFound >= currentNodeIndex) { @@ -250,15 +252,24 @@ function callHook(currentView: LView, initPhase: InitPhaseState, arr: HookData, const directive = currentView[directiveIndex]; if (isInitHook) { const indexWithintInitPhase = currentView[FLAGS] >> LViewFlags.IndexWithinInitPhaseShift; - // The init phase state must be always checked here as it may have been recursively - // updated + // The init phase state must be always checked here as it may have been recursively updated. if (indexWithintInitPhase < (currentView[PREORDER_HOOK_FLAGS] >> PreOrderHookFlags.NumberOfInitHooksCalledShift) && (currentView[FLAGS] & LViewFlags.InitPhaseStateMask) === initPhase) { currentView[FLAGS] += LViewFlags.IndexWithinInitPhaseIncrementer; - hook.call(directive); + profiler(ProfilerEvent.LifecycleHookStart, directive, hook); + try { + hook.call(directive); + } finally { + profiler(ProfilerEvent.LifecycleHookEnd, directive, hook); + } } } else { - hook.call(directive); + profiler(ProfilerEvent.LifecycleHookStart, directive, hook); + try { + hook.call(directive); + } finally { + profiler(ProfilerEvent.LifecycleHookEnd, directive, hook); + } } } diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index 483f45e7b4..aa8b010bce 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -11,12 +11,13 @@ import {ɵɵCopyDefinitionFeature} from './features/copy_definition_feature'; import {ɵɵInheritDefinitionFeature} from './features/inherit_definition_feature'; import {ɵɵNgOnChangesFeature} from './features/ng_onchanges_feature'; import {ɵɵProvidersFeature} from './features/providers_feature'; -import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, PipeDef, ɵɵComponentDefWithMeta, ɵɵDirectiveDefWithMeta, ɵɵFactoryDef, ɵɵPipeDefWithMeta} from './interfaces/definition'; +import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, PipeDef} from './interfaces/definition'; +import {ɵɵComponentDeclaration, ɵɵDirectiveDeclaration, ɵɵFactoryDeclaration, ɵɵInjectorDeclaration, ɵɵNgModuleDeclaration, ɵɵPipeDeclaration} from './interfaces/public_definitions'; import {getComponent, getDirectives, getHostElement, getRenderedText} from './util/discovery_utils'; export {NgModuleType} from '../metadata/ng_module_def'; export {ComponentFactory, ComponentFactoryResolver, ComponentRef, injectComponentFactoryResolver} from './component_ref'; -export {ɵɵgetFactoryOf, ɵɵgetInheritedFactory} from './di'; +export {ɵɵgetInheritedFactory} from './di'; export {getLocaleId, setLocaleId} from './i18n/i18n_locale_id'; // clang-format off export { @@ -134,10 +135,6 @@ export { AttributeMarker } from './interfaces/node'; export {CssSelectorList, ProjectionSlots} from './interfaces/projection'; -export { - ɵɵngDeclareComponent, - ɵɵngDeclareDirective, -} from './jit/partial'; export { setClassMetadata, } from './metadata'; @@ -166,9 +163,6 @@ export { ɵɵcontentQuery, ɵɵloadQuery, ɵɵqueryRefresh, - ɵɵstaticContentQuery -, - ɵɵstaticViewQuery, ɵɵviewQuery} from './query'; export { ɵɵdisableBindings, @@ -178,7 +172,7 @@ export { } from './state'; export {NO_CHANGE} from './tokens'; export { ɵɵresolveBody, ɵɵresolveDocument,ɵɵresolveWindow} from './util/misc_utils'; -export { ɵɵinjectPipeChangeDetectorRef,ɵɵtemplateRefExtractor} from './view_engine_compatibility_prebound'; +export { ɵɵtemplateRefExtractor} from './view_engine_compatibility_prebound'; // clang-format on export { @@ -195,17 +189,19 @@ export { PipeDef, renderComponent, whenRendered, - ɵɵComponentDefWithMeta, + ɵɵComponentDeclaration, ɵɵCopyDefinitionFeature, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdefineNgModule, ɵɵdefinePipe, - ɵɵDirectiveDefWithMeta, - ɵɵFactoryDef, + ɵɵDirectiveDeclaration, + ɵɵFactoryDeclaration, ɵɵInheritDefinitionFeature, + ɵɵInjectorDeclaration, + ɵɵNgModuleDeclaration, ɵɵNgOnChangesFeature, - ɵɵPipeDefWithMeta, + ɵɵPipeDeclaration, ɵɵProvidersFeature, ɵɵsetComponentScope, ɵɵsetNgModuleScope, diff --git a/packages/core/src/render3/instructions/i18n_icu_container_visitor.ts b/packages/core/src/render3/instructions/i18n_icu_container_visitor.ts index 1d203a2acf..acb42f498c 100644 --- a/packages/core/src/render3/instructions/i18n_icu_container_visitor.ts +++ b/packages/core/src/render3/instructions/i18n_icu_container_visitor.ts @@ -7,8 +7,8 @@ */ import {assertDomNode, assertNumber, assertNumberInRange} from '../../util/assert'; +import {EMPTY_ARRAY} from '../../util/empty'; import {assertTIcu, assertTNodeForLView} from '../assert'; -import {EMPTY_ARRAY} from '../empty'; import {getCurrentICUCaseIndex} from '../i18n/i18n_util'; import {I18nRemoveOpCodes, TIcu} from '../interfaces/i18n'; import {TIcuContainerNode} from '../interfaces/node'; diff --git a/packages/core/src/render3/instructions/listener.ts b/packages/core/src/render3/instructions/listener.ts index 50616cc962..c469fd8ece 100644 --- a/packages/core/src/render3/instructions/listener.ts +++ b/packages/core/src/render3/instructions/listener.ts @@ -8,18 +8,19 @@ import {assertIndexInRange} from '../../util/assert'; +import {EMPTY_OBJ} from '../../util/empty'; import {isObservable} from '../../util/lang'; -import {EMPTY_OBJ} from '../empty'; import {PropertyAliasValue, TNode, TNodeFlags, TNodeType} from '../interfaces/node'; import {GlobalTargetResolver, isProceduralRenderer, Renderer3} from '../interfaces/renderer'; import {RElement} from '../interfaces/renderer_dom'; import {isDirectiveHost} from '../interfaces/type_checks'; -import {CLEANUP, FLAGS, LView, LViewFlags, RENDERER, TView} from '../interfaces/view'; +import {CLEANUP, CONTEXT, FLAGS, LView, LViewFlags, RENDERER, TView} from '../interfaces/view'; import {assertTNodeType} from '../node_assert'; +import {profiler, ProfilerEvent} from '../profiler'; import {getCurrentDirectiveDef, getCurrentTNode, getLView, getTView} from '../state'; import {getComponentLViewByIndex, getNativeByTNode, unwrapRNode} from '../util/view_utils'; -import {getLCleanup, handleError, loadComponentRenderer, markViewDirty} from './shared'; +import {getOrCreateLViewCleanup, getOrCreateTViewCleanup, handleError, loadComponentRenderer, markViewDirty} from './shared'; @@ -120,12 +121,13 @@ function listenerInternal( eventTargetResolver?: GlobalTargetResolver): void { const isTNodeDirectiveHost = isDirectiveHost(tNode); const firstCreatePass = tView.firstCreatePass; - const tCleanup: false|any[] = firstCreatePass && (tView.cleanup || (tView.cleanup = [])); + const tCleanup: false|any[] = firstCreatePass && getOrCreateTViewCleanup(tView); + const context = lView[CONTEXT]; // When the ɵɵlistener instruction was generated and is executed we know that there is either a // native listener or a directive output on this element. As such we we know that we will have to // register a listener and store its cleanup function on LView. - const lCleanup = getLCleanup(lView); + const lCleanup = getOrCreateLViewCleanup(lView); ngDevMode && assertTNodeType(tNode, TNodeType.AnyRNode | TNodeType.AnyContainer); @@ -177,7 +179,7 @@ function listenerInternal( // The first argument of `listen` function in Procedural Renderer is: // - either a target name (as a string) in case of global target (window, document, body) // - or element reference (in all other cases) - listenerFn = wrapListener(tNode, lView, listenerFn, false /** preventDefault */); + listenerFn = wrapListener(tNode, lView, context, listenerFn, false /** preventDefault */); const cleanupFn = renderer.listen(resolved.name || target, eventName, listenerFn); ngDevMode && ngDevMode.rendererAddEventListener++; @@ -186,7 +188,7 @@ function listenerInternal( } } else { - listenerFn = wrapListener(tNode, lView, listenerFn, true /** preventDefault */); + listenerFn = wrapListener(tNode, lView, context, listenerFn, true /** preventDefault */); target.addEventListener(eventName, listenerFn, useCapture); ngDevMode && ngDevMode.rendererAddEventListener++; @@ -196,7 +198,7 @@ function listenerInternal( } else { // Even if there is no native listener to add, we still need to wrap the listener so that OnPush // ancestors are marked dirty when an event occurs. - listenerFn = wrapListener(tNode, lView, listenerFn, false /** preventDefault */); + listenerFn = wrapListener(tNode, lView, context, listenerFn, false /** preventDefault */); } // subscribe to directive outputs @@ -227,13 +229,16 @@ function listenerInternal( } function executeListenerWithErrorHandling( - lView: LView, listenerFn: (e?: any) => any, e: any): boolean { + lView: LView, context: {}|null, listenerFn: (e?: any) => any, e: any): boolean { try { + profiler(ProfilerEvent.OutputStart, context, listenerFn); // Only explicitly returning false from a listener should preventDefault return listenerFn(e) !== false; } catch (error) { handleError(lView, error); return false; + } finally { + profiler(ProfilerEvent.OutputEnd, context, listenerFn); } } @@ -248,7 +253,7 @@ function executeListenerWithErrorHandling( * (the procedural renderer does this already, so in those cases, we should skip) */ function wrapListener( - tNode: TNode, lView: LView, listenerFn: (e?: any) => any, + tNode: TNode, lView: LView, context: {}|null, listenerFn: (e?: any) => any, wrapWithPreventDefault: boolean): EventListener { // Note: we are performing most of the work in the listener function itself // to optimize listener registration. @@ -270,13 +275,13 @@ function wrapListener( markViewDirty(startView); } - let result = executeListenerWithErrorHandling(lView, listenerFn, e); + let result = executeListenerWithErrorHandling(lView, context, listenerFn, e); // A just-invoked listener function might have coalesced listeners so we need to check for // their presence and invoke as needed. let nextListenerFn = (<any>wrapListenerIn_markDirtyAndPreventDefault).__ngNextListenerFn__; while (nextListenerFn) { // We should prevent default if any of the listeners explicitly return false - result = executeListenerWithErrorHandling(lView, nextListenerFn, e) && result; + result = executeListenerWithErrorHandling(lView, context, nextListenerFn, e) && result; nextListenerFn = (<any>nextListenerFn).__ngNextListenerFn__; } diff --git a/packages/core/src/render3/instructions/lview_debug.ts b/packages/core/src/render3/instructions/lview_debug.ts index 395d5d7b22..1fd21dfbfd 100644 --- a/packages/core/src/render3/instructions/lview_debug.ts +++ b/packages/core/src/render3/instructions/lview_debug.ts @@ -15,7 +15,7 @@ import {assertDefined} from '../../util/assert'; import {createNamedArrayType} from '../../util/named_array_type'; import {initNgDevMode} from '../../util/ng_dev_mode'; import {assertNodeInjector} from '../assert'; -import {getInjectorIndex} from '../di'; +import {getInjectorIndex, getParentInjectorLocation} from '../di'; import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS, NATIVE} from '../interfaces/container'; import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/definition'; import {NO_PARENT_INJECTOR, NodeInjectorOffset} from '../interfaces/injector'; @@ -25,7 +25,7 @@ import {LQueries, TQueries} from '../interfaces/query'; import {Renderer3, RendererFactory3} from '../interfaces/renderer'; import {RComment, RElement, RNode} from '../interfaces/renderer_dom'; import {getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, TStylingKey, TStylingRange} from '../interfaces/styling'; -import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DebugNode, DECLARATION_VIEW, DestroyHookData, FLAGS, HEADER_OFFSET, HookData, HOST, HostBindingOpCodes, INJECTOR, LContainerDebug as ILContainerDebug, LView, LViewDebug as ILViewDebug, LViewDebugRange, LViewDebugRangeContent, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, T_HOST, TData, TView as ITView, TVIEW, TView, TViewType, TViewTypeAsString} from '../interfaces/view'; +import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DebugNode, DECLARATION_VIEW, DestroyHookData, FLAGS, HEADER_OFFSET, HookData, HOST, HostBindingOpCodes, INJECTOR, LContainerDebug as ILContainerDebug, LView, LViewDebug as ILViewDebug, LViewDebugRange, LViewDebugRangeContent, LViewFlags, NEXT, NodeInjectorDebug, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, T_HOST, TData, TView as ITView, TVIEW, TView, TViewType, TViewTypeAsString} from '../interfaces/view'; import {attachDebugObject} from '../util/debug_utils'; import {getParentInjectorIndex, getParentInjectorView} from '../util/injector_utils'; import {unwrapRNode} from '../util/view_utils'; @@ -102,7 +102,6 @@ function getLViewToClone(type: TViewType, name: string|null): Array<any> { } return embeddedArray; } - throw new Error('unreachable code'); } function nameSuffix(text: string|null|undefined): string { @@ -216,8 +215,20 @@ class TNode implements ITNode { debugNodeInjectorPath(lView: LView): DebugNode[] { const path: DebugNode[] = []; let injectorIndex = getInjectorIndex(this, lView); - ngDevMode && assertNodeInjector(lView, injectorIndex); + if (injectorIndex === -1) { + // Looks like the current `TNode` does not have `NodeInjector` associated with it => look for + // parent NodeInjector. + const parentLocation = getParentInjectorLocation(this, lView); + if (parentLocation !== NO_PARENT_INJECTOR) { + // We found a parent, so start searching from the parent location. + injectorIndex = getParentInjectorIndex(parentLocation); + lView = getParentInjectorView(parentLocation, lView); + } else { + // No parents have been found, so there are no `NodeInjector`s to consult. + } + } while (injectorIndex !== -1) { + ngDevMode && assertNodeInjector(lView, injectorIndex); const tNode = lView[TVIEW].data[injectorIndex + NodeInjectorOffset.TNODE] as TNode; path.push(buildDebugNode(tNode, lView)); const parentLocation = lView[injectorIndex + NodeInjectorOffset.PARENT]; @@ -583,15 +594,19 @@ export function buildDebugNode(tNode: ITNode, lView: LView): DebugNode { return { html: toHtml(native), type: toTNodeTypeAsString(tNode.type), + tNode, native: native as any, children: toDebugNodes(tNode.child, lView), factories, instances, - injector: buildNodeInjectorDebug(tNode, tView, lView) + injector: buildNodeInjectorDebug(tNode, tView, lView), + get injectorResolutionPath() { + return (tNode as TNode).debugNodeInjectorPath(lView); + }, }; } -function buildNodeInjectorDebug(tNode: ITNode, tView: ITView, lView: LView) { +function buildNodeInjectorDebug(tNode: ITNode, tView: ITView, lView: LView): NodeInjectorDebug { const viewProviders: Type<any>[] = []; for (let i = (tNode as TNode).providerIndexStart_; i < (tNode as TNode).providerIndexEnd_; i++) { viewProviders.push(tView.data[i] as Type<any>); @@ -633,6 +648,9 @@ function binary(array: any[], idx: number): string { * @param idx */ function toBloom(array: any[], idx: number): string { + if (idx < 0) { + return 'NO_NODE_INJECTOR'; + } return `${binary(array, idx + 7)}_${binary(array, idx + 6)}_${binary(array, idx + 5)}_${ binary(array, idx + 4)}_${binary(array, idx + 3)}_${binary(array, idx + 2)}_${ binary(array, idx + 1)}_${binary(array, idx + 0)}`; diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 17a233ed7d..34d6ef6daf 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -13,12 +13,13 @@ import {ViewEncapsulation} from '../../metadata/view'; import {validateAgainstEventAttributes, validateAgainstEventProperties} from '../../sanitization/sanitization'; import {Sanitizer} from '../../sanitization/sanitizer'; import {assertDefined, assertDomNode, assertEqual, assertGreaterThanOrEqual, assertIndexInRange, assertNotEqual, assertNotSame, assertSame, assertString} from '../../util/assert'; +import {escapeCommentText} from '../../util/dom'; import {createNamedArrayType} from '../../util/named_array_type'; import {initNgDevMode} from '../../util/ng_dev_mode'; import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect'; import {stringify} from '../../util/stringify'; import {assertFirstCreatePass, assertFirstUpdatePass, assertLContainer, assertLView, assertTNodeForLView, assertTNodeForTView} from '../assert'; -import {attachPatchData} from '../context_discovery'; +import {attachPatchData, readPatchedLView} from '../context_discovery'; import {getFactoryDef} from '../definition_factory'; import {diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode} from '../di'; import {formatRuntimeError, RuntimeError, RuntimeErrorCode} from '../error_code'; @@ -36,13 +37,14 @@ import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_COMPONENT_VIEW, DE import {assertPureTNodeType, assertTNodeType} from '../node_assert'; import {updateTextNode} from '../node_manipulation'; import {isInlineTemplate, isNodeMatchingSelectorList} from '../node_selector_matcher'; +import {profiler, ProfilerEvent} from '../profiler'; import {enterView, getBindingsEnabled, getCurrentDirectiveIndex, getCurrentParentTNode, getCurrentTNode, getCurrentTNodePlaceholderOk, getSelectedIndex, isCurrentTNodeParent, isInCheckNoChangesMode, isInI18nBlock, leaveView, setBindingIndex, setBindingRootForHostBindings, setCurrentDirectiveIndex, setCurrentQueryIndex, setCurrentTNode, setIsInCheckNoChangesMode, setSelectedIndex} from '../state'; import {NO_CHANGE} from '../tokens'; import {isAnimationProp, mergeHostAttrs} from '../util/attrs_utils'; import {INTERPOLATION_DELIMITER} from '../util/misc_utils'; import {renderStringify, stringifyForError} from '../util/stringify_utils'; import {getFirstLContainer, getLViewParent, getNextLContainer} from '../util/view_traversal_utils'; -import {getComponentLViewByIndex, getNativeByIndex, getNativeByTNode, isCreationMode, readPatchedLView, resetPreOrderHookFlags, unwrapLView, updateTransplantedViewCount, viewAttachedToChangeDetector} from '../util/view_utils'; +import {getComponentLViewByIndex, getNativeByIndex, getNativeByTNode, isCreationMode, resetPreOrderHookFlags, unwrapLView, updateTransplantedViewCount, viewAttachedToChangeDetector} from '../util/view_utils'; import {selectIndexInternal} from './advance'; import {attachLContainerDebug, attachLViewDebug, cloneToLViewFromTViewBlueprint, cloneToTViewData, LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeDebug, TNodeInitialInputs, TNodeLocalNames, TViewComponents, TViewConstructor} from './lview_debug'; @@ -500,16 +502,25 @@ export function renderComponentOrTemplate<T>( function executeTemplate<T>( tView: TView, lView: LView, templateFn: ComponentTemplate<T>, rf: RenderFlags, context: T) { const prevSelectedIndex = getSelectedIndex(); + const isUpdatePhase = rf & RenderFlags.Update; try { setSelectedIndex(-1); - if (rf & RenderFlags.Update && lView.length > HEADER_OFFSET) { + if (isUpdatePhase && lView.length > HEADER_OFFSET) { // When we're updating, inherently select 0 so we don't // have to generate that instruction for most update blocks. selectIndexInternal(tView, lView, HEADER_OFFSET, isInCheckNoChangesMode()); } + + const preHookType = + isUpdatePhase ? ProfilerEvent.TemplateUpdateStart : ProfilerEvent.TemplateCreateStart; + profiler(preHookType, context); templateFn(rf, context); } finally { setSelectedIndex(prevSelectedIndex); + + const postHookType = + isUpdatePhase ? ProfilerEvent.TemplateUpdateEnd : ProfilerEvent.TemplateCreateEnd; + profiler(postHookType, context); } } @@ -752,14 +763,26 @@ export function locateHostElement( * On the first template pass, saves in TView: * - Cleanup function * - Index of context we just saved in LView.cleanupInstances + * + * This function can also be used to store instance specific cleanup fns. In that case the `context` + * is `null` and the function is store in `LView` (rather than it `TView`). */ export function storeCleanupWithContext( tView: TView, lView: LView, context: any, cleanupFn: Function): void { - const lCleanup = getLCleanup(lView); - lCleanup.push(context); + const lCleanup = getOrCreateLViewCleanup(lView); + if (context === null) { + // If context is null that this is instance specific callback. These callbacks can only be + // inserted after template shared instances. For this reason in ngDevMode we freeze the TView. + if (ngDevMode) { + Object.freeze(getOrCreateTViewCleanup(tView)); + } + lCleanup.push(cleanupFn); + } else { + lCleanup.push(context); - if (tView.firstCreatePass) { - getTViewCleanup(tView).push(cleanupFn, lCleanup.length - 1); + if (tView.firstCreatePass) { + getOrCreateTViewCleanup(tView).push(cleanupFn, lCleanup.length - 1); + } } } @@ -1031,7 +1054,8 @@ function setNgReflectProperty( (element as RElement).setAttribute(attrName, debugValue); } } else { - const textContent = `bindings=${JSON.stringify({[attrName]: debugValue}, null, 2)}`; + const textContent = + escapeCommentText(`bindings=${JSON.stringify({[attrName]: debugValue}, null, 2)}`); if (isProceduralRenderer(renderer)) { renderer.setValue((element as RComment), textContent); } else { @@ -1992,12 +2016,12 @@ export function storePropertyBindingMetadata( export const CLEAN_PROMISE = _CLEAN_PROMISE; -export function getLCleanup(view: LView): any[] { +export function getOrCreateLViewCleanup(view: LView): any[] { // top level variables should not be exported for performance reasons (PERF_NOTES.md) return view[CLEANUP] || (view[CLEANUP] = ngDevMode ? new LCleanup() : []); } -function getTViewCleanup(tView: TView): any[] { +export function getOrCreateTViewCleanup(tView: TView): any[] { return tView.cleanup || (tView.cleanup = ngDevMode ? new TCleanup() : []); } diff --git a/packages/core/src/render3/interfaces/context.ts b/packages/core/src/render3/interfaces/context.ts index 41943df640..6f78e26891 100644 --- a/packages/core/src/render3/interfaces/context.ts +++ b/packages/core/src/render3/interfaces/context.ts @@ -10,10 +10,6 @@ import {RNode} from './renderer_dom'; import {LView} from './view'; -/** - * This property will be monkey-patched on elements, components and directives - */ -export const MONKEY_PATCH_KEY_NAME = '__ngContext__'; /** * The internal view context which is specific to a given DOM element, directive or diff --git a/packages/core/src/render3/interfaces/definition.ts b/packages/core/src/render3/interfaces/definition.ts index aae5327804..9f0c2e09b3 100644 --- a/packages/core/src/render3/interfaces/definition.ts +++ b/packages/core/src/render3/interfaces/definition.ts @@ -59,7 +59,7 @@ export const enum RenderFlags { * consumable for rendering. */ export interface ComponentType<T> extends Type<T> { - ɵcmp: never; + ɵcmp: unknown; } /** @@ -67,8 +67,8 @@ export interface ComponentType<T> extends Type<T> { * consumable for rendering. */ export interface DirectiveType<T> extends Type<T> { - ɵdir: never; - ɵfac: () => T; + ɵdir: unknown; + ɵfac: unknown; } /** @@ -76,51 +76,10 @@ export interface DirectiveType<T> extends Type<T> { * consumable for rendering. */ export interface PipeType<T> extends Type<T> { - ɵpipe: never; + ɵpipe: unknown; } -/** - * 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>; /** * Runtime link information for Directives. @@ -247,20 +206,6 @@ export interface DirectiveDef<T> { privateName: string) => void)|null; } -/** - * @codeGenApi - */ -export type ɵɵComponentDefWithMeta< - 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, CtorDependencies extends CtorDependency[]> = () => T; - /** * Runtime link information for Components. * @@ -370,7 +315,7 @@ export interface ComponentDef<T> extends DirectiveDef<T> { * Used to store the result of `noSideEffects` function so that it is not removed by closure * compiler. The property should never be read. */ - readonly _?: never; + readonly _?: unknown; } /** @@ -414,11 +359,6 @@ export interface PipeDef<T> { onDestroy: (() => void)|null; } -/** - * @codeGenApi - */ -export type ɵɵPipeDefWithMeta<T, Name extends string> = PipeDef<T>; - export interface DirectiveDefFeature { <T>(directiveDef: DirectiveDef<T>): void; /** diff --git a/packages/core/src/render3/interfaces/query.ts b/packages/core/src/render3/interfaces/query.ts index f189a93d29..7281967dce 100644 --- a/packages/core/src/render3/interfaces/query.ts +++ b/packages/core/src/render3/interfaces/query.ts @@ -18,9 +18,39 @@ import {TView} from './view'; */ export interface TQueryMetadata { predicate: Type<any>|InjectionToken<unknown>|string[]; - descendants: boolean; read: any; - isStatic: boolean; + flags: QueryFlags; +} + +/** + * A set of flags to be used with Queries. + * + * NOTE: Ensure changes here are reflected in `packages/compiler/src/render3/view/compiler.ts` + */ +export const enum QueryFlags { + /** + * No flags + */ + none = 0b0000, + + /** + * Whether or not the query should descend into children. + */ + descendants = 0b0001, + + /** + * The query can be computed statically and hence can be assigned eagerly. + * + * NOTE: Backwards compatibility with ViewEngine. + */ + isStatic = 0b0010, + + /** + * If the `QueryList` should fire change event only if actual change to query was computed (vs old + * behavior where the change was fired whenever the query was recomputed, even if the recomputed + * query resulted in the same list.) + */ + emitDistinctChangesOnly = 0b0100, } /** diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index 18749018cd..ba16dcf3c9 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -163,8 +163,11 @@ export interface LView extends Array<any> { * * These change per LView instance, so they cannot be stored on TView. Instead, * TView.cleanup saves an index to the necessary context in this array. + * + * After `LView` is created it is possible to attach additional instance specific functions at the + * end of the `lView[CLENUP]` because we know that no more `T` level cleanup functions will be + * addeded here. */ - // TODO: flatten into LView[] [CLEANUP]: any[]|null; /** @@ -1086,6 +1089,11 @@ export interface DebugNode { */ html: string|null; + /** + * Associated `TNode` + */ + tNode: TNode; + /** * Human readable node type. */ @@ -1115,6 +1123,11 @@ export interface DebugNode { * NodeInjector information. */ injector: NodeInjectorDebug; + + /** + * Injector resolution path. + */ + injectorResolutionPath: any; } export interface NodeInjectorDebug { @@ -1144,4 +1157,4 @@ export interface NodeInjectorDebug { * Location of the parent `TNode`. */ parentInjectorIndex: number; -} \ No newline at end of file +} diff --git a/packages/core/src/render3/jit/directive.ts b/packages/core/src/render3/jit/directive.ts index a87d25f8e9..58ea28408e 100644 --- a/packages/core/src/render3/jit/directive.ts +++ b/packages/core/src/render3/jit/directive.ts @@ -15,9 +15,9 @@ import {Query} from '../../metadata/di'; import {Component, Directive, Input} from '../../metadata/directives'; import {componentNeedsResolution, maybeQueueResolutionOfComponentResources} from '../../metadata/resource_loading'; import {ViewEncapsulation} from '../../metadata/view'; +import {EMPTY_ARRAY, EMPTY_OBJ} from '../../util/empty'; import {initNgDevMode} from '../../util/ng_dev_mode'; import {getComponentDef, getDirectiveDef} from '../definition'; -import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty'; import {NG_COMP_DEF, NG_DIR_DEF, NG_FACTORY_DEF} from '../fields'; import {ComponentType} from '../interfaces/definition'; import {stringifyForError} from '../util/stringify_utils'; @@ -211,9 +211,11 @@ function addDirectiveFactoryDef(type: Type<any>, metadata: Directive|Component) const meta = getDirectiveMetadata(type, metadata); const compiler = getCompilerFacade(); ngFactoryDef = compiler.compileFactory(angularCoreEnv, `ng:///${type.name}/ɵfac.js`, { - ...meta.metadata, - injectFn: 'directiveInject', - target: compiler.R3FactoryTarget.Directive + name: meta.metadata.name, + type: meta.metadata.type, + typeArgumentCount: 0, + deps: reflectDependencies(type), + target: compiler.FactoryTarget.Directive }); } return ngFactoryDef; @@ -239,9 +241,7 @@ export function directiveMetadata(type: Type<any>, metadata: Directive): R3Direc return { name: type.name, type: type, - typeArgumentCount: 0, selector: metadata.selector !== undefined ? metadata.selector : null, - deps: reflectDependencies(type), host: metadata.host || EMPTY_OBJ, propMetadata: propMetadata, inputs: metadata.inputs || EMPTY_ARRAY, @@ -286,7 +286,8 @@ export function convertToR3QueryMetadata(propertyName: string, ann: Query): R3Qu descendants: ann.descendants, first: ann.first, read: ann.read ? ann.read : null, - static: !!ann.static + static: !!ann.static, + emitDistinctChangesOnly: !!ann.emitDistinctChangesOnly, }; } function extractQueriesMetadata( diff --git a/packages/core/src/render3/jit/environment.ts b/packages/core/src/render3/jit/environment.ts index be4aae16ad..697c090c15 100644 --- a/packages/core/src/render3/jit/environment.ts +++ b/packages/core/src/render3/jit/environment.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import {forwardRef, resolveForwardRef} from '../../di/forward_ref'; import {ɵɵinject, ɵɵinvalidFactoryDep} from '../../di/injector_compatibility'; import {ɵɵdefineInjectable, ɵɵdefineInjector} from '../../di/interface/defs'; import * as sanitization from '../../sanitization/sanitization'; @@ -36,13 +37,11 @@ export const angularCoreEnv: {[name: string]: Function} = 'ɵɵdefineNgModule': r3.ɵɵdefineNgModule, 'ɵɵdefinePipe': r3.ɵɵdefinePipe, 'ɵɵdirectiveInject': r3.ɵɵdirectiveInject, - 'ɵɵgetFactoryOf': r3.ɵɵgetFactoryOf, 'ɵɵgetInheritedFactory': r3.ɵɵgetInheritedFactory, 'ɵɵinject': ɵɵinject, 'ɵɵinjectAttribute': r3.ɵɵinjectAttribute, 'ɵɵinvalidFactory': r3.ɵɵinvalidFactory, 'ɵɵinvalidFactoryDep': ɵɵinvalidFactoryDep, - 'ɵɵinjectPipeChangeDetectorRef': r3.ɵɵinjectPipeChangeDetectorRef, 'ɵɵtemplateRefExtractor': r3.ɵɵtemplateRefExtractor, 'ɵɵNgOnChangesFeature': r3.ɵɵNgOnChangesFeature, 'ɵɵProvidersFeature': r3.ɵɵProvidersFeature, @@ -97,8 +96,6 @@ export const angularCoreEnv: {[name: string]: Function} = 'ɵɵpipe': r3.ɵɵpipe, 'ɵɵqueryRefresh': r3.ɵɵqueryRefresh, 'ɵɵviewQuery': r3.ɵɵviewQuery, - 'ɵɵstaticViewQuery': r3.ɵɵstaticViewQuery, - 'ɵɵstaticContentQuery': r3.ɵɵstaticContentQuery, 'ɵɵloadQuery': r3.ɵɵloadQuery, 'ɵɵcontentQuery': r3.ɵɵcontentQuery, 'ɵɵreference': r3.ɵɵreference, @@ -167,4 +164,7 @@ export const angularCoreEnv: {[name: string]: Function} = 'ɵɵsanitizeUrlOrResourceUrl': sanitization.ɵɵsanitizeUrlOrResourceUrl, 'ɵɵtrustConstantHtml': sanitization.ɵɵtrustConstantHtml, 'ɵɵtrustConstantResourceUrl': sanitization.ɵɵtrustConstantResourceUrl, + + 'forwardRef': forwardRef, + 'resolveForwardRef': resolveForwardRef, }))(); diff --git a/packages/core/src/render3/jit/module.ts b/packages/core/src/render3/jit/module.ts index 7450c5dc7f..50fd3d7536 100644 --- a/packages/core/src/render3/jit/module.ts +++ b/packages/core/src/render3/jit/module.ts @@ -16,16 +16,15 @@ import {ModuleWithProviders, NgModule} from '../../metadata/ng_module'; import {NgModuleDef, NgModuleTransitiveScopes, NgModuleType} from '../../metadata/ng_module_def'; import {deepForEach, flatten} from '../../util/array_utils'; import {assertDefined} from '../../util/assert'; +import {EMPTY_ARRAY} from '../../util/empty'; import {getComponentDef, getDirectiveDef, getNgModuleDef, getPipeDef} from '../definition'; -import {NG_COMP_DEF, NG_DIR_DEF, NG_MOD_DEF, NG_PIPE_DEF} from '../fields'; +import {NG_COMP_DEF, NG_DIR_DEF, NG_FACTORY_DEF, NG_MOD_DEF, NG_PIPE_DEF} from '../fields'; import {ComponentDef} from '../interfaces/definition'; import {maybeUnwrapFn} from '../util/misc_utils'; import {stringifyForError} from '../util/stringify_utils'; import {angularCoreEnv} from './environment'; -const EMPTY_ARRAY: Type<any>[] = []; - interface ModuleQueueItem { moduleType: Type<any>; ngModule: NgModule; @@ -94,7 +93,7 @@ export function compileNgModule(moduleType: Type<any>, ngModule: NgModule = {}): } /** - * Compiles and adds the `ɵmod` and `ɵinj` properties to the module class. + * Compiles and adds the `ɵmod`, `ɵfac` and `ɵinj` properties to the module class. * * It's possible to compile a module via this API which will allow duplicate declarations in its * root. @@ -141,6 +140,25 @@ export function compileNgModuleDefs( } }); + let ngFactoryDef: any = null; + Object.defineProperty(moduleType, NG_FACTORY_DEF, { + get: () => { + if (ngFactoryDef === null) { + const compiler = getCompilerFacade(); + ngFactoryDef = compiler.compileFactory(angularCoreEnv, `ng:///${moduleType.name}/ɵfac.js`, { + name: moduleType.name, + type: moduleType, + deps: reflectDependencies(moduleType), + target: compiler.FactoryTarget.NgModule, + typeArgumentCount: 0, + }); + } + return ngFactoryDef; + }, + // Make the property configurable in dev mode to allow overriding in tests + configurable: !!ngDevMode, + }); + let ngInjectorDef: any = null; Object.defineProperty(moduleType, NG_INJ_DEF, { get: () => { @@ -151,7 +169,6 @@ export function compileNgModuleDefs( const meta: R3InjectorMetadataFacade = { name: moduleType.name, type: moduleType, - deps: reflectDependencies(moduleType), providers: ngModule.providers || EMPTY_ARRAY, imports: [ (ngModule.imports || EMPTY_ARRAY).map(resolveForwardRef), diff --git a/packages/core/src/render3/jit/partial.ts b/packages/core/src/render3/jit/partial.ts index 402fcd499b..b84d308d5e 100644 --- a/packages/core/src/render3/jit/partial.ts +++ b/packages/core/src/render3/jit/partial.ts @@ -6,13 +6,18 @@ * found in the LICENSE file at https://angular.io/license */ +import {getCompilerFacade, R3DeclareComponentFacade, R3DeclareDirectiveFacade, R3DeclareFactoryFacade, R3DeclareInjectorFacade, R3DeclareNgModuleFacade, R3DeclarePipeFacade} from '../../compiler/compiler_facade'; +import {angularCoreEnv} from './environment'; + /** * Compiles a partial directive declaration object into a full directive definition object. * * @codeGenApi */ -export function ɵɵngDeclareDirective(decl: unknown): unknown { - throw new Error('Not yet implemented'); +export function ɵɵngDeclareDirective(decl: R3DeclareDirectiveFacade): unknown { + const compiler = getCompilerFacade(); + return compiler.compileDirectiveDeclaration( + angularCoreEnv, `ng:///${decl.type.name}/ɵfac.js`, decl); } /** @@ -20,6 +25,56 @@ export function ɵɵngDeclareDirective(decl: unknown): unknown { * * @codeGenApi */ -export function ɵɵngDeclareComponent(decl: unknown): unknown { - throw new Error('Not yet implemented'); +export function ɵɵngDeclareComponent(decl: R3DeclareComponentFacade): unknown { + const compiler = getCompilerFacade(); + return compiler.compileComponentDeclaration( + angularCoreEnv, `ng:///${decl.type.name}/ɵcmp.js`, decl); +} + +/** + * Compiles a partial pipe declaration object into a full pipe definition object. + * + * @codeGenApi + */ +export function ɵɵngDeclareFactory(decl: R3DeclareFactoryFacade): unknown { + const compiler = getCompilerFacade(); + return compiler.compileFactoryDeclaration( + angularCoreEnv, `ng:///${decl.type.name}/ɵfac.js`, decl); +} + +/** + * These enums are used in the partial factory declaration calls. + */ +export {FactoryTarget} from '../../compiler/compiler_facade'; + +/** + * Compiles a partial injector declaration object into a full injector definition object. + * + * @codeGenApi + */ +export function ɵɵngDeclareInjector(decl: R3DeclareInjectorFacade): unknown { + const compiler = getCompilerFacade(); + return compiler.compileInjectorDeclaration( + angularCoreEnv, `ng:///${decl.type.name}/ɵinj.js`, decl); +} + +/** + * Compiles a partial NgModule declaration object into a full NgModule definition object. + * + * @codeGenApi + */ +export function ɵɵngDeclareNgModule(decl: R3DeclareNgModuleFacade): unknown { + const compiler = getCompilerFacade(); + return compiler.compileNgModuleDeclaration( + angularCoreEnv, `ng:///${decl.type.name}/ɵmod.js`, decl); +} + +/** + * Compiles a partial pipe declaration object into a full pipe definition object. + * + * @codeGenApi + */ +export function ɵɵngDeclarePipe(decl: R3DeclarePipeFacade): unknown { + const compiler = getCompilerFacade(); + return compiler.compilePipeDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵpipe.js`, decl); } diff --git a/packages/core/src/render3/jit/pipe.ts b/packages/core/src/render3/jit/pipe.ts index e5f14732b1..9377f44fdf 100644 --- a/packages/core/src/render3/jit/pipe.ts +++ b/packages/core/src/render3/jit/pipe.ts @@ -23,9 +23,13 @@ export function compilePipe(type: Type<any>, meta: Pipe): void { if (ngFactoryDef === null) { const metadata = getPipeMetadata(type, meta); const compiler = getCompilerFacade(); - ngFactoryDef = compiler.compileFactory( - angularCoreEnv, `ng:///${metadata.name}/ɵfac.js`, - {...metadata, injectFn: 'directiveInject', target: compiler.R3FactoryTarget.Pipe}); + ngFactoryDef = compiler.compileFactory(angularCoreEnv, `ng:///${metadata.name}/ɵfac.js`, { + name: metadata.name, + type: metadata.type, + typeArgumentCount: 0, + deps: reflectDependencies(type), + target: compiler.FactoryTarget.Pipe + }); } return ngFactoryDef; }, @@ -50,9 +54,7 @@ export function compilePipe(type: Type<any>, meta: Pipe): void { function getPipeMetadata(type: Type<any>, meta: Pipe): R3PipeMetadataFacade { return { type: type, - typeArgumentCount: 0, name: type.name, - deps: reflectDependencies(type), pipeName: meta.name, pure: meta.pure !== undefined ? meta.pure : true }; diff --git a/packages/core/src/render3/metadata.ts b/packages/core/src/render3/metadata.ts index 317e265c0f..e12a40e125 100644 --- a/packages/core/src/render3/metadata.ts +++ b/packages/core/src/render3/metadata.ts @@ -21,8 +21,8 @@ interface TypeWithMetadata extends Type<any> { * * These metadata fields can later be read with Angular's `ReflectionCapabilities` API. * - * Calls to `setClassMetadata` can be marked as pure, resulting in the metadata assignments being - * tree-shaken away during production builds. + * Calls to `setClassMetadata` can be guarded by ngDevMode, resulting in the metadata assignments + * being tree-shaken away during production builds. */ export function setClassMetadata( type: Type<any>, decorators: any[]|null, ctorParameters: (() => any[])|null, diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index c8f572b280..801957fe21 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -10,8 +10,10 @@ import {ViewEncapsulation} from '../metadata/view'; import {Renderer2} from '../render/api'; import {RendererStyleFlags2} from '../render/api_flags'; import {addToArray, removeFromArray} from '../util/array_utils'; -import {assertDefined, assertDomNode, assertEqual, assertString} from '../util/assert'; -import {assertLContainer, assertLView, assertTNodeForLView} from './assert'; +import {assertDefined, assertDomNode, assertEqual, assertFunction, assertString} from '../util/assert'; +import {escapeCommentText} from '../util/dom'; + +import {assertLContainer, assertLView, assertParentView, assertProjectionSlots, assertTNodeForLView} from './assert'; import {attachPatchData} from './context_discovery'; import {icuContainerIterate} from './i18n/i18n_tree_shaking'; import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS, NATIVE, unusedValueExportToPlacateAjd as unused1} from './interfaces/container'; @@ -113,7 +115,7 @@ export function createCommentNode(renderer: Renderer3, value: string): RComment ngDevMode && ngDevMode.rendererCreateComment++; // isProceduralRenderer check is not needed because both `Renderer2` and `Renderer3` have the same // method name. - return renderer.createComment(value); + return renderer.createComment(escapeCommentText(value)); } /** @@ -418,7 +420,7 @@ function cleanUpView(tView: TView, lView: LView): void { lView[FLAGS] |= LViewFlags.Destroyed; executeOnDestroys(tView, lView); - removeListeners(tView, lView); + processCleanups(tView, lView); // For component views only, the local renderer is destroyed at clean up time. if (lView[TVIEW].type === TViewType.Component && isProceduralRenderer(lView[RENDERER])) { ngDevMode && ngDevMode.rendererDestroy++; @@ -443,10 +445,14 @@ function cleanUpView(tView: TView, lView: LView): void { } /** Removes listeners and unsubscribes from output subscriptions */ -function removeListeners(tView: TView, lView: LView): void { +function processCleanups(tView: TView, lView: LView): void { const tCleanup = tView.cleanup; + const lCleanup = lView[CLEANUP]!; + // `LCleanup` contains both share information with `TCleanup` as well as instance specific + // information appended at the end. We need to know where the end of the `TCleanup` information + // is, and we track this with `lastLCleanupIndex`. + let lastLCleanupIndex = -1; if (tCleanup !== null) { - 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 @@ -454,7 +460,7 @@ function removeListeners(tView: TView, lView: LView): void { const target = typeof idxOrTargetGetter === 'function' ? idxOrTargetGetter(lView) : unwrapRNode(lView[idxOrTargetGetter]); - const listener = lCleanup[tCleanup[i + 2]]; + const listener = lCleanup[lastLCleanupIndex = tCleanup[i + 2]]; const useCaptureOrSubIdx = tCleanup[i + 3]; if (typeof useCaptureOrSubIdx === 'boolean') { // native DOM listener registered with Renderer3 @@ -462,19 +468,26 @@ function removeListeners(tView: TView, lView: LView): void { } else { if (useCaptureOrSubIdx >= 0) { // unregister - lCleanup[useCaptureOrSubIdx](); + lCleanup[lastLCleanupIndex = useCaptureOrSubIdx](); } else { // Subscription - lCleanup[-useCaptureOrSubIdx].unsubscribe(); + lCleanup[lastLCleanupIndex = -useCaptureOrSubIdx].unsubscribe(); } } i += 2; } else { // This is a cleanup function that is grouped with the index of its context - const context = lCleanup[tCleanup[i + 1]]; + const context = lCleanup[lastLCleanupIndex = tCleanup[i + 1]]; tCleanup[i].call(context); } } + } + if (lCleanup !== null) { + for (let i = lastLCleanupIndex + 1; i < lCleanup.length; i++) { + const instanceCleanupFn = lCleanup[i]; + ngDevMode && assertFunction(instanceCleanupFn, 'Expecting instance cleanup function.'); + instanceCleanupFn(); + } lView[CLEANUP] = null; } } @@ -760,14 +773,14 @@ function getFirstNativeNode(lView: LView, tNode: TNode|null): RNode|null { // If the ICU container has no nodes, than we use the ICU anchor as the node. return rNode || unwrapRNode(lView[tNode.index]); } else { - const componentView = lView[DECLARATION_COMPONENT_VIEW]; - const componentHost = componentView[T_HOST] as TElementNode; - const parentView = getLViewParent(componentView); - const firstProjectedTNode: TNode|null = - (componentHost.projection as (TNode | null)[])[tNode.projection as number]; - - if (firstProjectedTNode != null) { - return getFirstNativeNode(parentView!, firstProjectedTNode); + const projectionNodes = getProjectionNodes(lView, tNode); + if (projectionNodes !== null) { + if (Array.isArray(projectionNodes)) { + return projectionNodes[0]; + } + const parentView = getLViewParent(lView[DECLARATION_COMPONENT_VIEW]); + ngDevMode && assertParentView(parentView); + return getFirstNativeNode(parentView!, projectionNodes); } else { return getFirstNativeNode(lView, tNode.next); } @@ -777,6 +790,17 @@ function getFirstNativeNode(lView: LView, tNode: TNode|null): RNode|null { return null; } +export function getProjectionNodes(lView: LView, tNode: TNode|null): TNode|RNode[]|null { + if (tNode !== null) { + const componentView = lView[DECLARATION_COMPONENT_VIEW]; + const componentHost = componentView[T_HOST] as TElementNode; + const slotIdx = tNode.projection as number; + ngDevMode && assertProjectionSlots(lView); + return componentHost.projection![slotIdx]; + } + return null; +} + export function getBeforeNodeForView(viewIndexInContainer: number, lContainer: LContainer): RNode| null { const nextViewIndex = CONTAINER_HEADER_OFFSET + viewIndexInContainer + 1; diff --git a/packages/core/src/render3/query.ts b/packages/core/src/render3/query.ts index 89fa943ec3..3cd7b47b2e 100644 --- a/packages/core/src/render3/query.ts +++ b/packages/core/src/render3/query.ts @@ -11,11 +11,11 @@ import {InjectionToken} from '../di/injection_token'; import {Type} from '../interface/type'; -import {createElementRef, ElementRef as ViewEngine_ElementRef} from '../linker/element_ref'; +import {createElementRef, ElementRef as ViewEngine_ElementRef, unwrapElementRef} from '../linker/element_ref'; import {QueryList} from '../linker/query_list'; import {createTemplateRef, TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref'; import {createContainerRef, ViewContainerRef} from '../linker/view_container_ref'; -import {assertDefined, assertIndexInRange, throwError} from '../util/assert'; +import {assertDefined, assertIndexInRange, assertNumber, throwError} from '../util/assert'; import {stringify} from '../util/stringify'; import {assertFirstCreatePass, assertLContainer} from './assert'; import {getNodeInjectable, locateDirectiveOrProvider} from './di'; @@ -24,7 +24,7 @@ import {CONTAINER_HEADER_OFFSET, LContainer, MOVED_VIEWS} from './interfaces/con import {unusedValueExportToPlacateAjd as unused1} from './interfaces/definition'; import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector'; import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node'; -import {LQueries, LQuery, TQueries, TQuery, TQueryMetadata, unusedValueExportToPlacateAjd as unused4} from './interfaces/query'; +import {LQueries, LQuery, QueryFlags, TQueries, TQuery, TQueryMetadata, unusedValueExportToPlacateAjd as unused4} from './interfaces/query'; import {DECLARATION_LCONTAINER, LView, PARENT, QUERIES, TVIEW, TView} from './interfaces/view'; import {assertTNodeType} from './node_assert'; import {getCurrentQueryIndex, getCurrentTNode, getLView, getTView, setCurrentQueryIndex} from './state'; @@ -88,8 +88,8 @@ class LQueries_ implements LQueries { class TQueryMetadata_ implements TQueryMetadata { constructor( - public predicate: Type<any>|InjectionToken<unknown>|string[], public descendants: boolean, - public isStatic: boolean, public read: any = null) {} + public predicate: Type<any>|InjectionToken<unknown>|string[], public flags: QueryFlags, + public read: any = null) {} } class TQueries_ implements TQueries { @@ -202,7 +202,8 @@ class TQuery_ implements TQuery { } private isApplyingToNode(tNode: TNode): boolean { - if (this._appliesToNextNode && this.metadata.descendants === false) { + if (this._appliesToNextNode && + (this.metadata.flags & QueryFlags.descendants) !== QueryFlags.descendants) { const declarationNodeIdx = this._declarationNodeIndex; let parent = tNode.parent; // Determine if a given TNode is a "direct" child of a node on which a content query was @@ -427,14 +428,16 @@ export function ɵɵqueryRefresh(queryList: QueryList<any>): boolean { setCurrentQueryIndex(queryIndex + 1); const tQuery = getTQuery(tView, queryIndex); - if (queryList.dirty && (isCreationMode(lView) === tQuery.metadata.isStatic)) { + if (queryList.dirty && + (isCreationMode(lView) === + ((tQuery.metadata.flags & QueryFlags.isStatic) === QueryFlags.isStatic))) { if (tQuery.matches === null) { queryList.reset([]); } else { const result = tQuery.crossesNgTemplate ? collectQueryResults(tView, lView, queryIndex, []) : materializeViewResults(tView, lView, tQuery, queryIndex); - queryList.reset(result); + queryList.reset(result, unwrapElementRef); queryList.notifyOnChanges(); } return true; @@ -443,44 +446,26 @@ export function ɵɵqueryRefresh(queryList: QueryList<any>): boolean { return false; } -/** - * Creates new QueryList for a static view query. - * - * @param predicate The type for which the query will search - * @param descend Whether or not to descend into children - * @param read What to save in the query - * - * @codeGenApi - */ -export function ɵɵstaticViewQuery<T>( - predicate: Type<any>|InjectionToken<unknown>|string[], descend: boolean, read?: any): void { - viewQueryInternal(getTView(), getLView(), predicate, descend, read, true); -} - /** * Creates new QueryList, stores the reference in LView and returns QueryList. * * @param predicate The type for which the query will search - * @param descend Whether or not to descend into children + * @param flags Flags associated with the query * @param read What to save in the query * * @codeGenApi */ export function ɵɵviewQuery<T>( - predicate: Type<any>|InjectionToken<unknown>|string[], descend: boolean, read?: any): void { - viewQueryInternal(getTView(), getLView(), predicate, descend, read, false); -} - -function viewQueryInternal<T>( - tView: TView, lView: LView, predicate: Type<any>|InjectionToken<unknown>|string[], - descend: boolean, read: any, isStatic: boolean): void { + predicate: Type<any>|InjectionToken<unknown>|string[], flags: QueryFlags, read?: any): void { + ngDevMode && assertNumber(flags, 'Expecting flags'); + const tView = getTView(); if (tView.firstCreatePass) { - createTQuery(tView, new TQueryMetadata_(predicate, descend, isStatic, read), -1); - if (isStatic) { + createTQuery(tView, new TQueryMetadata_(predicate, flags, read), -1); + if ((flags & QueryFlags.isStatic) === QueryFlags.isStatic) { tView.staticViewQueries = true; } } - createLQuery<T>(tView, lView); + createLQuery<T>(tView, getLView(), flags); } /** @@ -489,50 +474,27 @@ function viewQueryInternal<T>( * * @param directiveIndex Current directive index * @param predicate The type for which the query will search - * @param descend Whether or not to descend into children + * @param flags Flags associated with the query * @param read What to save in the query * @returns QueryList<T> * * @codeGenApi */ export function ɵɵcontentQuery<T>( - directiveIndex: number, predicate: Type<any>|InjectionToken<unknown>|string[], descend: boolean, - read?: any): void { - contentQueryInternal( - getTView(), getLView(), predicate, descend, read, false, getCurrentTNode()!, directiveIndex); -} - -/** - * Registers a QueryList, associated with a static content query, for later refresh - * (part of a view refresh). - * - * @param directiveIndex Current directive index - * @param predicate The type for which the query will search - * @param descend Whether or not to descend into children - * @param read What to save in the query - * @returns QueryList<T> - * - * @codeGenApi - */ -export function ɵɵstaticContentQuery<T>( - directiveIndex: number, predicate: Type<any>|InjectionToken<unknown>|string[], descend: boolean, - read?: any): void { - contentQueryInternal( - getTView(), getLView(), predicate, descend, read, true, getCurrentTNode()!, directiveIndex); -} - -function contentQueryInternal<T>( - tView: TView, lView: LView, predicate: Type<any>|InjectionToken<unknown>|string[], - descend: boolean, read: any, isStatic: boolean, tNode: TNode, directiveIndex: number): void { + directiveIndex: number, predicate: Type<any>|InjectionToken<unknown>|string[], + flags: QueryFlags, read?: any): void { + ngDevMode && assertNumber(flags, 'Expecting flags'); + const tView = getTView(); if (tView.firstCreatePass) { - createTQuery(tView, new TQueryMetadata_(predicate, descend, isStatic, read), tNode.index); + const tNode = getCurrentTNode()!; + createTQuery(tView, new TQueryMetadata_(predicate, flags, read), tNode.index); saveContentQueryAndDirectiveIndex(tView, directiveIndex); - if (isStatic) { + if ((flags & QueryFlags.isStatic) === QueryFlags.isStatic) { tView.staticContentQueries = true; } } - createLQuery<T>(tView, lView); + createLQuery<T>(tView, getLView(), flags); } /** @@ -551,8 +513,9 @@ function loadQueryInternal<T>(lView: LView, queryIndex: number): QueryList<T> { return lView[QUERIES]!.queries[queryIndex].queryList; } -function createLQuery<T>(tView: TView, lView: LView) { - const queryList = new QueryList<T>(); +function createLQuery<T>(tView: TView, lView: LView, flags: QueryFlags) { + const queryList = new QueryList<T>( + (flags & QueryFlags.emitDistinctChangesOnly) === QueryFlags.emitDistinctChangesOnly); storeCleanupWithContext(tView, lView, queryList, queryList.destroy); if (lView[QUERIES] === null) lView[QUERIES] = new LQueries_(); diff --git a/packages/core/src/render3/state.ts b/packages/core/src/render3/state.ts index 0217e2f60d..5d1e02f106 100644 --- a/packages/core/src/render3/state.ts +++ b/packages/core/src/render3/state.ts @@ -284,11 +284,13 @@ export function getTView(): TView { * walking the declaration view tree in listeners to get vars from parent views. * * @param viewToRestore The OpaqueViewState instance to restore. + * @returns Context of the restored OpaqueViewState instance. * * @codeGenApi */ -export function ɵɵrestoreView(viewToRestore: OpaqueViewState) { +export function ɵɵrestoreView<T = any>(viewToRestore: OpaqueViewState): T { instructionState.lFrame.contextLView = viewToRestore as any as LView; + return (viewToRestore as any as LView)[CONTEXT] as T; } diff --git a/packages/core/src/render3/util/change_detection_utils.ts b/packages/core/src/render3/util/change_detection_utils.ts index c4b96263b5..3a94e05817 100644 --- a/packages/core/src/render3/util/change_detection_utils.ts +++ b/packages/core/src/render3/util/change_detection_utils.ts @@ -13,12 +13,8 @@ import {getRootComponents} from './discovery_utils'; * Marks a component for check (in case of OnPush components) and synchronously * performs change detection on the application this component belongs to. * - * 把一个组件标记为需要检查(OnPush 组件中),并且在本组件所属的应用中同步执行执行变更检测。 - * * @param component Component to {@link ChangeDetectorRef#markForCheck mark for check}. * - * 要 {@link ChangeDetectorRef#markForCheck 标记为需要检查}的组件。 - * * @publicApi * @globalApi ng */ diff --git a/packages/core/src/render3/util/discovery_utils.ts b/packages/core/src/render3/util/discovery_utils.ts index 9fa8b887f2..1966517cd4 100644 --- a/packages/core/src/render3/util/discovery_utils.ts +++ b/packages/core/src/render3/util/discovery_utils.ts @@ -26,14 +26,8 @@ import {getTNode, unwrapRNode} from './view_utils'; /** * Retrieves the component instance associated with a given DOM element. * - * 检索与给定 DOM 元素关联的组件实例。 - * * @usageNotes - * * Given the following DOM structure: - * - * 给定以下 DOM 结构: - * * ```html * <my-app> * <div> @@ -41,25 +35,16 @@ import {getTNode, unwrapRNode} from './view_utils'; * </div> * </my-app> * ``` - * * Calling `getComponent` on `<child-comp>` will return the instance of `ChildComponent` * associated with this DOM element. * - * 在 `<child-comp>` 上调用 `getComponent` 将返回与此 DOM 元素关联的 `ChildComponent`。 - * * Calling the function on `<my-app>` will return the `MyApp` instance. * - * 在 `<my-app>` 上调用该函数将返回 `MyApp` 实例。 * * @param element DOM element from which the component should be retrieved. - * - * 要从中检索组件的 DOM 元素。 - * * @returns Component instance associated with the element or `null` if there * is no component associated with it. * - * 与元素关联的组件实例;如果没有与之关联的组件,则为 `null` - * * @publicApi * @globalApi ng */ @@ -81,17 +66,10 @@ export function getComponent<T>(element: Element): T|null { * view that the element is part of. Otherwise retrieves the instance of the component whose view * owns the element (in this case, the result is the same as calling `getOwningComponent`). * - * 如果在嵌入式视图中(例如 `*ngIf` 或 `*ngFor`),则检索元素所属的嵌入式视图的上下文。否则,检索其视图中拥有该元素的组件的实例(在这种情况下,其结果与调用 `getOwningComponent` 相同)。 - * * @param element Element for which to get the surrounding component instance. - * - * 要获取外围组件实例的元素。 - * * @returns Instance of the component that is around the element or null if the element isn't * inside any component. * - * 元素外围组件的实例;如果元素不在任何组件内,则为 null。 - * * @publicApi * @globalApi ng */ @@ -104,24 +82,15 @@ export function getContext<T>(element: Element): T|null { /** * Retrieves the component instance whose view contains the DOM element. * - * 检索其视图中包含此 DOM 元素的组件实例。 - * * For example, if `<child-comp>` is used in the template of `<app-comp>` * (i.e. a `ViewChild` of `<app-comp>`), calling `getOwningComponent` on `<child-comp>` * would return `<app-comp>`. * - * 例如,如果 `<child-comp>` 在 `<app-comp>` 的模板中使用(即 `<app-comp>` 的 `ViewChild`),在 `<child-comp>` 上调用 `getOwningComponent` 将返回 `<app-comp>`。 - * * @param elementOrDir DOM element, component or directive instance * for which to retrieve the root components. - * - * 要为其检索根组件的 DOM 元素、组件或指令实例。 - * * @returns Component instance whose view owns the DOM element or null if the element is not * part of a component view. * - * 其视图中拥有 DOM 元素的组件实例;如果该元素不属于组件视图,则为 null。 - * * @publicApi * @globalApi ng */ @@ -142,17 +111,10 @@ export function getOwningComponent<T>(elementOrDir: Element|{}): T|null { * Retrieves all root components associated with a DOM element, directive or component instance. * Root components are those which have been bootstrapped by Angular. * - * 检索与 DOM 元素,指令或组件实例关联的所有根组件。根组件是由 Angular 引导启动的组件。 - * * @param elementOrDir DOM element, component or directive instance * for which to retrieve the root components. - * - * 要检索其根组件的 DOM 元素、组件或指令实例。 - * * @returns Root components associated with the target object. * - * 与目标对象关联的根组件。 - * * @publicApi * @globalApi ng */ @@ -163,17 +125,10 @@ export function getRootComponents(elementOrDir: Element|{}): {}[] { /** * Retrieves an `Injector` associated with an element, component or directive instance. * - * 检索与元素、组件或指令实例关联的 `Injector`。 - * * @param elementOrDir DOM element, component or directive instance for which to * retrieve the injector. - * - * 要为其获取注入器的 DOM 元素、组件或指令实例。 - * * @returns Injector associated with the element, component or directive instance. * - * 与元素、组件或指令实例关联的注入器。 - * * @publicApi * @globalApi ng */ @@ -217,38 +172,22 @@ export function getInjectionTokens(element: Element): any[] { * Retrieves directive instances associated with a given DOM element. Does not include * component instances. * - * 检索与给定 DOM 元素关联的指令实例。不包括组件实例。 - * * @usageNotes - * * Given the following DOM structure: - * - * 给定以下 DOM 结构: - * * ``` * <my-app> * <button my-button></button> * <my-comp></my-comp> * </my-app> * ``` - * * Calling `getDirectives` on `<button>` will return an array with an instance of the `MyButton` * directive that is associated with the DOM element. * - * 在 `<button>` 上调用 `getDirectives` 将返回一个数组,该数组带有与 DOM 元素关联 `MyButton` - * * Calling `getDirectives` on `<my-comp>` will return an empty array. * - * 在 `<my-comp>` 上调用 `getDirectives` 将返回一个空数组。 - * * @param element DOM element for which to get the directives. - * - * 要为其获取指令的 DOM 元素。 - * * @returns Array of directives associated with the element. * - * 与元素关联的指令数组。 - * * @publicApi * @globalApi ng */ @@ -303,22 +242,15 @@ export function getLocalRefs(target: {}): {[key: string]: any} { * Retrieves the host element of a component or directive instance. * The host element is the DOM element that matched the selector of the directive. * - * 检索组件或指令实例的宿主元素。 宿主元素是与指令的选择器匹配的 DOM 元素。 - * * @param componentOrDirective Component or directive instance for which the host * element should be retrieved. - * - * 要为其检索宿主元素的组件或指令实例。 - * * @returns Host element of the target. * - * 目标的宿主元素。 - * * @publicApi * @globalApi ng */ export function getHostElement(componentOrDirective: {}): Element { - return getLContext(componentOrDirective)!.native as never as Element; + return getLContext(componentOrDirective)!.native as unknown as Element; } /** @@ -343,45 +275,19 @@ export function loadLContextFromNode(node: Node): LContext { /** * Event listener configuration returned from `getListeners`. - * - * `getListeners` 返回的事件监听器配置。 - * * @publicApi */ export interface Listener { - /** - * Name of the event listener. - * - * 事件监听器的名称。 - * - */ + /** Name of the event listener. */ name: string; - /** - * Element that the listener is bound to. - * - * 监听器绑定到的元素。 - * - */ + /** Element that the listener is bound to. */ element: Element; - /** - * Callback that is invoked when the event is triggered. - * - * 触发事件时调用的回调。 - * - */ + /** Callback that is invoked when the event is triggered. */ callback: (value: any) => any; - /** - * Whether the listener is using event capturing. - * - * 监听器是否正在使用事件捕获。 - * - */ + /** Whether the listener is using event capturing. */ useCapture: boolean; /** * Type of the listener (e.g. a native DOM event or a custom @Output). - * - * 监听器的类型(例如,原生 DOM 事件或自定义 @Output)。 - * */ type: 'dom'|'output'; } @@ -392,14 +298,8 @@ export interface Listener { * listeners, but it does not include event listeners defined outside of the Angular context * (e.g. through `addEventListener`). * - * 检索与 DOM 元素关联的事件监听器的列表。该列表包含宿主监听器,但不包含在 Angular 上下文之外定义的事件监听器(例如,通过 `addEventListener` )。 - * * @usageNotes - * * Given the following DOM structure: - * - * 给定以下 DOM 结构: - * * ``` * <my-app> * <div (click)="doSomething()"></div> @@ -407,9 +307,6 @@ export interface Listener { * * ``` * Calling `getListeners` on `<div>` will return an object that looks as follows: - * - * 在 `<div>` 上调用 `getListeners` 将返回一个如下所示的对象: - * * ``` * { * name: 'click', @@ -420,12 +317,8 @@ export interface Listener { * ``` * * @param element Element for which the DOM listeners should be retrieved. - * - * 要为其检索 DOM 监听器的元素。 - * * @returns Array of event listeners on the DOM element. * - * DOM 元素上的事件监听器数组。 * @publicApi * @globalApi ng */ diff --git a/packages/core/src/render3/util/global_utils.ts b/packages/core/src/render3/util/global_utils.ts index 489eb506ab..9e488d3f1a 100644 --- a/packages/core/src/render3/util/global_utils.ts +++ b/packages/core/src/render3/util/global_utils.ts @@ -7,6 +7,7 @@ */ import {assertDefined} from '../../util/assert'; import {global} from '../../util/global'; +import {setProfiler} from '../profiler'; import {applyChanges} from './change_detection_utils'; import {getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getOwningComponent, getRootComponents} from './discovery_utils'; @@ -40,6 +41,13 @@ let _published = false; export function publishDefaultGlobalUtils() { if (!_published) { _published = true; + + /** + * Warning: this function is *INTERNAL* and should not be relied upon in application's code. + * The contract of the function might be changed in any release and/or the function can be + * removed completely. + */ + publishGlobalUtil('ɵsetProfiler', setProfiler); publishGlobalUtil('getComponent', getComponent); publishGlobalUtil('getContext', getContext); publishGlobalUtil('getListeners', getListeners); diff --git a/packages/core/src/render3/util/stringify_utils.ts b/packages/core/src/render3/util/stringify_utils.ts index c905b122d4..872457fe9f 100644 --- a/packages/core/src/render3/util/stringify_utils.ts +++ b/packages/core/src/render3/util/stringify_utils.ts @@ -10,11 +10,14 @@ * Used for stringify render output in Ivy. * Important! This function is very performance-sensitive and we should * be extra careful not to introduce megamorphic reads in it. + * Check `core/test/render3/perf/render_stringify` for benchmarks and alternate implementations. */ export function renderStringify(value: any): string { if (typeof value === 'string') return value; if (value == null) return ''; - return '' + value; + // Use `String` so that it invokes the `toString` method of the value. Note that this + // appears to be faster than calling `value.toString` (see `render_stringify` benchmark). + return String(value); } diff --git a/packages/core/src/render3/util/view_traversal_utils.ts b/packages/core/src/render3/util/view_traversal_utils.ts index 0dae10ab63..64bf2ba85d 100644 --- a/packages/core/src/render3/util/view_traversal_utils.ts +++ b/packages/core/src/render3/util/view_traversal_utils.ts @@ -8,12 +8,11 @@ import {assertDefined} from '../../util/assert'; import {assertLView} from '../assert'; +import {readPatchedLView} from '../context_discovery'; import {LContainer} from '../interfaces/container'; import {isLContainer, isLView} from '../interfaces/type_checks'; import {CHILD_HEAD, CONTEXT, FLAGS, LView, LViewFlags, NEXT, PARENT, RootContext} from '../interfaces/view'; -import {readPatchedLView} from './view_utils'; - /** * Gets the parent LView of the passed LView, if the PARENT is an LContainer, will get the parent of diff --git a/packages/core/src/render3/util/view_utils.ts b/packages/core/src/render3/util/view_utils.ts index 8683609874..0487d4d256 100644 --- a/packages/core/src/render3/util/view_utils.ts +++ b/packages/core/src/render3/util/view_utils.ts @@ -6,10 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {assertDefined, assertDomNode, assertGreaterThan, assertGreaterThanOrEqual, assertIndexInRange, assertLessThan} from '../../util/assert'; +import {assertDomNode, assertGreaterThan, assertGreaterThanOrEqual, assertIndexInRange, assertLessThan} from '../../util/assert'; import {assertTNode, assertTNodeForLView} from '../assert'; import {LContainer, TYPE} from '../interfaces/container'; -import {LContext, MONKEY_PATCH_KEY_NAME} from '../interfaces/context'; import {TConstants, TNode} from '../interfaces/node'; import {isProceduralRenderer} from '../interfaces/renderer'; import {RNode} from '../interfaces/renderer_dom'; @@ -143,24 +142,6 @@ export function getComponentLViewByIndex(nodeIndex: number, hostView: LView): LV return lView; } - -/** - * Returns the monkey-patch value data present on the target (which could be - * a component, directive or a DOM node). - */ -export function readPatchedData(target: any): LView|LContext|null { - ngDevMode && assertDefined(target, 'Target expected'); - return target[MONKEY_PATCH_KEY_NAME] || null; -} - -export function readPatchedLView(target: any): LView|null { - const value = readPatchedData(target); - if (value) { - return Array.isArray(value) ? value : (value as LContext).lView; - } - return null; -} - /** Checks whether a given view is in creation mode */ export function isCreationMode(view: LView): boolean { return (view[FLAGS] & LViewFlags.CreationMode) === LViewFlags.CreationMode; diff --git a/packages/core/src/render3/view_engine_compatibility_prebound.ts b/packages/core/src/render3/view_engine_compatibility_prebound.ts index 0298259f0c..9e9f5bf657 100644 --- a/packages/core/src/render3/view_engine_compatibility_prebound.ts +++ b/packages/core/src/render3/view_engine_compatibility_prebound.ts @@ -24,18 +24,3 @@ import {LView} from './interfaces/view'; export function ɵɵtemplateRefExtractor(tNode: TNode, lView: LView): TemplateRef<any>|null { return createTemplateRef(tNode, lView); } - - -/** - * Returns the appropriate `ChangeDetectorRef` for a pipe. - * - * @codeGenApi - */ -export function ɵɵinjectPipeChangeDetectorRef(flags = InjectFlags.Default): ChangeDetectorRef|null { - const value = injectChangeDetectorRef(true); - if (value == null && !(flags & InjectFlags.Optional)) { - throwProviderNotFoundError('ChangeDetectorRef'); - } else { - return value; - } -} diff --git a/packages/core/src/render3/view_ref.ts b/packages/core/src/render3/view_ref.ts index 9899c5a165..8f7fdc72fa 100644 --- a/packages/core/src/render3/view_ref.ts +++ b/packages/core/src/render3/view_ref.ts @@ -7,12 +7,15 @@ */ 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, ViewRefTracker} from '../linker/view_ref'; +import {removeFromArray} from '../util/array_utils'; +import {assertEqual} from '../util/assert'; import {collectNativeNodes} from './collect_native_nodes'; import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupWithContext} from './instructions/shared'; -import {CONTEXT, FLAGS, LView, LViewFlags, TVIEW} from './interfaces/view'; -import {destroyLView, renderDetachView} from './node_manipulation'; +import {CONTAINER_HEADER_OFFSET, VIEW_REFS} from './interfaces/container'; +import {isLContainer} from './interfaces/type_checks'; +import {CONTEXT, FLAGS, LView, LViewFlags, PARENT, TVIEW} from './interfaces/view'; +import {destroyLView, detachView, renderDetachView} from './node_manipulation'; @@ -24,7 +27,7 @@ export interface viewEngine_ChangeDetectorRef_interface extends viewEngine_Chang export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_InternalViewRef, viewEngine_ChangeDetectorRef_interface { private _appRef: ViewRefTracker|null = null; - private _viewContainerRef: viewEngine_ViewContainerRef|null = null; + private _attachedToViewContainer = false; get rootNodes(): any[] { const lView = this._lView; @@ -58,6 +61,10 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int return this._lView[CONTEXT] as T; } + set context(value: T) { + this._lView[CONTEXT] = value; + } + get destroyed(): boolean { return (this._lView[FLAGS] & LViewFlags.Destroyed) === LViewFlags.Destroyed; } @@ -65,14 +72,21 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int destroy(): void { if (this._appRef) { this._appRef.detachView(this); - } else if (this._viewContainerRef) { - const index = this._viewContainerRef.indexOf(this); - - if (index > -1) { - this._viewContainerRef.detach(index); + } else if (this._attachedToViewContainer) { + const parent = this._lView[PARENT]; + if (isLContainer(parent)) { + const viewRefs = parent[VIEW_REFS] as ViewRef<unknown>[] | null; + const index = viewRefs ? viewRefs.indexOf(this) : -1; + if (index > -1) { + ngDevMode && + assertEqual( + index, parent.indexOf(this._lView) - CONTAINER_HEADER_OFFSET, + 'An attached view should be in the same position within its container as its ViewRef in the VIEW_REFS array.'); + detachView(parent, index); + removeFromArray(viewRefs!, index); + } } - - this._viewContainerRef = null; + this._attachedToViewContainer = false; } destroyLView(this._lView[TVIEW], this._lView); } @@ -94,7 +108,6 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int * <!-- TODO: Add a link to a chapter on OnPush components --> * * @usageNotes - * * ### Example * * ```typescript @@ -132,7 +145,6 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int * <!-- TODO: Add a live demo once ref.detectChanges is merged into master --> * * @usageNotes - * * ### Example * * The following example defines a component with a large list of readonly data. @@ -187,7 +199,6 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int * <!-- TODO: Add a link to a chapter on detach/reattach/local digest --> * * @usageNotes - * * ### Example * * The following example creates a component displaying `live` data. The component will detach @@ -249,7 +260,6 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int * <!-- TODO: Add a live demo once ref.detectChanges is merged into master --> * * @usageNotes - * * ### Example * * The following example defines a component with a large list of readonly data. @@ -275,11 +285,11 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int checkNoChangesInternal(this._lView[TVIEW], this._lView, this.context); } - attachToViewContainerRef(vcRef: viewEngine_ViewContainerRef) { + attachToViewContainerRef() { if (this._appRef) { throw new Error('This view is already attached directly to the ApplicationRef!'); } - this._viewContainerRef = vcRef; + this._attachedToViewContainer = true; } detachFromAppRef() { @@ -288,7 +298,7 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int } attachToAppRef(appRef: ViewRefTracker) { - if (this._viewContainerRef) { + if (this._attachedToViewContainer) { throw new Error('This view is already attached to a ViewContainer!'); } this._appRef = appRef; diff --git a/packages/core/src/sanitization/bypass.ts b/packages/core/src/sanitization/bypass.ts index 9b52ab2b40..f45924688f 100644 --- a/packages/core/src/sanitization/bypass.ts +++ b/packages/core/src/sanitization/bypass.ts @@ -18,8 +18,6 @@ export const enum BypassType { /** * Marker interface for a value that's safe to use in a particular context. * - * 标记界面,可在特定上下文中安全使用值。 - * * @publicApi */ export interface SafeValue {} @@ -27,8 +25,6 @@ export interface SafeValue {} /** * Marker interface for a value that's safe to use as HTML. * - * 标记界面,可安全用作 HTML 值。 - * * @publicApi */ export interface SafeHtml extends SafeValue {} @@ -36,8 +32,6 @@ export interface SafeHtml extends SafeValue {} /** * Marker interface for a value that's safe to use as style (CSS). * - * 标记界面,可安全用作样式(CSS)。 - * * @publicApi */ export interface SafeStyle extends SafeValue {} @@ -45,8 +39,6 @@ export interface SafeStyle extends SafeValue {} /** * Marker interface for a value that's safe to use as JavaScript. * - * 标记界面,可安全用作 JavaScript 的值。 - * * @publicApi */ export interface SafeScript extends SafeValue {} @@ -54,8 +46,6 @@ export interface SafeScript extends SafeValue {} /** * Marker interface for a value that's safe to use as a URL linking to a document. * - * 标记界面,用于安全地用作链接到文档的 URL 的值。 - * * @publicApi */ export interface SafeUrl extends SafeValue {} @@ -63,8 +53,6 @@ export interface SafeUrl extends SafeValue {} /** * Marker interface for a value that's safe to use as a URL to load executable code from. * - * 标记接口,用于安全地用作 URL 的值,以从中加载可执行代码。 - * * @publicApi */ export interface SafeResourceUrl extends SafeValue {} diff --git a/packages/core/src/sanitization/html_sanitizer.ts b/packages/core/src/sanitization/html_sanitizer.ts index d55dd9fe24..b5121d452a 100644 --- a/packages/core/src/sanitization/html_sanitizer.ts +++ b/packages/core/src/sanitization/html_sanitizer.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {isDevMode} from '../util/is_dev_mode'; import {TrustedHTML} from '../util/security/trusted_type_defs'; import {trustedHTMLFromString} from '../util/security/trusted_types'; import {getInertBodyHelper, InertBodyHelper} from './inert_body'; @@ -271,7 +270,7 @@ export function _sanitizeHtml(defaultDoc: any, unsafeHtmlInput: string): Trusted const sanitizer = new SanitizingHtmlSerializer(); const safeHtml = sanitizer.sanitizeChildren( getTemplateContent(inertBodyElement!) as Element || inertBodyElement); - if (isDevMode() && sanitizer.sanitizedSomething) { + if ((typeof ngDevMode === 'undefined' || ngDevMode) && sanitizer.sanitizedSomething) { console.warn( 'WARNING: sanitizing HTML stripped some content, see https://g.co/ng/security#xss'); } diff --git a/packages/core/src/sanitization/inert_body.ts b/packages/core/src/sanitization/inert_body.ts index c460c5dc65..4086956ac6 100644 --- a/packages/core/src/sanitization/inert_body.ts +++ b/packages/core/src/sanitization/inert_body.ts @@ -16,7 +16,8 @@ import {trustedHTMLFromString} from '../util/security/trusted_types'; * Fallback: InertDocument strategy */ export function getInertBodyHelper(defaultDoc: Document): InertBodyHelper { - return isDOMParserAvailable() ? new DOMParserHelper() : new InertDocumentHelper(defaultDoc); + const inertDocumentHelper = new InertDocumentHelper(defaultDoc); + return isDOMParserAvailable() ? new DOMParserHelper(inertDocumentHelper) : inertDocumentHelper; } export interface InertBodyHelper { @@ -31,6 +32,8 @@ export interface InertBodyHelper { * This is the default strategy used in browsers that support it. */ class DOMParserHelper implements InertBodyHelper { + constructor(private inertDocumentHelper: InertBodyHelper) {} + getInertBodyElement(html: string): HTMLElement|null { // We add these extra elements to ensure that the rest of the content is parsed as expected // e.g. leading whitespace is maintained and tags like `<meta>` do not get hoisted to the @@ -41,6 +44,12 @@ class DOMParserHelper implements InertBodyHelper { const body = new window.DOMParser() .parseFromString(trustedHTMLFromString(html) as string, 'text/html') .body as HTMLBodyElement; + if (body === null) { + // In some browsers (e.g. Mozilla/5.0 iPad AppleWebKit Mobile) the `body` property only + // becomes available in the following tick of the JS engine. In that case we fall back to + // the `inertDocumentHelper` instead. + return this.inertDocumentHelper.getInertBodyElement(html); + } body.removeChild(body.firstChild!); return body; } catch { diff --git a/packages/core/src/sanitization/sanitization.ts b/packages/core/src/sanitization/sanitization.ts index f8050d39cf..c30b079cd1 100644 --- a/packages/core/src/sanitization/sanitization.ts +++ b/packages/core/src/sanitization/sanitization.ts @@ -145,8 +145,10 @@ export function ɵɵsanitizeScript(unsafeScript: any): TrustedScript|string { } /** - * Promotes the given constant string to a TrustedHTML. - * @param html constant string containing trusted HTML. + * A template tag function for promoting the associated constant literal to a + * TrustedHTML. Interpolation is explicitly not allowed. + * + * @param html constant template literal containing trusted HTML. * @returns TrustedHTML wrapping `html`. * * @security This is a security-sensitive function and should only be used to @@ -155,13 +157,24 @@ export function ɵɵsanitizeScript(unsafeScript: any): TrustedScript|string { * * @codeGenApi */ -export function ɵɵtrustConstantHtml(html: string): TrustedHTML|string { - return trustedHTMLFromString(html); +export function ɵɵtrustConstantHtml(html: TemplateStringsArray): TrustedHTML|string { + // The following runtime check ensures that the function was called as a + // template tag (e.g. ɵɵtrustConstantHtml`content`), without any interpolation + // (e.g. not ɵɵtrustConstantHtml`content ${variable}`). A TemplateStringsArray + // is an array with a `raw` property that is also an array. The associated + // template literal has no interpolation if and only if the length of the + // TemplateStringsArray is 1. + if (ngDevMode && (!Array.isArray(html) || !Array.isArray(html.raw) || html.length !== 1)) { + throw new Error(`Unexpected interpolation in trusted HTML constant: ${html.join('?')}`); + } + return trustedHTMLFromString(html[0]); } /** - * Promotes the given constant string to a TrustedScriptURL. - * @param url constant string containing a trusted script URL. + * A template tag function for promoting the associated constant literal to a + * TrustedScriptURL. Interpolation is explicitly not allowed. + * + * @param url constant template literal containing a trusted script URL. * @returns TrustedScriptURL wrapping `url`. * * @security This is a security-sensitive function and should only be used to @@ -170,8 +183,17 @@ export function ɵɵtrustConstantHtml(html: string): TrustedHTML|string { * * @codeGenApi */ -export function ɵɵtrustConstantResourceUrl(url: string): TrustedScriptURL|string { - return trustedScriptURLFromString(url); +export function ɵɵtrustConstantResourceUrl(url: TemplateStringsArray): TrustedScriptURL|string { + // The following runtime check ensures that the function was called as a + // template tag (e.g. ɵɵtrustConstantResourceUrl`content`), without any + // interpolation (e.g. not ɵɵtrustConstantResourceUrl`content ${variable}`). A + // TemplateStringsArray is an array with a `raw` property that is also an + // array. The associated template literal has no interpolation if and only if + // the length of the TemplateStringsArray is 1. + if (ngDevMode && (!Array.isArray(url) || !Array.isArray(url.raw) || url.length !== 1)) { + throw new Error(`Unexpected interpolation in trusted URL constant: ${url.join('?')}`); + } + return trustedScriptURLFromString(url[0]); } /** diff --git a/packages/core/src/sanitization/sanitizer.ts b/packages/core/src/sanitization/sanitizer.ts index 4d20902730..adf9523725 100644 --- a/packages/core/src/sanitization/sanitizer.ts +++ b/packages/core/src/sanitization/sanitizer.ts @@ -12,14 +12,12 @@ import {SecurityContext} from './security'; /** * Sanitizer is used by the views to sanitize potentially dangerous values. * - * 视图使用消毒器来对潜在的危险值进行无害化处理。 - * * @publicApi */ export abstract class Sanitizer { abstract sanitize(context: SecurityContext, value: {}|string|null): string|null; /** @nocollapse */ - static ɵprov = ɵɵdefineInjectable({ + static ɵprov = /** @pureOrBreakMyCode */ ɵɵdefineInjectable({ token: Sanitizer, providedIn: 'root', factory: () => null, diff --git a/packages/core/src/sanitization/security.ts b/packages/core/src/sanitization/security.ts index 8ff7e48605..4a52309069 100644 --- a/packages/core/src/sanitization/security.ts +++ b/packages/core/src/sanitization/security.ts @@ -11,12 +11,8 @@ * like `innerHTML` that could cause Cross Site Scripting (XSS) security bugs when improperly * handled. * - * SecurityContext 标记了具有危险安全隐患的位置,例如,像 `innerHTML` 这样的 DOM 属性,如果处理不当,可能会导致跨站点脚本(XSS)安全错误。 - * * See DomSanitizer for more details on security in Angular applications. * - * 有关 Angular 应用程序中安全性的更多详细信息,请参见 DomSanitizer。 - * * @publicApi */ export enum SecurityContext { diff --git a/packages/core/src/sanitization/url_sanitizer.ts b/packages/core/src/sanitization/url_sanitizer.ts index 67e5fb8563..526927e4f2 100644 --- a/packages/core/src/sanitization/url_sanitizer.ts +++ b/packages/core/src/sanitization/url_sanitizer.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {isDevMode} from '../util/is_dev_mode'; /** * A pattern that recognizes a commonly useful subset of URLs that are safe. @@ -47,7 +46,7 @@ export function _sanitizeUrl(url: string): string { url = String(url); if (url.match(SAFE_URL_PATTERN) || url.match(DATA_URL_PATTERN)) return url; - if (isDevMode()) { + if (typeof ngDevMode === 'undefined' || ngDevMode) { console.warn(`WARNING: sanitizing unsafe URL value ${url} (see https://g.co/ng/security#xss)`); } diff --git a/packages/core/src/testability/testability.ts b/packages/core/src/testability/testability.ts index b078a411cd..55d6e1f9b3 100644 --- a/packages/core/src/testability/testability.ts +++ b/packages/core/src/testability/testability.ts @@ -14,9 +14,6 @@ import {NgZone} from '../zone/ng_zone'; * Testability API. * `declare` keyword causes tsickle to generate externs, so these methods are * not renamed by Closure Compiler. - * - * `Testability` API。`declare` 关键字会导致 tsickle 生成外部变量,因此 Closure Compiler 不会重命名这些方法。 - * * @publicApi */ export declare interface PublicTestability { @@ -55,9 +52,6 @@ interface WaitCallback { * The Testability service provides testing hooks that can be accessed from * the browser and by services such as Protractor. Each bootstrapped Angular * application on the page will have an instance of Testability. - * - * `Testability` 服务提供了可以从浏览器和诸如 Protractor 之类的服务访问的测试钩子。页面上每个自举的 Angular 应用程序都会有一个 Testability 实例。 - * * @publicApi */ @Injectable() @@ -68,9 +62,6 @@ export class Testability implements PublicTestability { * Whether any work was done since the last 'whenStable' callback. This is * useful to detect if this could have potentially destabilized another * component while it is stabilizing. - * - * 自上次 “whenStable” 回调以来是否完成了任何工作。这对于检测它在稳定过程中是否可能使另一个组件不稳定可能很有用。 - * * @internal */ private _didWork: boolean = false; @@ -109,13 +100,7 @@ export class Testability implements PublicTestability { /** * Increases the number of pending request - * - * 增加待处理请求的数量 - * * @deprecated pending requests are now tracked with zones. - * - * 现在可以使用 Zone 来跟踪未决请求。 - * */ increasePendingRequestCount(): number { this._pendingCount += 1; @@ -125,13 +110,7 @@ export class Testability implements PublicTestability { /** * Decreases the number of pending request - * - * 减少待处理的请求数 - * * @deprecated pending requests are now tracked with zones - * - * 现在使用 Zone 跟踪待处理的请求 - * */ decreasePendingRequestCount(): number { this._pendingCount -= 1; @@ -144,9 +123,6 @@ export class Testability implements PublicTestability { /** * Whether an associated application is stable - * - * 关联的应用程序是否稳定 - * */ isStable(): boolean { return this._isZoneStable && this._pendingCount === 0 && !this._ngZone.hasPendingMacrotasks; @@ -211,30 +187,19 @@ export class Testability implements PublicTestability { * Wait for the application to be stable with a timeout. If the timeout is reached before that * happens, the callback receives a list of the macro tasks that were pending, otherwise null. * - * 等待应用程序稳定并超时。如果在此之前已达到超时,则回调将收到未决的宏任务的列表,否则为 null。 - * * @param doneCb The callback to invoke when Angular is stable or the timeout expires * whichever comes first. - * - * 当 Angular 稳定或超时到期时调用的回调,以先到者为准。 - * * @param timeout Optional. The maximum time to wait for Angular to become stable. If not * specified, whenStable() will wait forever. - * - * 可选的。等待 Angular 稳定下来的最长时间。如果未指定,那么 whenStable() 将永远等待。 - * * @param updateCb Optional. If specified, this callback will be invoked whenever the set of * pending macrotasks changes. If this callback returns true doneCb will not be invoked * and no further updates will be issued. - * - * 可选的。如果指定,则每当挂起的宏任务集发生更改时,都会调用此回调。如果此回调返回 true,那么将不会调用 doneCb,并且不会发出进一步的更新。 - * */ whenStable(doneCb: Function, timeout?: number, updateCb?: Function): void { if (updateCb && !this.taskTrackingZone) { throw new Error( 'Task tracking zone is required when passing an update callback to ' + - 'whenStable(). Is "zone.js/dist/task-tracking.js" loaded?'); + 'whenStable(). Is "zone.js/plugins/task-tracking" loaded?'); } // These arguments are 'Function' above to keep the public API simple. this.addCallback(doneCb as DoneCallback, timeout, updateCb as UpdateCallback); @@ -243,13 +208,7 @@ export class Testability implements PublicTestability { /** * Get the number of pending requests - * - * 获取待处理的请求数 - * * @deprecated pending requests are now tracked with zones - * - * 现在使用 Zone 跟踪待处理的请求 - * */ getPendingRequestCount(): number { return this._pendingCount; @@ -257,21 +216,9 @@ export class Testability implements PublicTestability { /** * Find providers by name - * - * 按名称查找提供者 - * * @param using The root element to search from - * - * 要搜索的根元素 - * * @param provider The name of binding variable - * - * 绑定变量的名称 - * * @param exactMatch Whether using exactMatch - * - * 是否使用 exactMatch - * */ findProviders(using: any, provider: string, exactMatch: boolean): any[] { // TODO(juliemr): implement. @@ -281,9 +228,6 @@ export class Testability implements PublicTestability { /** * A global registry of {@link Testability} instances for specific elements. - * - * {@link Testability} 实例的全局注册表,用于特定元素。 - * * @publicApi */ @Injectable() @@ -297,17 +241,8 @@ export class TestabilityRegistry { /** * Registers an application with a testability hook so that it can be tracked - * - * 使用 `Testability` 钩子注册应用程序,以便可以对其进行跟踪 - * * @param token token of application, root element - * - * 应用令牌,根元素 - * * @param testability Testability hook - * - * `Testability` 钩子 - * */ registerApplication(token: any, testability: Testability) { this._applications.set(token, testability); @@ -315,13 +250,7 @@ export class TestabilityRegistry { /** * Unregisters an application. - * - * 注销应用程序。 - * * @param token token of application, root element - * - * 应用令牌,根元素 - * */ unregisterApplication(token: any) { this._applications.delete(token); @@ -329,9 +258,6 @@ export class TestabilityRegistry { /** * Unregisters all applications - * - * 注销所有应用程序 - * */ unregisterAllApplications() { this._applications.clear(); @@ -339,13 +265,7 @@ export class TestabilityRegistry { /** * Get a testability hook associated with the application - * - * 获取与应用程序关联的 `Testability` 钩子 - * * @param elem root element - * - * 根元素 - * */ getTestability(elem: any): Testability|null { return this._applications.get(elem) || null; @@ -353,9 +273,6 @@ export class TestabilityRegistry { /** * Get all registered testabilities - * - * 获取所有注册的测试能力 - * */ getAllTestabilities(): Testability[] { return Array.from(this._applications.values()); @@ -363,9 +280,6 @@ export class TestabilityRegistry { /** * Get all registered applications(root elements) - * - * 获取所有注册的应用程序(根元素) - * */ getAllRootElements(): any[] { return Array.from(this._applications.keys()); @@ -373,18 +287,9 @@ export class TestabilityRegistry { /** * Find testability of a node in the Tree - * - * 在树中查找节点的 `Testability` - * * @param elem node - * - * 节点 - * * @param findInAncestors whether finding testability in ancestors if testability was not found in * current node - * - * 在当前节点中未找到 `Testability` 的情况下是否要在祖先中寻找 `Testability` - * */ findTestabilityInTree(elem: Node, findInAncestors: boolean = true): Testability|null { return _testabilityGetter.findTestabilityInTree(this, elem, findInAncestors); @@ -395,8 +300,6 @@ export class TestabilityRegistry { * Adapter interface for retrieving the `Testability` service associated for a * particular context. * - * 适配器接口,用于检索与特定上下文关联 `Testability` - * * @publicApi */ export interface GetTestability { @@ -415,9 +318,6 @@ class _NoopGetTestability implements GetTestability { /** * Set the {@link GetTestability} implementation used by the Angular testing framework. - * - * 设置 Angular 测试框架使用的 {@link GetTestability} 实现。 - * * @publicApi */ export function setTestabilityGetter(getter: GetTestability): void { diff --git a/packages/core/src/util/array_utils.ts b/packages/core/src/util/array_utils.ts index f8d819d334..747f476977 100644 --- a/packages/core/src/util/array_utils.ts +++ b/packages/core/src/util/array_utils.ts @@ -20,6 +20,31 @@ export function addAllToArray(items: any[], arr: any[]) { } } +/** + * Determines if the contents of two arrays is identical + * + * @param a first array + * @param b second array + * @param identityAccessor Optional function for extracting stable object identity from a value in + * the array. + */ +export function arrayEquals<T>(a: T[], b: T[], identityAccessor?: (value: T) => unknown): boolean { + if (a.length !== b.length) return false; + for (let i = 0; i < a.length; i++) { + let valueA = a[i]; + let valueB = b[i]; + if (identityAccessor) { + valueA = identityAccessor(valueA) as any; + valueB = identityAccessor(valueB) as any; + } + if (valueB !== valueA) { + return false; + } + } + return true; +} + + /** * Flattens an array. */ diff --git a/packages/core/src/util/assert.ts b/packages/core/src/util/assert.ts index 5fdd5bf0b5..bf1092a99b 100644 --- a/packages/core/src/util/assert.ts +++ b/packages/core/src/util/assert.ts @@ -31,6 +31,12 @@ export function assertString(actual: any, msg: string): asserts actual is string } } +export function assertFunction(actual: any, msg: string): asserts actual is Function { + if (!(typeof actual === 'function')) { + throwError(msg, actual === null ? 'null' : typeof actual, 'function', '==='); + } +} + export function assertEqual<T>(actual: T, expected: T, msg: string) { if (!(actual == expected)) { throwError(msg, actual, expected, '=='); diff --git a/packages/core/src/util/decorators.ts b/packages/core/src/util/decorators.ts index b265871681..92d4d0e04c 100644 --- a/packages/core/src/util/decorators.ts +++ b/packages/core/src/util/decorators.ts @@ -16,8 +16,6 @@ import {noSideEffects} from './closure'; * An interface implemented by all Angular type decorators, which allows them to be used as * decorators as well as Angular syntax. * - * 由所有 Angular 类型装饰器实现的接口,该接口允许将它们用作装饰器以及 Angular 语法。 - * * ``` * @ng.Component({...}) * class MyClass {...} @@ -28,9 +26,6 @@ import {noSideEffects} from './closure'; export interface TypeDecorator { /** * Invoke as decorator. - * - * 作为装饰器调用。 - * */ <T extends Type<any>>(type: T): T; diff --git a/packages/core/src/util/global.ts b/packages/core/src/util/global.ts index 0910b69fe6..e4f01f5a93 100644 --- a/packages/core/src/util/global.ts +++ b/packages/core/src/util/global.ts @@ -12,8 +12,6 @@ declare var WorkerGlobalScope: any /** TODO #9100 */; // We don't want to include the whole node.d.ts this this compilation unit so we'll just fake // the global "global" var for now. declare var global: any /** TODO #9100 */; -// Not yet available in TypeScript: https://github.com/Microsoft/TypeScript/pull/29332 -declare var globalThis: any /** TODO #9100 */; const __globalThis = typeof globalThis !== 'undefined' && globalThis; const __window = typeof window !== 'undefined' && window; diff --git a/packages/core/src/util/is_dev_mode.ts b/packages/core/src/util/is_dev_mode.ts index 796b804370..ad8184c922 100644 --- a/packages/core/src/util/is_dev_mode.ts +++ b/packages/core/src/util/is_dev_mode.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ +import {global} from './global'; + /** * This file is used to control if the default rendering pipeline should be `ViewEngine` or `Ivy`. * @@ -21,12 +23,8 @@ let _runModeLocked: boolean = false; * Returns whether Angular is in development mode. After called once, * the value is locked and won't change any more. * - * 返回 Angular 是否处于开发模式。调用一次后,该值将被锁定,并且将不再更改。 - * * By default, this is true, unless a user calls `enableProdMode` before calling this. * - * 默认情况下,这是正确的,除非用户在调用它之前调用 `enableProdMode` - * * @publicApi */ export function isDevMode(): boolean { @@ -38,19 +36,22 @@ export function isDevMode(): boolean { * Disable Angular's development mode, which turns off assertions and other * checks within the framework. * - * 禁用 Angular 的开发模式,该模式将关闭框架中的断言和其他检查。 - * * One important assertion this disables verifies that a change detection pass * does not result in additional changes to any bindings (also known as * unidirectional data flow). * - * 一个重要的断言,它禁用了对变更检测不会导致对任何绑定的(也称为单向数据流)额外更改的验证。 - * * @publicApi */ export function enableProdMode(): void { if (_runModeLocked) { throw new Error('Cannot enable prod mode after platform setup.'); } + + // The below check is there so when ngDevMode is set via terser + // `global['ngDevMode'] = false;` is also dropped. + if (typeof ngDevMode === undefined || !!ngDevMode) { + global['ngDevMode'] = false; + } + _devMode = false; } diff --git a/packages/core/src/util/security/trusted_types.ts b/packages/core/src/util/security/trusted_types.ts index 59c45e4662..75f7d7320f 100644 --- a/packages/core/src/util/security/trusted_types.ts +++ b/packages/core/src/util/security/trusted_types.ts @@ -111,7 +111,7 @@ export function newTrustedFunctionForDev(...args: string[]): Function { // below, where the Chromium bug is also referenced: // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor const fnArgs = args.slice(0, -1).join(','); - const fnBody = args.pop()!.toString(); + const fnBody = args[args.length - 1]; const body = `(function anonymous(${fnArgs} ) { ${fnBody} })`; @@ -120,6 +120,13 @@ export function newTrustedFunctionForDev(...args: string[]): Function { // being stripped out of JS binaries even if not used. The global['eval'] // indirection fixes that. const fn = global['eval'](trustedScriptFromString(body) as string) as Function; + if (fn.bind === undefined) { + // Workaround for a browser bug that only exists in Chrome 83, where passing + // a TrustedScript to eval just returns the TrustedScript back without + // evaluating it. In that case, fall back to the most straightforward + // implementation: + return new Function(...args); + } // To completely mimic the behavior of calling "new Function", two more // things need to happen: diff --git a/packages/core/src/version.ts b/packages/core/src/version.ts index ccd24226d7..71a5cb8878 100644 --- a/packages/core/src/version.ts +++ b/packages/core/src/version.ts @@ -9,8 +9,6 @@ /** * @description Represents the version of Angular * - * 表示 Angular 的版本 - * * @publicApi */ export class Version { diff --git a/packages/core/src/view/index.ts b/packages/core/src/view/index.ts index 7a828eb1dc..74e9cfc8a2 100644 --- a/packages/core/src/view/index.ts +++ b/packages/core/src/view/index.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +export {EMPTY_ARRAY} from '../util/empty'; export {anchorDef, elementDef} from './element'; export {clearOverrides, createNgModuleFactory, overrideComponentView, overrideProvider} from './entrypoint'; export {ngContentDef} from './ng_content'; @@ -16,7 +17,7 @@ export {queryDef} from './query'; export {createComponentFactory, getComponentViewDefinitionFactory, nodeValue, ViewRef_} from './refs'; export {initServicesIfNeeded} from './services'; export {textDef} from './text'; -export {createRendererType2, elementEventFullName, EMPTY_ARRAY, EMPTY_MAP, inlineInterpolate, interpolate, rootRenderNodes, tokenKey, unwrapValue} from './util'; +export {createRendererType2, elementEventFullName, 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/query.ts b/packages/core/src/view/query.ts index 83f4cae4a1..e60248ab82 100644 --- a/packages/core/src/view/query.ts +++ b/packages/core/src/view/query.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementRef} from '../linker/element_ref'; +import {ElementRef, unwrapElementRef} from '../linker/element_ref'; import {QueryList} from '../linker/query_list'; import {asElementData, asProviderData, asQueryList, NodeDef, NodeFlags, QueryBindingDef, QueryBindingType, QueryDef, QueryValueType, ViewData} from './types'; @@ -50,8 +50,8 @@ export function queryDef( }; } -export function createQuery(): QueryList<any> { - return new QueryList(); +export function createQuery(emitDistinctChangesOnly: boolean): QueryList<any> { + return new QueryList(emitDistinctChangesOnly); } export function dirtyParentQueries(view: ViewData) { @@ -107,7 +107,7 @@ export function checkAndUpdateQuery(view: ViewData, nodeDef: NodeDef) { newValues = calcQueryValues(view, 0, view.def.nodes.length - 1, nodeDef.query!, []); directiveInstance = view.component; } - queryList.reset(newValues); + queryList.reset(newValues, unwrapElementRef); const bindings = nodeDef.query!.bindings; let notify = false; for (let i = 0; i < bindings.length; i++) { diff --git a/packages/core/src/view/refs.ts b/packages/core/src/view/refs.ts index 7d219ace1c..b724fcc800 100644 --- a/packages/core/src/view/refs.ts +++ b/packages/core/src/view/refs.ts @@ -264,6 +264,10 @@ export class ViewRef_ implements EmbeddedViewRef<any>, InternalViewRef { return this._view.context; } + set context(value: any) { + this._view.context = value; + } + get destroyed(): boolean { return (this._view.state & ViewState.Destroyed) !== 0; } diff --git a/packages/core/src/view/services.ts b/packages/core/src/view/services.ts index d11566ca5a..c15f0a4aa1 100644 --- a/packages/core/src/view/services.ts +++ b/packages/core/src/view/services.ts @@ -16,6 +16,7 @@ import {NgModuleRef} from '../linker/ng_module_factory'; import {Renderer2, RendererFactory2} from '../render/api'; import {RendererStyleFlags2, RendererType2} from '../render/api_flags'; import {Sanitizer} from '../sanitization/sanitizer'; +import {escapeCommentText} from '../util/dom'; import {isDevMode} from '../util/is_dev_mode'; import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../util/ng_reflect'; @@ -448,7 +449,8 @@ function debugCheckAndUpdateNode( const el = asElementData(view, elDef.nodeIndex).renderElement; if (!elDef.element!.name) { // a comment. - view.renderer.setValue(el, `bindings=${JSON.stringify(bindingValues, null, 2)}`); + view.renderer.setValue( + el, escapeCommentText(`bindings=${JSON.stringify(bindingValues, null, 2)}`)); } else { // a regular element. for (let attr in bindingValues) { @@ -727,7 +729,7 @@ export class DebugRenderer2 implements Renderer2 { } createComment(value: string): any { - const comment = this.delegate.createComment(value); + const comment = this.delegate.createComment(escapeCommentText(value)); const debugCtx = this.createDebugContext(comment); if (debugCtx) { indexDebugNode(new DebugNode__PRE_R3__(comment, null, debugCtx)); diff --git a/packages/core/src/view/types.ts b/packages/core/src/view/types.ts index 930be64de2..caef9f00d7 100644 --- a/packages/core/src/view/types.ts +++ b/packages/core/src/view/types.ts @@ -209,6 +209,7 @@ export const enum NodeFlags { StaticQuery = 1 << 28, DynamicQuery = 1 << 29, TypeNgModule = 1 << 30, + EmitDistinctChangesOnly = 1 << 31, CatQuery = TypeContentQuery | TypeViewQuery, // mutually exclusive values... diff --git a/packages/core/src/view/util.ts b/packages/core/src/view/util.ts index 459b222381..c83b49360c 100644 --- a/packages/core/src/view/util.ts +++ b/packages/core/src/view/util.ts @@ -444,5 +444,4 @@ function _toStringWithNull(v: any): string { return v != null ? v.toString() : ''; } -export const EMPTY_ARRAY: any[] = []; export const EMPTY_MAP: {[key: string]: any} = {}; diff --git a/packages/core/src/view/view.ts b/packages/core/src/view/view.ts index 949fac6e1e..9a3c1a737a 100644 --- a/packages/core/src/view/view.ts +++ b/packages/core/src/view/view.ts @@ -327,7 +327,9 @@ function createViewNodes(view: ViewData) { break; case NodeFlags.TypeContentQuery: case NodeFlags.TypeViewQuery: - nodeData = createQuery() as any; + nodeData = createQuery( + (nodeDef.flags & NodeFlags.EmitDistinctChangesOnly) === + NodeFlags.EmitDistinctChangesOnly) as any; break; case NodeFlags.TypeNgContent: appendNgContent(view, renderHost, nodeDef); diff --git a/packages/core/src/zone/ng_zone.ts b/packages/core/src/zone/ng_zone.ts index 4e9dc03174..74d1748116 100644 --- a/packages/core/src/zone/ng_zone.ts +++ b/packages/core/src/zone/ng_zone.ts @@ -15,26 +15,19 @@ import {getNativeRequestAnimationFrame} from '../util/raf'; /** * An injectable service for executing work inside or outside of the Angular zone. * - * 一种用于在 Angular Zone 内部或外部执行任务的可注入服务。 - * * The most common use of this service is to optimize performance when starting a work consisting of * one or more asynchronous tasks that don't require UI updates or error handling to be handled by * Angular. Such tasks can be kicked off via {@link #runOutsideAngular} and if needed, these tasks * can reenter the Angular zone via {@link #run}. * - * 此服务最常见的用途是在启动包含一个或多个不需要 Angular 处理的 UI 更新或错误处理的异步任务的工作时优化性能。可以通过 {@link #runOutsideAngular} 启动此类任务,如果需要,这些任务可以通过 {@link #run} 重新进入 Angular zone。 - * * <!-- TODO: add/fix links to: * - docs explaining zones and the use of zones in Angular and change-detection * - link to runOutsideAngular/run (throughout this file!) * --> * * @usageNotes - * * ### Example * - * ### 例子 - * * ``` * import {Component, NgZone} from '@angular/core'; * import {NgIf} from '@angular/common'; @@ -99,17 +92,11 @@ export class NgZone { /** * Whether there are no outstanding microtasks or macrotasks. - * - * 是否没有未解决的微任务或宏任务。 - * */ readonly isStable: boolean = true; /** * Notifies when code enters Angular Zone. This gets fired first on VM Turn. - * - * 在代码进入 “Angular Zone ” 时通知。这首先在 VM 周期中触发。 - * */ readonly onUnstable: EventEmitter<any> = new EventEmitter(false); @@ -117,9 +104,6 @@ export class NgZone { * Notifies when there is no more microtasks enqueued in the current VM Turn. * This is a hint for Angular to do change detection, which may enqueue more microtasks. * For this reason this event can fire multiple times per VM Turn. - * - * 在当前的 VM Turn 中没有更多微任务排队时通知。这是 Angular 进行变更检测的提示,它可能会排队更多的微任务。因此,此事件可在每次 VM 周期中触发多次。 - * */ readonly onMicrotaskEmpty: EventEmitter<any> = new EventEmitter(false); @@ -127,17 +111,11 @@ export class NgZone { * Notifies when the last `onMicrotaskEmpty` has run and there are no more microtasks, which * implies we are about to relinquish VM turn. * This event gets called just once. - * - * 在最后一个 `onMicrotaskEmpty` 已运行并且没有更多微任务时通知,这意味着我们将放弃 VM 周期。该事件只会被调用一次。 - * */ readonly onStable: EventEmitter<any> = new EventEmitter(false); /** * Notifies that an error has been delivered. - * - * 通知已传递的错误。 - * */ readonly onError: EventEmitter<any> = new EventEmitter(false); @@ -194,21 +172,13 @@ export class NgZone { * Executes the `fn` function synchronously within the Angular zone and returns value returned by * the function. * - * 在 Angular Zone 内同步执行的 `fn` 函数,并返回该函数返回的值。 - * * Running functions via `run` allows you to reenter Angular zone from a task that was executed * outside of the Angular zone (typically started via {@link #runOutsideAngular}). * - * 通过 `run` 运行的函数可让你从在 Angular Zone 之外执行的任务(通常通过 {@link #runOutsideAngular} 启动)重新进入 Angular Zone 。 - * * Any future tasks or microtasks scheduled from within this function will continue executing from * within the Angular zone. * - * 在此功能内计划的任何将来的任务或微任务将在 Angular Zone 内继续执行。 - * * If a synchronous error happens it will be rethrown and not reported via `onError`. - * - * 如果发生同步错误,它将被重新抛出,并且不会通过 `onError` 报告。 */ run<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T { return (this as any as NgZonePrivate)._inner.run(fn, applyThis, applyArgs); @@ -218,21 +188,13 @@ export class NgZone { * Executes the `fn` function synchronously within the Angular zone as a task and returns value * returned by the function. * - * 作为任务在 Angular Zone 中同步执行 `fn` 函数,并返回该函数返回的值。 - * * Running functions via `run` allows you to reenter Angular zone from a task that was executed * outside of the Angular zone (typically started via {@link #runOutsideAngular}). * - * 通过 `run` 运行的函数可让你从在 Angular Zone 之外执行的任务(通常通过 {@link #runOutsideAngular} 启动)重新进入 Angular Zone 。 - * * Any future tasks or microtasks scheduled from within this function will continue executing from * within the Angular zone. * - * 在此功能内计划的任何将来的任务或微任务都将在 Angular Zone 内继续执行。 - * * If a synchronous error happens it will be rethrown and not reported via `onError`. - * - * 如果发生同步错误,它将被重新抛出,并且不会通过 `onError` 报告。 */ runTask<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[], name?: string): T { const zone = (this as any as NgZonePrivate)._inner; @@ -247,9 +209,6 @@ export class NgZone { /** * Same as `run`, except that synchronous errors are caught and forwarded via `onError` and not * rethrown. - * - * 与 `run` 相同,但同步错误是通过 `onError` 捕获并转发的,而不是重新抛出。 - * */ runGuarded<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T { return (this as any as NgZonePrivate)._inner.runGuarded(fn, applyThis, applyArgs); @@ -259,22 +218,14 @@ export class NgZone { * Executes the `fn` function synchronously in Angular's parent zone and returns value returned by * the function. * - * 在 Angular 的父 Zone 中同步执行 `fn` 函数,并返回该函数返回的值。 - * * Running functions via {@link #runOutsideAngular} allows you to escape Angular's zone and do * work that * doesn't trigger Angular change-detection or is subject to Angular's error handling. * - * 通过 {@link #runOutsideAngular} 运行函数可让你离开 Angular 的 Zone 并执行不会触发 Angular 变更检测或受 Angular 错误处理控制的工作。 - * * Any future tasks or microtasks scheduled from within this function will continue executing from * outside of the Angular zone. * - * 从此函数中计划的任何将来的任务或微任务将在 Angular Zone 之外继续执行。 - * * Use {@link #run} to reenter the Angular zone and do work that updates the application model. - * - * 使用 {@link #run} 重新进入 Angular Zone 并执行更新应用程序模型的工作。 */ runOutsideAngular<T>(fn: (...args: any[]) => T): T { return (this as any as NgZonePrivate)._outer.run(fn); @@ -292,6 +243,17 @@ interface NgZonePrivate extends NgZone { hasPendingMacrotasks: boolean; hasPendingMicrotasks: boolean; lastRequestAnimationFrameId: number; + /** + * A flag to indicate if NgZone is currently inside + * checkStable and to prevent re-entry. The flag is + * needed because it is possible to invoke the change + * detection from within change detection leading to + * incorrect behavior. + * + * For detail, please refer here, + * https://github.com/angular/angular/pull/40540 + */ + isCheckStableRunning: boolean; isStable: boolean; /** * Optionally specify coalescing event change detections or not. @@ -340,6 +302,21 @@ interface NgZonePrivate extends NgZone { } function checkStable(zone: NgZonePrivate) { + // TODO: @JiaLiPassion, should check zone.isCheckStableRunning to prevent + // re-entry. The case is: + // + // @Component({...}) + // export class AppComponent { + // constructor(private ngZone: NgZone) { + // this.ngZone.onStable.subscribe(() => { + // this.ngZone.run(() => console.log('stable');); + // }); + // } + // + // The onStable subscriber run another function inside ngZone + // which causes `checkStable()` re-entry. + // But this fix causes some issues in g3, so this fix will be + // launched in another PR. if (zone._nesting == 0 && !zone.hasPendingMicrotasks && !zone.isStable) { try { zone._nesting++; @@ -358,7 +335,20 @@ function checkStable(zone: NgZonePrivate) { } function delayChangeDetectionForEvents(zone: NgZonePrivate) { - if (zone.lastRequestAnimationFrameId !== -1) { + /** + * We also need to check _nesting here + * Consider the following case with shouldCoalesceRunChangeDetection = true + * + * ngZone.run(() => {}); + * ngZone.run(() => {}); + * + * We want the two `ngZone.run()` only trigger one change detection + * when shouldCoalesceRunChangeDetection is true. + * And because in this case, change detection run in async way(requestAnimationFrame), + * so we also need to check the _nesting here to prevent multiple + * change detections. + */ + if (zone.isCheckStableRunning || zone.lastRequestAnimationFrameId !== -1) { return; } zone.lastRequestAnimationFrameId = zone.nativeRequestAnimationFrame.call(global, () => { @@ -375,7 +365,9 @@ function delayChangeDetectionForEvents(zone: NgZonePrivate) { zone.fakeTopEventTask = Zone.root.scheduleEventTask('fakeTopEventTask', () => { zone.lastRequestAnimationFrameId = -1; updateMicroTaskStatus(zone); + zone.isCheckStableRunning = true; checkStable(zone); + zone.isCheckStableRunning = false; }, undefined, () => {}, () => {}); } zone.fakeTopEventTask.invoke(); diff --git a/packages/core/test/BUILD.bazel b/packages/core/test/BUILD.bazel index 8827492936..746631ca27 100644 --- a/packages/core/test/BUILD.bazel +++ b/packages/core/test/BUILD.bazel @@ -27,7 +27,7 @@ genrule( $(execpath @npm//typescript/bin:tsc) $< --outDir $$es2015_out_dir --target ES2015 \ --types --module umd $(execpath @npm//typescript/bin:tsc) --outFile $@ $$es2015_out_file --allowJs \ - --types --target ES5 + --types --target ES5 --downlevelIteration """, tools = ["@npm//typescript/bin:tsc"], ) diff --git a/packages/core/test/acceptance/bootstrap_spec.ts b/packages/core/test/acceptance/bootstrap_spec.ts index 7024c3b6c5..edd8eaa43d 100644 --- a/packages/core/test/acceptance/bootstrap_spec.ts +++ b/packages/core/test/acceptance/bootstrap_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {COMPILER_OPTIONS, Component, destroyPlatform, NgModule, ViewEncapsulation} from '@angular/core'; +import {ApplicationRef, COMPILER_OPTIONS, Component, destroyPlatform, NgModule, NgZone, TestabilityRegistry, ViewEncapsulation} from '@angular/core'; +import {expect} from '@angular/core/testing/src/testing_internal'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import {onlyInIvy, withBody} from '@angular/private/testing'; @@ -151,6 +152,97 @@ describe('bootstrap', () => { ngModuleRef.destroy(); })); + describe('ApplicationRef cleanup', () => { + it('should cleanup ApplicationRef when Injector is destroyed', + withBody('<my-app></my-app>', async () => { + const TestModule = createComponentAndModule(); + + const ngModuleRef = await platformBrowserDynamic().bootstrapModule(TestModule); + const appRef = ngModuleRef.injector.get(ApplicationRef); + const testabilityRegistry = ngModuleRef.injector.get(TestabilityRegistry); + + expect(appRef.components.length).toBe(1); + expect(testabilityRegistry.getAllRootElements().length).toBe(1); + + ngModuleRef.destroy(); // also destroys an Injector instance. + + expect(appRef.components.length).toBe(0); + expect(testabilityRegistry.getAllRootElements().length).toBe(0); + })); + + it('should cleanup ApplicationRef when ComponentRef is destroyed', + withBody('<my-app></my-app>', async () => { + const TestModule = createComponentAndModule(); + + const ngModuleRef = await platformBrowserDynamic().bootstrapModule(TestModule); + const appRef = ngModuleRef.injector.get(ApplicationRef); + const testabilityRegistry = ngModuleRef.injector.get(TestabilityRegistry); + const componentRef = appRef.components[0]; + + expect(appRef.components.length).toBe(1); + expect(testabilityRegistry.getAllRootElements().length).toBe(1); + + componentRef.destroy(); + + expect(appRef.components.length).toBe(0); + expect(testabilityRegistry.getAllRootElements().length).toBe(0); + })); + + it('should not throw in case ComponentRef is destroyed and Injector is destroyed after that', + withBody('<my-app></my-app>', async () => { + const TestModule = createComponentAndModule(); + + const ngModuleRef = await platformBrowserDynamic().bootstrapModule(TestModule); + const appRef = ngModuleRef.injector.get(ApplicationRef); + const testabilityRegistry = ngModuleRef.injector.get(TestabilityRegistry); + const componentRef = appRef.components[0]; + + expect(appRef.components.length).toBe(1); + expect(testabilityRegistry.getAllRootElements().length).toBe(1); + + componentRef.destroy(); + ngModuleRef.destroy(); // also destroys an Injector instance. + + expect(appRef.components.length).toBe(0); + expect(testabilityRegistry.getAllRootElements().length).toBe(0); + })); + + it('should not throw in case Injector is destroyed and ComponentRef is destroyed after that', + withBody('<my-app></my-app>', async () => { + const TestModule = createComponentAndModule(); + + const ngModuleRef = await platformBrowserDynamic().bootstrapModule(TestModule); + const appRef = ngModuleRef.injector.get(ApplicationRef); + const testabilityRegistry = ngModuleRef.injector.get(TestabilityRegistry); + const componentRef = appRef.components[0]; + + expect(appRef.components.length).toBe(1); + expect(testabilityRegistry.getAllRootElements().length).toBe(1); + + ngModuleRef.destroy(); // also destroys an Injector instance. + componentRef.destroy(); + + expect(appRef.components.length).toBe(0); + expect(testabilityRegistry.getAllRootElements().length).toBe(0); + })); + }); + + describe('PlatformRef cleanup', () => { + it('should unsubscribe from `onError` when Injector is destroyed', + withBody('<my-app></my-app>', async () => { + const TestModule = createComponentAndModule(); + + const ngModuleRef = await platformBrowserDynamic().bootstrapModule(TestModule); + const ngZone = ngModuleRef.injector.get(NgZone); + + expect(ngZone.onError.observers.length).toBe(1); + + ngModuleRef.destroy(); + + expect(ngZone.onError.observers.length).toBe(0); + })); + }); + onlyInIvy('options cannot be changed in Ivy').describe('changing bootstrap options', () => { beforeEach(() => { spyOn(console, 'error'); @@ -289,4 +381,4 @@ export class MultipleSelectorsAppComponent { bootstrap: [MultipleSelectorsAppComponent], }) export class MultipleSelectorsAppModule { -} \ No newline at end of file +} diff --git a/packages/core/test/acceptance/component_spec.ts b/packages/core/test/acceptance/component_spec.ts index 4023e77118..41c8208b78 100644 --- a/packages/core/test/acceptance/component_spec.ts +++ b/packages/core/test/acceptance/component_spec.ts @@ -303,6 +303,73 @@ describe('component', () => { expect(wrapperEls.length).toBe(2); // other elements are preserved }); + describe('with ngDevMode', () => { + const _global: {ngDevMode: any} = global as any; + let saveNgDevMode!: typeof ngDevMode; + beforeEach(() => saveNgDevMode = ngDevMode); + afterEach(() => _global.ngDevMode = saveNgDevMode); + // In dev mode we have some additional logic to freeze `TView.cleanup` array + // (see `storeCleanupWithContext` function). + // The tests below verify that this action doesn't trigger any change in behaviour + // for prod mode. See https://github.com/angular/angular/issues/40105. + ['ngDevMode off', 'ngDevMode on'].forEach((mode) => { + it('should invoke `onDestroy` callbacks of dynamically created component with ' + mode, + () => { + if (mode === 'ngDevMode off') { + _global.ngDevMode = false; + } + let wasOnDestroyCalled = false; + @Component({ + selector: '[comp]', + template: 'comp content', + }) + class DynamicComponent { + } + + @NgModule({ + declarations: [DynamicComponent], + entryComponents: [DynamicComponent], // needed only for ViewEngine + }) + class TestModule { + } + + @Component({ + selector: 'button', + template: '<div id="app-root" #anchor></div>', + }) + class App { + @ViewChild('anchor', {read: ViewContainerRef}) anchor!: ViewContainerRef; + + constructor(private cfr: ComponentFactoryResolver, private injector: Injector) {} + + create() { + const factory = this.cfr.resolveComponentFactory(DynamicComponent); + const componentRef = factory.create(this.injector); + componentRef.onDestroy(() => { + wasOnDestroyCalled = true; + }); + this.anchor.insert(componentRef.hostView); + } + + clear() { + this.anchor.clear(); + } + } + + TestBed.configureTestingModule({imports: [TestModule], declarations: [App]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + // Add ComponentRef to ViewContainerRef instance. + fixture.componentInstance.create(); + // Clear ViewContainerRef to invoke `onDestroy` callbacks on ComponentRef. + fixture.componentInstance.clear(); + + expect(wasOnDestroyCalled).toBeTrue(); + }); + }); + }); + describe('invalid host element', () => { it('should throw when <ng-container> is used as a host element for a Component', () => { @Component({ diff --git a/packages/core/test/acceptance/di_spec.ts b/packages/core/test/acceptance/di_spec.ts index 70f59007a0..fe1fe122c8 100644 --- a/packages/core/test/acceptance/di_spec.ts +++ b/packages/core/test/acceptance/di_spec.ts @@ -617,7 +617,8 @@ describe('di', () => { TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]}); expect(() => TestBed.createComponent(MyComp)) - .toThrowError('NG0200: Circular dependency in DI detected for DirectiveA'); + .toThrowError( + 'NG0200: Circular dependency in DI detected for DirectiveA. Find more at https://angular.io/errors/NG0200'); }); onlyInIvy('Ivy has different error message for circular dependency') @@ -633,7 +634,8 @@ describe('di', () => { TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]}); expect(() => TestBed.createComponent(MyComp)) - .toThrowError('NG0200: Circular dependency in DI detected for DirectiveA'); + .toThrowError( + 'NG0200: Circular dependency in DI detected for DirectiveA. Find more at https://angular.io/errors/NG0200'); }); describe('flags', () => { @@ -1779,7 +1781,8 @@ describe('di', () => { TestBed.configureTestingModule({declarations: [DirectiveString, MyComp, MyApp]}); expect(() => TestBed.createComponent(MyApp)) - .toThrowError('NG0201: No provider for String found in NodeInjector'); + .toThrowError( + 'NG0201: No provider for String found in NodeInjector. Find more at https://angular.io/errors/NG0201'); }); onlyInIvy('Ivy has different error message when dependency is not found') @@ -1859,7 +1862,8 @@ describe('di', () => { TestBed.configureTestingModule({declarations: [DirectiveComp, MyComp, MyApp]}); expect(() => TestBed.createComponent(MyApp)) - .toThrowError('NG0201: No provider for MyApp found in NodeInjector'); + .toThrowError( + 'NG0201: No provider for MyApp found in NodeInjector. Find more at https://angular.io/errors/NG0201'); }); describe('regression', () => { @@ -2803,6 +2807,106 @@ describe('di', () => { }); }); + it('should be able to use Host in `useFactory` dependency config', () => { + // Scenario: + // --------- + // <root (provides token A)> + // <comp (provides token B via useFactory(@Host() @Inject(A))></comp> + // </root> + @Component({ + selector: 'root', + template: '<comp></comp>', + viewProviders: [{ + provide: 'A', + useValue: 'A from Root', + }] + }) + class Root { + } + + @Component({ + selector: 'comp', + template: '{{ token }}', + viewProviders: [{ + provide: 'B', + deps: [[new Inject('A'), new Host()]], + useFactory: (token: string) => `${token} (processed by useFactory)`, + }] + }) + class Comp { + constructor(@Inject('B') readonly token: string) {} + } + + @Component({ + template: `<root></root>`, + }) + class App { + } + + TestBed.configureTestingModule({declarations: [Root, Comp, App]}); + + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('A from Root (processed by useFactory)'); + }); + + it('should not lookup outside of the host element when Host is used in `useFactory`', () => { + // Scenario: + // --------- + // <root (provides token A)> + // <intermediate> + // <comp (provides token B via useFactory(@Host() @Inject(A))></comp> + // </intermediate> + // </root> + @Component({ + selector: 'root', + template: '<intermediate></intermediate>', + viewProviders: [{ + provide: 'A', + useValue: 'A from Root', + }] + }) + class Root { + } + + @Component({ + selector: 'intermediate', + template: '<comp></comp>', + }) + class Intermediate { + } + + @Component({ + selector: 'comp', + template: '{{ token }}', + viewProviders: [{ + provide: 'B', + deps: [[new Inject('A'), new Host(), new Optional()]], + useFactory: (token: string) => + token ? `${token} (processed by useFactory)` : 'No token A found', + }] + }) + class Comp { + constructor(@Inject('B') readonly token: string) {} + } + + @Component({ + template: `<root></root>`, + }) + class App { + } + + TestBed.configureTestingModule({declarations: [Root, Comp, App, Intermediate]}); + + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + // Making sure that the `@Host` takes effect and token `A` becomes unavailable in DI since it's + // defined one level up from the Comp's host view. + expect(fixture.nativeElement.textContent).toBe('No token A found'); + }); + it('should not cause cyclic dependency if same token is requested in deps with @SkipSelf', () => { @Component({ selector: 'my-comp', diff --git a/packages/core/test/acceptance/host_binding_spec.ts b/packages/core/test/acceptance/host_binding_spec.ts index 30f4166c9a..df4e19ce1d 100644 --- a/packages/core/test/acceptance/host_binding_spec.ts +++ b/packages/core/test/acceptance/host_binding_spec.ts @@ -1473,4 +1473,63 @@ describe('host bindings', () => { /Attempted to set attribute `dynamic` on a container node. Host bindings are not valid on ng-container or ng-template./); }); }); + + onlyInIvy('VE does not support this').describe('host bindings on edge case properties', () => { + it('should handle host bindings with the same name as a primitive value', () => { + @Directive({ + selector: '[dir]', + host: { + '[class.a]': 'true', + '[class.b]': 'false', + } + }) + class MyDirective { + @HostBinding('class.c') true: any; + @HostBinding('class.d') false: any; + } + + @Component({template: '<span dir></span>'}) + class MyApp { + @ViewChild(MyDirective) dir!: MyDirective; + } + + TestBed.configureTestingModule({declarations: [MyApp, MyDirective]}); + const fixture = TestBed.createComponent(MyApp); + fixture.detectChanges(); + const span = fixture.nativeElement.querySelector('span'); + expect(span.className).toBe('a'); + + fixture.componentInstance.dir.true = 1; + fixture.componentInstance.dir.false = 2; + fixture.detectChanges(); + + expect(span.className).toBe('a c d'); + }); + + it('should handle host bindings with quoted names', () => { + @Directive({selector: '[dir]'}) + class MyDirective { + @HostBinding('class.a') 'is-a': any; + @HostBinding('class.b') 'is-"b"': any = true; + @HostBinding('class.c') '"is-c"': any; + } + + @Component({template: '<span dir></span>'}) + class MyApp { + @ViewChild(MyDirective) dir!: MyDirective; + } + + TestBed.configureTestingModule({declarations: [MyApp, MyDirective]}); + const fixture = TestBed.createComponent(MyApp); + fixture.detectChanges(); + const span = fixture.nativeElement.querySelector('span'); + expect(span.className).toBe('b'); + + fixture.componentInstance.dir['is-a'] = 1; + fixture.componentInstance.dir['"is-c"'] = 2; + fixture.detectChanges(); + + expect(span.className).toBe('b a c'); + }); + }); }); diff --git a/packages/core/test/acceptance/i18n_spec.ts b/packages/core/test/acceptance/i18n_spec.ts index 315e92d448..cc7fb156ce 100644 --- a/packages/core/test/acceptance/i18n_spec.ts +++ b/packages/core/test/acceptance/i18n_spec.ts @@ -1730,6 +1730,27 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { expect(titleDirInstances[0].title).toBe('Bonjour'); }); + it('should support static i18n attributes on inline templates', () => { + loadTranslations({[computeMsgId('Hello')]: 'Bonjour'}); + @Component({ + selector: 'my-cmp', + template: ` + <div *ngIf="true" i18n-title title="Hello"></div> + `, + }) + class Cmp { + } + + TestBed.configureTestingModule({ + imports: [CommonModule], + declarations: [Cmp], + }); + const fixture = TestBed.createComponent(Cmp); + fixture.detectChanges(); + + expect(fixture.nativeElement.firstChild.title).toBe('Bonjour'); + }); + it('should allow directive inputs (as an interpolated prop) on <ng-template>', () => { loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); diff --git a/packages/core/test/acceptance/integration_spec.ts b/packages/core/test/acceptance/integration_spec.ts index 21e0c0000a..bc4e911b9e 100644 --- a/packages/core/test/acceptance/integration_spec.ts +++ b/packages/core/test/acceptance/integration_spec.ts @@ -5,12 +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 {animate, AnimationEvent, state, style, transition, trigger} from '@angular/animations'; +import {AnimationDriver} from '@angular/animations/browser'; +import {MockAnimationDriver, MockAnimationPlayer} from '@angular/animations/browser/testing'; import {CommonModule} from '@angular/common'; import {Component, ContentChild, Directive, ElementRef, EventEmitter, HostBinding, HostListener, Input, NgModule, OnInit, Output, Pipe, QueryList, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core'; +import {Inject} from '@angular/core/src/di'; import {TVIEW} from '@angular/core/src/render3/interfaces/view'; import {getLView} from '@angular/core/src/render3/state'; import {ngDevModeResetPerfCounters} from '@angular/core/src/util/ng_dev_mode'; -import {TestBed} from '@angular/core/testing'; +import {fakeAsync, flushMicrotasks, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {ivyEnabled, onlyInIvy} from '@angular/private/testing'; @@ -2017,4 +2021,218 @@ describe('acceptance integration tests', () => { assertAttrValues(elements[2], 'post-update-pass'); }); }); + + describe('animations', () => { + it('should apply triggers for a list of items when they are sorted and reSorted', + fakeAsync(() => { + interface Item { + value: any; + id: number; + } + + @Component({ + template: ` + <div *ngIf="showWarningMessage; else listOfItems"> + Nooo! + </div> + + <ng-template #listOfItems> + <animation-comp *ngFor="let item of items; trackBy: itemTrackFn"> + {{ item.value }} + </animation-comp> + </ng-template> + ` + }) + class Cmp { + showWarningMessage = false; + + items: Item[] = [ + {value: 1, id: 1}, + {value: 2, id: 2}, + {value: 3, id: 3}, + {value: 4, id: 4}, + {value: 5, id: 5}, + ]; + + itemTrackFn(value: Item) { + return value.id; + } + } + + @Component({ + selector: 'animation-comp', + animations: [ + trigger( + 'host', + [ + state('void', style({height: '0px'})), + transition( + '* => *', + [ + animate('1s'), + ]), + ]), + ], + template: ` + <ng-content></ng-content> + ` + }) + class AnimationComp { + @HostBinding('@host') public hostState = ''; + + @HostListener('@host.start', ['$event']) + onLeaveStart(event: AnimationEvent) { + // we just want to register the listener + } + } + + TestBed.configureTestingModule({ + declarations: [Cmp, AnimationComp], + providers: [{provide: AnimationDriver, useClass: MockAnimationDriver}], + }); + const fixture = TestBed.createComponent(Cmp); + fixture.detectChanges(); + + let elements = queryAll(fixture.nativeElement, 'animation-comp'); + expect(elements.length).toEqual(5); + expect(elements.map(e => e.textContent?.trim())).toEqual(['1', '2', '3', '4', '5']); + + const items = fixture.componentInstance.items; + arraySwap(items, 2, 0); // 3 2 1 4 5 + arraySwap(items, 2, 1); // 3 1 2 4 5 + const first = items.shift()!; + items.push(first); // 1 2 4 5 3 + fixture.detectChanges(); + + elements = queryAll(fixture.nativeElement, 'animation-comp'); + expect(elements.length).toEqual(5); + expect(elements.map(e => e.textContent?.trim())).toEqual(['1', '2', '4', '5', '3']); + completeAnimations(); + + fixture.componentInstance.showWarningMessage = true; + fixture.detectChanges(); + completeAnimations(); + + elements = queryAll(fixture.nativeElement, 'animation-comp'); + expect(elements.length).toEqual(0); + expect(fixture.nativeElement.textContent.trim()).toEqual('Nooo!'); + + fixture.componentInstance.showWarningMessage = false; + fixture.detectChanges(); + + elements = queryAll(fixture.nativeElement, 'animation-comp'); + expect(elements.length).toEqual(5); + })); + + it('should insert and remove views in the correct order when animations are present', + fakeAsync(() => { + @Component({ + animations: [ + trigger('root', [transition('* => *', [])]), + trigger('outer', [transition('* => *', [])]), + trigger('inner', [transition('* => *', [])]), + ], + template: ` + <div *ngIf="showRoot" (@root.start)="track('root', $event)" @root> + <div *ngIf="showIfContents; else innerCompList" (@outer.start)="track('outer', $event)" @outer> + Nooo! + </div> + + <ng-template #innerCompList> + <inner-comp *ngFor="let item of items; trackBy: itemTrackFn" (@inner.start)="track('inner', $event)" @inner> + {{ item.value }} + </inner-comp> + </ng-template> + </div> + ` + }) + class Cmp { + showRoot = true; + showIfContents = true; + items = [1]; + log: string[] = []; + + track(name: string, event: AnimationEvent) { + this.log.push(name); + } + } + + @Component({ + selector: 'inner-comp', + animations: [ + trigger('host', [transition('* => *', [])]), + ], + template: ` + <ng-content></ng-content> + ` + }) + class InnerComp { + @HostBinding('@host') public hostState = ''; + + constructor(@Inject(Cmp) private parent: Cmp) {} + + @HostListener('@host.start', ['$event']) + onLeaveStart(event: AnimationEvent) { + this.parent.log.push('host'); + } + } + + TestBed.configureTestingModule({ + declarations: [Cmp, InnerComp], + providers: [{provide: AnimationDriver, useClass: MockAnimationDriver}], + }); + const fixture = TestBed.createComponent(Cmp); + fixture.detectChanges(); + completeAnimations(); + + const comp = fixture.componentInstance; + expect(comp.log).toEqual([ + 'root', // insertion of the inner-comp content + 'outer', // insertion of the default ngIf + ]); + + comp.log = []; + comp.showIfContents = false; + fixture.detectChanges(); + completeAnimations(); + + expect(comp.log).toEqual([ + 'host', // insertion of the inner-comp content + 'outer', // insertion of the template into the ngIf + 'inner' // insertion of the inner comp element + ]); + + comp.log = []; + comp.showRoot = false; + fixture.detectChanges(); + completeAnimations(); + + expect(comp.log).toEqual([ + 'root', // removal the root div container + 'host', // removal of the inner-comp content + 'inner' // removal of the inner comp element + ]); + })); + }); }); + +function completeAnimations() { + flushMicrotasks(); + const log = MockAnimationDriver.log as MockAnimationPlayer[]; + log.forEach(player => player.finish()); + flushMicrotasks(); +} + +function arraySwap(arr: any[], indexA: number, indexB: number): void { + const item = arr[indexA]; + arr[indexA] = arr[indexB]; + arr[indexB] = item; +} + +/** + * Queries the provided `root` element for sub elements by the selector and casts the result as an + * array of elements + */ +function queryAll(root: HTMLElement, selector: string): HTMLElement[] { + return Array.from(root.querySelectorAll(selector)); +} diff --git a/packages/core/test/acceptance/lifecycle_spec.ts b/packages/core/test/acceptance/lifecycle_spec.ts index d0ab6fec68..e322a78aeb 100644 --- a/packages/core/test/acceptance/lifecycle_spec.ts +++ b/packages/core/test/acceptance/lifecycle_spec.ts @@ -7,8 +7,7 @@ */ import {CommonModule} from '@angular/common'; -import {ChangeDetectorRef, Component, ComponentFactoryResolver, ContentChildren, Directive, Input, NgModule, OnChanges, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core'; -import {SimpleChange} from '@angular/core/src/core'; +import {AfterViewInit, ChangeDetectorRef, Component, ComponentFactoryResolver, ContentChildren, Directive, DoCheck, Input, NgModule, OnChanges, QueryList, SimpleChange, SimpleChanges, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {onlyInIvy} from '@angular/private/testing'; @@ -4378,4 +4377,72 @@ describe('non-regression', () => { expect(destroyed).toBeTruthy(); }); + + onlyInIvy('Use case is not supported in ViewEngine') + .it('should not throw when calling detectChanges from a setter in the presence of a data binding, ngOnChanges and ngAfterViewInit', + () => { + const hooks: string[] = []; + + @Directive({selector: '[testDir]'}) + class TestDirective implements OnChanges, AfterViewInit { + constructor(private _changeDetectorRef: ChangeDetectorRef) {} + + @Input('testDir') + set value(_value: any) { + this._changeDetectorRef.detectChanges(); + } + ngOnChanges() { + hooks.push('ngOnChanges'); + } + ngAfterViewInit() { + hooks.push('ngAfterViewInit'); + } + } + + @Component({template: `<div [testDir]="value">{{value}}</div>`}) + class App { + value = 1; + } + + TestBed.configureTestingModule({declarations: [App, TestDirective]}); + const fixture = TestBed.createComponent(App); + expect(() => fixture.detectChanges()).not.toThrow(); + expect(hooks).toEqual(['ngOnChanges', 'ngAfterViewInit']); + expect(fixture.nativeElement.textContent.trim()).toBe('1'); + }); + + onlyInIvy('Use case is not supported in ViewEngine') + .it('should call hooks in the correct order when calling detectChanges in a setter', () => { + const hooks: string[] = []; + + @Directive({selector: '[testDir]'}) + class TestDirective implements OnChanges, DoCheck, AfterViewInit { + constructor(private _changeDetectorRef: ChangeDetectorRef) {} + + @Input('testDir') + set value(_value: any) { + this._changeDetectorRef.detectChanges(); + } + ngOnChanges() { + hooks.push('ngOnChanges'); + } + ngDoCheck() { + hooks.push('ngDoCheck'); + } + ngAfterViewInit() { + hooks.push('ngAfterViewInit'); + } + } + + @Component({template: `<div [testDir]="value">{{value}}</div>`}) + class App { + value = 1; + } + + TestBed.configureTestingModule({declarations: [App, TestDirective]}); + const fixture = TestBed.createComponent(App); + expect(() => fixture.detectChanges()).not.toThrow(); + expect(hooks).toEqual(['ngOnChanges', 'ngDoCheck', 'ngAfterViewInit']); + expect(fixture.nativeElement.textContent.trim()).toBe('1'); + }); }); diff --git a/packages/core/test/acceptance/ng_module_spec.ts b/packages/core/test/acceptance/ng_module_spec.ts index c65218fd19..c5cf5c9bfd 100644 --- a/packages/core/test/acceptance/ng_module_spec.ts +++ b/packages/core/test/acceptance/ng_module_spec.ts @@ -380,7 +380,7 @@ describe('NgModule', () => { function createNgModule(Comp: any) { class Module { static ɵmod = defineNgModule({type: Module}); - static ɵinj = defineInjector({factory: () => new Module()}); + static ɵinj = defineInjector({}); } setClassMetadata( Module, [{ diff --git a/packages/core/test/acceptance/property_binding_spec.ts b/packages/core/test/acceptance/property_binding_spec.ts index ddb9a18ffe..fdf7ca9a4b 100644 --- a/packages/core/test/acceptance/property_binding_spec.ts +++ b/packages/core/test/acceptance/property_binding_spec.ts @@ -623,4 +623,26 @@ describe('property bindings', () => { fixture.detectChanges(); }).not.toThrow(); }); + + it('should allow quoted binding syntax inside property binding', () => { + @Component({template: `<span [id]="'{{ id }}'"></span>`}) + class Comp { + } + + TestBed.configureTestingModule({declarations: [Comp]}); + const fixture = TestBed.createComponent(Comp); + fixture.detectChanges(); + expect(fixture.nativeElement.querySelector('span').id).toBe('{{ id }}'); + }); + + it('should allow quoted binding syntax with escaped quotes inside property binding', () => { + @Component({template: `<span [id]="'{{ \\' }}'"></span>`}) + class Comp { + } + + TestBed.configureTestingModule({declarations: [Comp]}); + const fixture = TestBed.createComponent(Comp); + fixture.detectChanges(); + expect(fixture.nativeElement.querySelector('span').id).toBe('{{ \' }}'); + }); }); diff --git a/packages/core/test/acceptance/query_spec.ts b/packages/core/test/acceptance/query_spec.ts index 341f4f45c9..97de093d80 100644 --- a/packages/core/test/acceptance/query_spec.ts +++ b/packages/core/test/acceptance/query_spec.ts @@ -1123,6 +1123,46 @@ describe('query logic', () => { fixture.detectChanges(); expect(changes).toBe(1); }); + + it('should only fire if the content of the query changes', () => { + // When views are inserted/removed the content query need to be recomputed. + // Recomputing the query may result in no changes to the query (the item added/removed was + // not part of the query). This tests asserts that the query does not fire when no changes + // occur. + TestBed.configureTestingModule( + {declarations: [QueryCompWithStrictChangeEmitParent, QueryCompWithNoChanges]}); + const fixture = TestBed.createComponent(QueryCompWithNoChanges); + let changesStrict = 0; + const componentInstance = fixture.componentInstance.queryComp; + fixture.detectChanges(); + + componentInstance.foos.changes.subscribe((value: any) => { + // subscribe to the changes and record when changes occur. + changesStrict += 1; + }); + + // First verify that the subscription is working. + fixture.componentInstance.innerShowing = false; + fixture.detectChanges(); + expect(changesStrict).toBe(1); // We detected a change + expect(componentInstance.foos.toArray().length).toEqual(1); + + + // now verify that removing a view does not needlessly fire subscription + fixture.componentInstance.showing = false; + fixture.detectChanges(); + expect(changesStrict).toBe(1); // We detected a change + expect(componentInstance.foos.toArray().length).toEqual(1); + + // now verify that adding a view does not needlessly fire subscription + fixture.componentInstance.showing = true; + fixture.detectChanges(); + expect(changesStrict).toBe(1); // We detected a change + // Note: even though the `showing` is `true` and the second `<div>` is displayed, the + // child element of that <div> is hidden because the `innerShowing` flag is still `false`, + // so we expect only one element to be present in the `foos` array. + expect(componentInstance.foos.toArray().length).toEqual(1); + }); }); describe('view boundaries', () => { @@ -1217,11 +1257,8 @@ describe('query logic', () => { * - systematically detach and insert a view - this would result in unnecessary processing * when the previous and new indexes for the move operation are the same; * - detect the situation where the indexes are the same and do no processing in such case. - * - * This tests asserts on the implementation choices done by the VE (detach and insert) so we - * can replicate the same behaviour in ivy. */ - it('should notify on changes when a given view is removed and re-inserted at the same index', + it('should NOT notify on changes when a given view is removed and re-inserted at the same index', () => { @Component({ selector: 'test-comp', @@ -1258,7 +1295,7 @@ describe('query logic', () => { vc.move(viewRef, 0); fixture.detectChanges(); expect(queryList.length).toBe(1); - expect(fixture.componentInstance.queryListNotificationCounter).toBe(2); + expect(fixture.componentInstance.queryListNotificationCounter).toBe(1); }); it('should support a mix of content queries from the declaration and embedded view', () => { @@ -1964,6 +2001,37 @@ export class QueryCompWithChanges { showing = false; } +@Component({ + selector: 'query-with-no-changes', + template: ` + <query-component> + <div *ngIf="true" #foo></div> + <div *ngIf="showing"> + Showing me should not change the content of the query + <div *ngIf="innerShowing" #foo></div> + </div> + </query-component> + ` +}) +export class QueryCompWithNoChanges { + showing: boolean = true; + innerShowing: boolean = true; + queryComp!: QueryCompWithStrictChangeEmitParent; +} + +@Component({selector: 'query-component', template: `<ng-content></ng-content>`}) +export class QueryCompWithStrictChangeEmitParent { + @ContentChildren('foo', { + descendants: true, + emitDistinctChangesOnly: true, + }) + foos!: QueryList<any>; + + constructor(public queryCompWithNoChanges: QueryCompWithNoChanges) { + queryCompWithNoChanges.queryComp = this; + } +} + @Component({selector: 'query-target', template: '<ng-content></ng-content>'}) class SuperDirectiveQueryTarget { } diff --git a/packages/core/test/acceptance/styling_spec.ts b/packages/core/test/acceptance/styling_spec.ts index e6ac22da21..2013613a9a 100644 --- a/packages/core/test/acceptance/styling_spec.ts +++ b/packages/core/test/acceptance/styling_spec.ts @@ -17,6 +17,40 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; import {ivyEnabled, modifiedInIvy, onlyInIvy} from '@angular/private/testing'; describe('styling', () => { + /** + * This helper function tests to see if the current browser supports non standard way of writing + * into styles. + * + * This is not the correct way to write to style and is not supported in IE11. + * ``` + * div.style = 'color: white'; + * ``` + * + * This is the correct way to write to styles: + * ``` + * div.style.cssText = 'color: white'; + * ``` + * + * Even though writing to `div.style` is not officially supported, it works in all + * browsers except IE11. + * + * This function detects this condition and allows us to skip affected tests. + */ + let _supportsWritingStringsToStyleProperty: boolean|null = null; + function supportsWritingStringsToStyleProperty() { + if (_supportsWritingStringsToStyleProperty === null) { + const div = document.createElement('div'); + const CSS = 'color: white;'; + try { + (div as any).style = CSS; + } catch (e) { + _supportsWritingStringsToStyleProperty = false; + } + _supportsWritingStringsToStyleProperty = (div.style.cssText === CSS); + } + return _supportsWritingStringsToStyleProperty; + } + beforeEach(ngDevModeResetPerfCounters); describe('apply in prioritization order', () => { @@ -260,6 +294,29 @@ describe('styling', () => { const header = fixture.nativeElement.querySelector('h1') as HTMLElement; expect(getComputedStyle(header).getPropertyValue('width')).toEqual('100px'); }); + + it('should support case-sensitive css variables', () => { + // This test only works in browsers which support CSS variables. + if (!supportsCssVariables) { + return; + } + + @Component({ + template: ` + <div [style.--MyVar]="'100px'"> + <span style="width: var(--MyVar)">CONTENT</span> + </div> + ` + }) + class Cmp { + } + TestBed.configureTestingModule({declarations: [Cmp]}); + const fixture = TestBed.createComponent(Cmp); + fixture.detectChanges(); + + const span = fixture.nativeElement.querySelector('span') as HTMLElement; + expect(getComputedStyle(span).getPropertyValue('width')).toEqual('100px'); + }); }); modifiedInIvy('shadow bindings include static portion') @@ -3069,6 +3126,64 @@ describe('styling', () => { expect(fixture.debugElement.nativeElement.innerHTML).toContain('three'); }); + it('should allow static and bound `class` attribute, but use last occurrence', () => { + @Component({ + template: ` + <div id="first" class="zero {{one}}" [class]="'two'"></div> + <div id="second" [class]="'two'" class="zero {{one}}"></div> + `, + }) + class MyComp { + one = 'one'; + } + + TestBed.configureTestingModule({declarations: [MyComp]}); + const fixture = TestBed.createComponent(MyComp); + fixture.detectChanges(); + + const first = fixture.nativeElement.querySelector('#first').outerHTML; + expect(first).not.toContain('zero'); + expect(first).not.toContain('one'); + expect(first).toContain('two'); + + const second = fixture.nativeElement.querySelector('#second').outerHTML; + expect(second).toContain('zero'); + expect(second).toContain('one'); + expect(second).not.toContain('two'); + }); + + it('should allow static and bound `style` attribute, but use last occurrence', () => { + if (!ivyEnabled && !supportsWritingStringsToStyleProperty()) { + // VE does not treat `[style]` as anything special, instead it simply writes to the + // `style` property on the element like so `element.style=value`. This seems to work fine + // every where except IE11, where it throws an error and as a consequence this test fails in + // VE on IE11. + return; + } + + @Component({ + template: ` + <div id="first" style="margin: {{margin}}" [style]="'padding: 20px;'"></div> + <div id="second" [style]="'padding: 20px;'" style="margin: {{margin}}"></div> + `, + }) + class MyComp { + margin = '10px'; + } + + TestBed.configureTestingModule({declarations: [MyComp]}); + const fixture = TestBed.createComponent(MyComp); + fixture.detectChanges(); + + const first = fixture.nativeElement.querySelector('#first').outerHTML; + expect(first).not.toContain('margin'); + expect(first).toContain('padding'); + + const second = fixture.nativeElement.querySelector('#second').outerHTML; + expect(second).toContain('margin'); + expect(second).not.toContain('padding'); + }); + it('should allow to reset style property value defined using [style.prop.px] binding', () => { @Component({ template: '<div [style.left.px]="left"></div>', @@ -3625,35 +3740,6 @@ describe('styling', () => { expectStyle(div).toEqual({color: 'white', display: 'block'}); }); - /** - * Tests to see if the current browser supports non standard way of writing into styles. - * - * This is not the correct way to write to style and is not supported in IE11. - * ``` - * div.style = 'color: white'; - * ``` - * - * This is the correct way to write to styles: - * ``` - * div.style.cssText = 'color: white'; - * ``` - * - * Even though writing to `div.style` is not officially supported, it works in all - * browsers except IE11. - * - * This function detects this condition and allows us to skip the test. - */ - function supportsWritingStringsToStyleProperty() { - const div = document.createElement('div'); - const CSS = 'color: white;'; - try { - (div as any).style = CSS; - } catch (e) { - return false; - } - return div.style.cssText === CSS; - } - onlyInIvy('styling priority resolution is Ivy only feature.') .it('should allow lookahead binding on second pass #35118', () => { @Component({ diff --git a/packages/core/test/acceptance/template_ref_spec.ts b/packages/core/test/acceptance/template_ref_spec.ts index ab14c0c765..84ed32d582 100644 --- a/packages/core/test/acceptance/template_ref_spec.ts +++ b/packages/core/test/acceptance/template_ref_spec.ts @@ -273,4 +273,87 @@ describe('TemplateRef', () => { }); }); }); + + describe('context', () => { + @Component({ + template: ` + <ng-template #templateRef let-name="name">{{name}}</ng-template> + <ng-container #containerRef></ng-container> + ` + }) + class App { + @ViewChild('templateRef') templateRef!: TemplateRef<any>; + @ViewChild('containerRef', {read: ViewContainerRef}) containerRef!: ViewContainerRef; + } + + it('should update if the context of a view ref is mutated', () => { + TestBed.configureTestingModule({declarations: [App]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + const context = {name: 'Frodo'}; + const viewRef = fixture.componentInstance.templateRef.createEmbeddedView(context); + fixture.componentInstance.containerRef.insert(viewRef); + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('Frodo'); + + context.name = 'Bilbo'; + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('Bilbo'); + }); + + it('should update if the context of a view ref is replaced', () => { + TestBed.configureTestingModule({declarations: [App]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + const viewRef = fixture.componentInstance.templateRef.createEmbeddedView({name: 'Frodo'}); + fixture.componentInstance.containerRef.insert(viewRef); + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('Frodo'); + + viewRef.context = {name: 'Bilbo'}; + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('Bilbo'); + }); + + it('should use the latest context information inside template listeners', () => { + const events: string[] = []; + + @Component({ + template: ` + <ng-template #templateRef let-name="name"> + <button (click)="log(name)"></button> + </ng-template> + <ng-container #containerRef></ng-container> + ` + }) + class ListenerTest { + @ViewChild('templateRef') templateRef!: TemplateRef<any>; + @ViewChild('containerRef', {read: ViewContainerRef}) containerRef!: ViewContainerRef; + + log(name: string) { + events.push(name); + } + } + + TestBed.configureTestingModule({declarations: [ListenerTest]}); + const fixture = TestBed.createComponent(ListenerTest); + fixture.detectChanges(); + const viewRef = fixture.componentInstance.templateRef.createEmbeddedView({name: 'Frodo'}); + fixture.componentInstance.containerRef.insert(viewRef); + fixture.detectChanges(); + + const button = fixture.nativeElement.querySelector('button'); + button.click(); + expect(events).toEqual(['Frodo']); + + viewRef.context = {name: 'Bilbo'}; + fixture.detectChanges(); + button.click(); + expect(events).toEqual(['Frodo', 'Bilbo']); + }); + }); }); diff --git a/packages/core/test/acceptance/text_spec.ts b/packages/core/test/acceptance/text_spec.ts index 7584c1e5e1..952006c5a9 100644 --- a/packages/core/test/acceptance/text_spec.ts +++ b/packages/core/test/acceptance/text_spec.ts @@ -129,4 +129,60 @@ describe('text instructions', () => { expect(div.innerHTML).toBe('function foo() { }'); }); + + it('should stringify an object using its toString method', () => { + class TestObject { + toString() { + return 'toString'; + } + valueOf() { + return 'valueOf'; + } + } + + @Component({template: '{{object}}'}) + class App { + object = new TestObject(); + } + + TestBed.configureTestingModule({declarations: [App]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('toString'); + }); + + it('should stringify a symbol', () => { + // This test is only valid on browsers that support Symbol. + if (typeof Symbol === 'undefined') { + return; + } + + @Component({template: '{{symbol}}'}) + class App { + symbol = Symbol('hello'); + } + + TestBed.configureTestingModule({declarations: [App]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + // Note that this uses `toContain`, because a polyfilled `Symbol` produces something like + // `Symbol(hello)_p.sc8s398cplk`, whereas the native one is `Symbol(hello)`. + expect(fixture.nativeElement.textContent).toContain('Symbol(hello)'); + }); + + it('should handle binding syntax used inside quoted text', () => { + @Component({ + template: `{{'Interpolations look like {{this}}'}}`, + }) + class App { + } + + TestBed.configureTestingModule({declarations: [App]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('Interpolations look like {{this}}'); + }); }); diff --git a/packages/core/test/acceptance/view_ref_spec.ts b/packages/core/test/acceptance/view_ref_spec.ts index 21b82a17fb..3c3f6bd8c1 100644 --- a/packages/core/test/acceptance/view_ref_spec.ts +++ b/packages/core/test/acceptance/view_ref_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ApplicationRef, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, NgModule} from '@angular/core'; +import {ApplicationRef, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, ElementRef, EmbeddedViewRef, Injector, NgModule, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core'; import {InternalViewRef} from '@angular/core/src/linker/view_ref'; import {TestBed} from '@angular/core/testing'; @@ -72,4 +72,80 @@ describe('ViewRef', () => { expect(called).toBe(true); }); + + it('should remove view ref from view container when destroyed', () => { + @Component({template: ''}) + class DynamicComponent { + constructor(public viewContainerRef: ViewContainerRef) {} + } + + @Component({template: `<ng-template>Hello</ng-template>`}) + class App { + @ViewChild(TemplateRef) templateRef!: TemplateRef<any>; + componentRef!: ComponentRef<DynamicComponent>; + viewRef!: EmbeddedViewRef<any>; + constructor( + private _viewContainerRef: ViewContainerRef, + private _componentFactoryResolver: ComponentFactoryResolver) {} + + create() { + const factory = this._componentFactoryResolver.resolveComponentFactory(DynamicComponent); + this.viewRef = this.templateRef.createEmbeddedView(null); + this.componentRef = this._viewContainerRef.createComponent(factory); + this.componentRef.instance.viewContainerRef.insert(this.viewRef); + } + } + + @NgModule({declarations: [App, DynamicComponent], entryComponents: [DynamicComponent]}) + class MyTestModule { + } + + TestBed.configureTestingModule({imports: [MyTestModule]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + fixture.componentInstance.create(); + const viewContainerRef = fixture.componentInstance.componentRef.instance.viewContainerRef; + + expect(viewContainerRef.length).toBe(1); + fixture.componentInstance.viewRef.destroy(); + expect(viewContainerRef.length).toBe(0); + }); + + it('should mark a ViewRef as destroyed when the host view is destroyed', () => { + @Component({template: ''}) + class DynamicComponent { + constructor(public viewContainerRef: ViewContainerRef) {} + } + + @Component({template: `<ng-template>Hello</ng-template>`}) + class App { + @ViewChild(TemplateRef) templateRef!: TemplateRef<any>; + componentRef!: ComponentRef<DynamicComponent>; + viewRef!: EmbeddedViewRef<any>; + constructor( + private _viewContainerRef: ViewContainerRef, + private _componentFactoryResolver: ComponentFactoryResolver) {} + + create() { + const factory = this._componentFactoryResolver.resolveComponentFactory(DynamicComponent); + this.viewRef = this.templateRef.createEmbeddedView(null); + this.componentRef = this._viewContainerRef.createComponent(factory); + this.componentRef.instance.viewContainerRef.insert(this.viewRef); + } + } + + @NgModule({declarations: [App, DynamicComponent], entryComponents: [DynamicComponent]}) + class MyTestModule { + } + + TestBed.configureTestingModule({imports: [MyTestModule]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + fixture.componentInstance.create(); + const {componentRef, viewRef} = fixture.componentInstance; + + expect(viewRef.destroyed).toBe(false); + componentRef.hostView.destroy(); + expect(viewRef.destroyed).toBe(true); + }); }); diff --git a/packages/core/test/animation/animation_integration_spec.ts b/packages/core/test/animation/animation_integration_spec.ts index 02f5704682..99338410d5 100644 --- a/packages/core/test/animation/animation_integration_spec.ts +++ b/packages/core/test/animation/animation_integration_spec.ts @@ -8,7 +8,6 @@ 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 {fakeAsync, flushMicrotasks, TestBed} from '@angular/core/testing'; import {ɵDomRendererFactory2} from '@angular/platform-browser'; @@ -47,8 +46,7 @@ describe('animation tests', function() { {declarations: [SharedAnimationCmp], imports: [BrowserAnimationsModule]}); const fixture = TestBed.createComponent(SharedAnimationCmp); - const cmp = fixture.componentInstance; - expect(cmp.animationType).toEqual('BrowserAnimations'); + expect(fixture.componentInstance.animationType).toEqual('BrowserAnimations'); }); it('should hint at NoopAnimationsModule being used', () => { @@ -57,9 +55,20 @@ describe('animation tests', function() { {declarations: [SharedAnimationCmp], imports: [NoopAnimationsModule]}); const fixture = TestBed.createComponent(SharedAnimationCmp); - const cmp = fixture.componentInstance; - expect(cmp.animationType).toEqual('NoopAnimations'); + expect(fixture.componentInstance.animationType).toEqual('NoopAnimations'); }); + + it('should hint at NoopAnimationsModule being used when BrowserAnimationsModule is provided with disabled animations', + () => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + declarations: [SharedAnimationCmp], + imports: [BrowserAnimationsModule.withConfig({disableAnimations: true})] + }); + + const fixture = TestBed.createComponent(SharedAnimationCmp); + expect(fixture.componentInstance.animationType).toEqual('NoopAnimations'); + }); }); @Component({template: '<p>template text</p>'}) diff --git a/packages/core/test/application_init_spec.ts b/packages/core/test/application_init_spec.ts index ee078a606e..d36e6df8fb 100644 --- a/packages/core/test/application_init_spec.ts +++ b/packages/core/test/application_init_spec.ts @@ -5,73 +5,154 @@ * 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 {Injector} from '@angular/core'; -import {APP_INITIALIZER, ApplicationInitStatus} from '@angular/core/src/application_init'; +import {ApplicationInitStatus} from '@angular/core/src/application_init'; +import {EMPTY, Observable, Subscriber} from 'rxjs'; -import {inject, TestBed, waitForAsync} from '../testing'; +describe('ApplicationInitStatus', () => { + let status: ApplicationInitStatus; + const runInitializers = () => + // Cast to `any` to access an internal function for testing purposes. + (status as any).runInitializers(); -{ - describe('ApplicationInitStatus', () => { - describe('no initializers', () => { - it('should return true for `done`', - waitForAsync(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => { - (status as any).runInitializers(); - expect(status.done).toBe(true); - }))); - - it('should return a promise that resolves immediately for `donePromise`', - waitForAsync(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => { - (status as any).runInitializers(); - status.donePromise.then(() => { - expect(status.done).toBe(true); - }); - }))); + describe('no initializers', () => { + beforeEach(() => { + status = new ApplicationInitStatus([]); }); - describe('with async initializers', () => { - let resolve: (result: any) => void; - let promise: Promise<any>; - let completerResolver = false; - beforeEach(() => { - let initializerFactory = (injector: Injector) => { - return () => { - const initStatus = injector.get(ApplicationInitStatus); - initStatus.donePromise.then(() => { - expect(completerResolver).toBe(true); - }); - }; - }; - promise = new Promise((res) => { - resolve = res; - }); - TestBed.configureTestingModule({ - providers: [ - {provide: APP_INITIALIZER, multi: true, useValue: () => promise}, - { - provide: APP_INITIALIZER, - multi: true, - useFactory: initializerFactory, - deps: [Injector] - }, - ] - }); - }); + it('should return true for `done`', () => { + runInitializers(); + expect(status.done).toBe(true); + }); - it('should update the status once all async initializers are done', - waitForAsync(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => { - (status as any).runInitializers(); - - setTimeout(() => { - completerResolver = true; - resolve(null); - }); - - expect(status.done).toBe(false); - status.donePromise.then(() => { - expect(status.done).toBe(true); - expect(completerResolver).toBe(true); - }); - }))); + it('should return a promise that resolves immediately for `donePromise`', async () => { + runInitializers(); + await status.donePromise; + expect(status.done).toBe(true); }); }); -} + + describe('with async promise initializers', () => { + let resolve: (result: any) => void; + let reject: (reason?: any) => void; + let promise: Promise<any>; + let initFnInvoked = false; + + beforeEach(() => { + promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + status = new ApplicationInitStatus([() => promise]); + }); + + it('should update the status once all async promise initializers are done', async () => { + runInitializers(); + + setTimeout(() => { + initFnInvoked = true; + resolve(null); + }); + + expect(status.done).toBe(false); + await status.donePromise; + expect(status.done).toBe(true); + expect(initFnInvoked).toBe(true); + }); + + it('should handle a case when promise is rejected', async () => { + runInitializers(); + + setTimeout(() => { + initFnInvoked = true; + reject(); + }); + + expect(status.done).toBe(false); + try { + await status.donePromise; + fail('donePromise should have been rejected when promise is rejected'); + } catch { + expect(status.done).toBe(false); + expect(initFnInvoked).toBe(true); + } + }); + }); + + describe('with app initializers represented using observables', () => { + let subscriber: Subscriber<any>; + let initFnInvoked = false; + beforeEach(() => { + const observable = new Observable((res) => { + subscriber = res; + }); + status = new ApplicationInitStatus([() => observable]); + }); + + it('should update the status once all async observable initializers are completed', + async () => { + runInitializers(); + + setTimeout(() => { + initFnInvoked = true; + subscriber.complete(); + }); + + expect(status.done).toBe(false); + await status.donePromise; + expect(status.done).toBe(true); + expect(initFnInvoked).toBe(true); + }); + + it('should update the status once all async observable initializers emitted and completed', + async () => { + runInitializers(); + + subscriber.next('one'); + subscriber.next('two'); + + setTimeout(() => { + initFnInvoked = true; + subscriber.complete(); + }); + + await status.donePromise; + expect(status.done).toBe(true); + expect(initFnInvoked).toBe(true); + }); + + it('should update the status if all async observable initializers are completed synchronously', + async () => { + // Create a status instance using an initializer that returns the `EMPTY` Observable + // which completes synchronously upon subscription. + status = new ApplicationInitStatus([() => EMPTY]); + + runInitializers(); + + // Although the Observable completes synchronously, we still queue a promise for + // simplicity. This means that the `done` flag will not be `true` immediately, even + // though there was not actually any asynchronous activity. + expect(status.done).toBe(false); + + await status.donePromise; + expect(status.done).toBe(true); + }); + + it('should handle a case when observable emits an error', async () => { + runInitializers(); + + setTimeout(() => { + initFnInvoked = true; + subscriber.error(); + }); + + expect(status.done).toBe(false); + try { + await status.donePromise; + fail('donePromise should have been rejected when observable emits an error'); + } catch { + expect(status.done).toBe(false); + expect(initFnInvoked).toBe(true); + } + }); + }); +}); diff --git a/packages/core/test/application_ref_spec.ts b/packages/core/test/application_ref_spec.ts index 2f97e91745..b3b6fa09ed 100644 --- a/packages/core/test/application_ref_spec.ts +++ b/packages/core/test/application_ref_spec.ts @@ -59,7 +59,7 @@ class SomeComponent { const errorHandler = new ErrorHandler(); (errorHandler as any)._console = mockConsole as any; - const platformModule = getDOM().supportsDOMEvents() ? + const platformModule = getDOM().supportsDOMEvents ? BrowserModule : require('@angular/platform-server').ServerModule; 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 d4f067f254..dd29afd07a 100644 --- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json +++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json @@ -368,6 +368,12 @@ { "name": "ɵɵdefineComponent" }, + { + "name": "ɵɵdefineInjector" + }, + { + "name": "ɵɵdefineNgModule" + }, { "name": "ɵɵelementStart" } diff --git a/packages/core/test/bundling/forms/BUILD.bazel b/packages/core/test/bundling/forms/BUILD.bazel deleted file mode 100644 index 5c0930c0d9..0000000000 --- a/packages/core/test/bundling/forms/BUILD.bazel +++ /dev/null @@ -1,85 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load("//tools:defaults.bzl", "jasmine_node_test", "ng_module", "ng_rollup_bundle", "ts_library") -load("//tools/symbol-extractor:index.bzl", "js_expected_symbol_test") -load("@npm//http-server:index.bzl", "http_server") - -ng_module( - name = "forms", - srcs = ["index.ts"], - tags = [ - "ivy-only", - ], - deps = [ - "//packages/core", - "//packages/forms", - "//packages/platform-browser", - ], -) - -ng_rollup_bundle( - name = "bundle", - entry_point = ":index.ts", - tags = [ - "ivy-only", - ], - deps = [ - ":forms", - "//packages/core", - "//packages/forms", - "//packages/platform-browser", - "@npm//rxjs", - ], -) - -ts_library( - name = "test_lib", - testonly = True, - srcs = glob(["*_spec.ts"]), - tags = [ - "ivy-only", - ], - deps = [ - "//packages:types", - "//packages/compiler", - "//packages/core", - "//packages/core/testing", - "//packages/private/testing", - ], -) - -jasmine_node_test( - name = "test", - data = [ - ":bundle.js", - ":bundle.min.js", - ":bundle.min.js.br", - ":bundle.min_debug.js", - ], - tags = [ - "ivy-only", - ], - deps = [":test_lib"], -) - -js_expected_symbol_test( - name = "symbol_test", - src = ":bundle.min_debug.js", - golden = ":bundle.golden_symbols.json", - tags = [ - "ivy-aot", - "ivy-only", - ], -) - -http_server( - name = "prodserver", - data = [ - "index.html", - ":bundle.min.js", - ":bundle.min_debug.js", - ], - tags = [ - "ivy-only", - ], -) diff --git a/packages/core/test/bundling/forms/bundle.golden_symbols.json b/packages/core/test/bundling/forms/bundle.golden_symbols.json deleted file mode 100644 index 8535a95744..0000000000 --- a/packages/core/test/bundling/forms/bundle.golden_symbols.json +++ /dev/null @@ -1,1736 +0,0 @@ -[ - { - "name": "ALLOW_MULTIPLE_PLATFORMS" - }, - { - "name": "APPLICATION_MODULE_PROVIDERS" - }, - { - "name": "APP_BOOTSTRAP_LISTENER" - }, - { - "name": "APP_ID" - }, - { - "name": "APP_ID_RANDOM_PROVIDER" - }, - { - "name": "APP_INITIALIZER" - }, - { - "name": "AbstractControl" - }, - { - "name": "AbstractControlDirective" - }, - { - "name": "AbstractControlStatus" - }, - { - "name": "AbstractFormGroupDirective" - }, - { - "name": "AnonymousSubject" - }, - { - "name": "ApplicationInitStatus" - }, - { - "name": "ApplicationModule" - }, - { - "name": "ApplicationRef" - }, - { - "name": "BROWSER_MODULE_PROVIDERS" - }, - { - "name": "BUILTIN_ACCESSORS" - }, - { - "name": "BrowserDomAdapter" - }, - { - "name": "BrowserGetTestability" - }, - { - "name": "BrowserModule" - }, - { - "name": "CHECKBOX_VALUE_ACCESSOR" - }, - { - "name": "CIRCULAR" - }, - { - "name": "CLEAN_PROMISE" - }, - { - "name": "COMPONENT_REGEX" - }, - { - "name": "COMPOSITION_BUFFER_MODE" - }, - { - "name": "ChangeDetectionStrategy" - }, - { - "name": "CheckboxControlValueAccessor" - }, - { - "name": "CommonModule" - }, - { - "name": "Compiler" - }, - { - "name": "Compiler_compileModuleAndAllComponentsAsync" - }, - { - "name": "Compiler_compileModuleAndAllComponentsSync" - }, - { - "name": "Compiler_compileModuleAndAllComponentsSync__POST_R3__" - }, - { - "name": "Compiler_compileModuleAsync" - }, - { - "name": "Compiler_compileModuleSync" - }, - { - "name": "Compiler_compileModuleSync__POST_R3__" - }, - { - "name": "ComponentFactory" - }, - { - "name": "ComponentFactory" - }, - { - "name": "ComponentFactoryResolver" - }, - { - "name": "ComponentFactoryResolver" - }, - { - "name": "ComponentRef" - }, - { - "name": "ConnectableObservable" - }, - { - "name": "ConnectableSubscriber" - }, - { - "name": "Console" - }, - { - "name": "ControlContainer" - }, - { - "name": "DEFAULT_CURRENCY_CODE" - }, - { - "name": "DEFAULT_VALUE_ACCESSOR" - }, - { - "name": "DOCUMENT" - }, - { - "name": "DOCUMENT" - }, - { - "name": "DefaultDomRenderer2" - }, - { - "name": "DefaultIterableDiffer" - }, - { - "name": "DefaultIterableDifferFactory" - }, - { - "name": "DefaultKeyValueDiffer" - }, - { - "name": "DefaultKeyValueDifferFactory" - }, - { - "name": "DefaultValueAccessor" - }, - { - "name": "DomEventsPlugin" - }, - { - "name": "DomRendererFactory2" - }, - { - "name": "DomSharedStylesHost" - }, - { - "name": "EMAIL_REGEXP" - }, - { - "name": "EMPTY_ARRAY" - }, - { - "name": "EMPTY_ARRAY" - }, - { - "name": "EMPTY_ARRAY" - }, - { - "name": "EMPTY_OBJ" - }, - { - "name": "EMPTY_PAYLOAD" - }, - { - "name": "EVENT_MANAGER_PLUGINS" - }, - { - "name": "ElementRef" - }, - { - "name": "EmulatedEncapsulationDomRenderer2" - }, - { - "name": "ErrorHandler" - }, - { - "name": "EventEmitter" - }, - { - "name": "EventManager" - }, - { - "name": "EventManagerPlugin" - }, - { - "name": "FormArray" - }, - { - "name": "FormArrayName" - }, - { - "name": "FormBuilder" - }, - { - "name": "FormControl" - }, - { - "name": "FormControlName" - }, - { - "name": "FormGroup" - }, - { - "name": "FormGroupDirective" - }, - { - "name": "FormGroupName" - }, - { - "name": "FormsExampleModule" - }, - { - "name": "FormsModule" - }, - { - "name": "INJECTOR" - }, - { - "name": "INJECTOR_IMPL" - }, - { - "name": "INJECTOR_SCOPE" - }, - { - "name": "Inject" - }, - { - "name": "InjectFlags" - }, - { - "name": "InjectionToken" - }, - { - "name": "Injector" - }, - { - "name": "InnerSubscriber" - }, - { - "name": "IterableChangeRecord_" - }, - { - "name": "IterableDiffers" - }, - { - "name": "KeyEventsPlugin" - }, - { - "name": "KeyValueChangeRecord_" - }, - { - "name": "KeyValueDiffers" - }, - { - "name": "LOCALE_DATA" - }, - { - "name": "LOCALE_ID" - }, - { - "name": "LOCALE_ID" - }, - { - "name": "LifecycleHooksFeature" - }, - { - "name": "LocaleDataIndex" - }, - { - "name": "MODIFIER_KEYS" - }, - { - "name": "MODIFIER_KEY_GETTERS" - }, - { - "name": "MapOperator" - }, - { - "name": "MapSubscriber" - }, - { - "name": "MergeMapOperator" - }, - { - "name": "MergeMapSubscriber" - }, - { - "name": "ModuleWithComponentFactories" - }, - { - "name": "NAMESPACE_URIS" - }, - { - "name": "NEW_LINE" - }, - { - "name": "NG_ASYNC_VALIDATORS" - }, - { - "name": "NG_COMP_DEF" - }, - { - "name": "NG_DIR_DEF" - }, - { - "name": "NG_ELEMENT_ID" - }, - { - "name": "NG_FACTORY_DEF" - }, - { - "name": "NG_INJECTABLE_DEF" - }, - { - "name": "NG_INJECTOR_DEF" - }, - { - "name": "NG_INJ_DEF" - }, - { - "name": "NG_LOC_ID_DEF" - }, - { - "name": "NG_MODEL_WITH_FORM_CONTROL_WARNING" - }, - { - "name": "NG_MOD_DEF" - }, - { - "name": "NG_PIPE_DEF" - }, - { - "name": "NG_PROV_DEF" - }, - { - "name": "NG_VALIDATORS" - }, - { - "name": "NG_VALUE_ACCESSOR" - }, - { - "name": "NOT_FOUND" - }, - { - "name": "NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR" - }, - { - "name": "NOT_YET" - }, - { - "name": "NO_CHANGE" - }, - { - "name": "NULL_INJECTOR" - }, - { - "name": "NUMBER_VALUE_ACCESSOR" - }, - { - "name": "NgControl" - }, - { - "name": "NgControlStatus" - }, - { - "name": "NgControlStatusGroup" - }, - { - "name": "NgForOf" - }, - { - "name": "NgForOfContext" - }, - { - "name": "NgForm" - }, - { - "name": "NgLocaleLocalization" - }, - { - "name": "NgLocalization" - }, - { - "name": "NgModel" - }, - { - "name": "NgModelGroup" - }, - { - "name": "NgModuleFactory" - }, - { - "name": "NgModuleRef" - }, - { - "name": "NgModuleRef" - }, - { - "name": "NgOnChangesFeatureImpl" - }, - { - "name": "NgZone" - }, - { - "name": "NodeInjector" - }, - { - "name": "NodeInjectorFactory" - }, - { - "name": "NoopNgZone" - }, - { - "name": "NullInjector" - }, - { - "name": "NumberValueAccessor" - }, - { - "name": "ObjectUnsubscribedError" - }, - { - "name": "Observable" - }, - { - "name": "Optional" - }, - { - "name": "OuterSubscriber" - }, - { - "name": "PLATFORM_ID" - }, - { - "name": "PLATFORM_INITIALIZER" - }, - { - "name": "PlatformRef" - }, - { - "name": "Plural" - }, - { - "name": "R3Injector" - }, - { - "name": "R3TemplateRef" - }, - { - "name": "R3ViewContainerRef" - }, - { - "name": "RADIO_VALUE_ACCESSOR" - }, - { - "name": "RANGE_VALUE_ACCESSOR" - }, - { - "name": "REQUIRED_VALIDATOR" - }, - { - "name": "RadioControlRegistry" - }, - { - "name": "RadioControlValueAccessor" - }, - { - "name": "RangeValueAccessor" - }, - { - "name": "ReactiveFormsComponent" - }, - { - "name": "ReactiveFormsComponent_div_14_Template" - }, - { - "name": "ReactiveFormsModule" - }, - { - "name": "RecordViewTuple" - }, - { - "name": "RefCountOperator" - }, - { - "name": "RefCountSubscriber" - }, - { - "name": "Renderer2" - }, - { - "name": "RendererFactory2" - }, - { - "name": "RendererStyleFlags2" - }, - { - "name": "RequiredValidator" - }, - { - "name": "RootComponent" - }, - { - "name": "RootViewRef" - }, - { - "name": "RuntimeError" - }, - { - "name": "SCHEDULER" - }, - { - "name": "SELECT_MULTIPLE_VALUE_ACCESSOR" - }, - { - "name": "SELECT_VALUE_ACCESSOR" - }, - { - "name": "SERVER_TRANSITION_PROVIDERS" - }, - { - "name": "SWITCH_ELEMENT_REF_FACTORY" - }, - { - "name": "SWITCH_RENDERER2_FACTORY" - }, - { - "name": "SWITCH_TEMPLATE_REF_FACTORY" - }, - { - "name": "SWITCH_VIEW_CONTAINER_REF_FACTORY" - }, - { - "name": "SafeSubscriber" - }, - { - "name": "Sanitizer" - }, - { - "name": "SelectControlValueAccessor" - }, - { - "name": "SelectMultipleControlValueAccessor" - }, - { - "name": "Self" - }, - { - "name": "ShadowDomRenderer" - }, - { - "name": "SharedStylesHost" - }, - { - "name": "SimpleChange" - }, - { - "name": "SkipSelf" - }, - { - "name": "Subject" - }, - { - "name": "SubjectSubscriber" - }, - { - "name": "SubjectSubscription" - }, - { - "name": "Subscriber" - }, - { - "name": "Subscription" - }, - { - "name": "THROW_IF_NOT_FOUND" - }, - { - "name": "TRANSITION_ID" - }, - { - "name": "TemplateFormsComponent" - }, - { - "name": "TemplateFormsComponent_div_14_Template" - }, - { - "name": "TemplateRef" - }, - { - "name": "Testability" - }, - { - "name": "TestabilityRegistry" - }, - { - "name": "USE_VALUE" - }, - { - "name": "UnsubscriptionError" - }, - { - "name": "VERSION" - }, - { - "name": "VE_ViewContainerRef" - }, - { - "name": "Validators" - }, - { - "name": "Version" - }, - { - "name": "ViewContainerRef" - }, - { - "name": "ViewEncapsulation" - }, - { - "name": "ViewEngineTemplateRef" - }, - { - "name": "ViewRef" - }, - { - "name": "_DOM" - }, - { - "name": "_DuplicateItemRecordList" - }, - { - "name": "_DuplicateMap" - }, - { - "name": "_NoopGetTestability" - }, - { - "name": "_NullComponentFactoryResolver" - }, - { - "name": "__extends" - }, - { - "name": "__forward_ref__" - }, - { - "name": "__global" - }, - { - "name": "__globalThis" - }, - { - "name": "__self" - }, - { - "name": "__window" - }, - { - "name": "_chromeNumKeyPadMap" - }, - { - "name": "_currentInjector" - }, - { - "name": "_enable_super_gross_mode_that_will_cause_bad_things" - }, - { - "name": "_global" - }, - { - "name": "_hasInvalidParent" - }, - { - "name": "_keyMap" - }, - { - "name": "_randomChar" - }, - { - "name": "_renderCompCount" - }, - { - "name": "_symbolIterator" - }, - { - "name": "_testabilityGetter" - }, - { - "name": "addComponentLogic" - }, - { - "name": "addToArray" - }, - { - "name": "addToViewTree" - }, - { - "name": "allocExpando" - }, - { - "name": "allocLFrame" - }, - { - "name": "appendChild" - }, - { - "name": "applyNodes" - }, - { - "name": "applyProjectionRecursive" - }, - { - "name": "applyToElementOrContainer" - }, - { - "name": "applyView" - }, - { - "name": "attachPatchData" - }, - { - "name": "autoRegisterModuleById" - }, - { - "name": "baseElement" - }, - { - "name": "bindingUpdated" - }, - { - "name": "bloomHasToken" - }, - { - "name": "callHook" - }, - { - "name": "callHooks" - }, - { - "name": "checkStable" - }, - { - "name": "classIndexOf" - }, - { - "name": "cleanUpValidators" - }, - { - "name": "cleanUpView" - }, - { - "name": "coerceToAsyncValidator" - }, - { - "name": "coerceToValidator" - }, - { - "name": "collectStylingFromDirectives" - }, - { - "name": "collectStylingFromTAttrs" - }, - { - "name": "composeAsyncValidators" - }, - { - "name": "composeValidators" - }, - { - "name": "computeStaticStyling" - }, - { - "name": "concatStringsWithSpace" - }, - { - "name": "config" - }, - { - "name": "configureViewWithDirective" - }, - { - "name": "connectableObservableDescriptor" - }, - { - "name": "controlNameBinding" - }, - { - "name": "controlPath" - }, - { - "name": "createDirectivesInstances" - }, - { - "name": "createElementNode" - }, - { - "name": "createElementRef" - }, - { - "name": "createInjectorWithoutInjectorInstances" - }, - { - "name": "createLContainer" - }, - { - "name": "createLFrame" - }, - { - "name": "createLView" - }, - { - "name": "createNodeInjector" - }, - { - "name": "createPlatformFactory" - }, - { - "name": "createTView" - }, - { - "name": "decoratePreventDefault" - }, - { - "name": "deepForEach" - }, - { - "name": "defaultErrorLogger" - }, - { - "name": "defaultIterableDiffers" - }, - { - "name": "defaultKeyValueDiffers" - }, - { - "name": "defaultScheduler" - }, - { - "name": "destroyLView" - }, - { - "name": "detachMovedView" - }, - { - "name": "detachView" - }, - { - "name": "detectChangesInRootView" - }, - { - "name": "detectChangesInternal" - }, - { - "name": "diPublicInInjector" - }, - { - "name": "domRendererFactory3" - }, - { - "name": "empty" - }, - { - "name": "enterDI" - }, - { - "name": "enterView" - }, - { - "name": "executeCheckHooks" - }, - { - "name": "executeInitAndCheckHooks" - }, - { - "name": "executeListenerWithErrorHandling" - }, - { - "name": "executeTemplate" - }, - { - "name": "executeValidators" - }, - { - "name": "executeViewQueryFn" - }, - { - "name": "extendStatics" - }, - { - "name": "extractDirectiveDef" - }, - { - "name": "extractPipeDef" - }, - { - "name": "fillProperties" - }, - { - "name": "findAttrIndexInNode" - }, - { - "name": "findStylingValue" - }, - { - "name": "flattenStyles" - }, - { - "name": "flattenUnsubscriptionErrors" - }, - { - "name": "forkJoinInternal" - }, - { - "name": "formArrayNameProvider" - }, - { - "name": "formControlBinding" - }, - { - "name": "formDirectiveProvider" - }, - { - "name": "formDirectiveProvider" - }, - { - "name": "formGroupNameProvider" - }, - { - "name": "forwardRef" - }, - { - "name": "from" - }, - { - "name": "fromArray" - }, - { - "name": "generateInitialInputs" - }, - { - "name": "generatePropertyAliases" - }, - { - "name": "getClosureSafeProperty" - }, - { - "name": "getComponentDef" - }, - { - "name": "getComponentLViewByIndex" - }, - { - "name": "getConstant" - }, - { - "name": "getControlAsyncValidators" - }, - { - "name": "getControlValidators" - }, - { - "name": "getCurrentTNode" - }, - { - "name": "getCurrentTNodePlaceholderOk" - }, - { - "name": "getDOM" - }, - { - "name": "getDebugContext" - }, - { - "name": "getDeclarationTNode" - }, - { - "name": "getFactoryDef" - }, - { - "name": "getFirstLContainer" - }, - { - "name": "getInjectableDef" - }, - { - "name": "getInjectorDef" - }, - { - "name": "getInjectorIndex" - }, - { - "name": "getLCleanup" - }, - { - "name": "getLView" - }, - { - "name": "getLViewParent" - }, - { - "name": "getLocaleData" - }, - { - "name": "getNativeByTNode" - }, - { - "name": "getNearestLContainer" - }, - { - "name": "getNextLContainer" - }, - { - "name": "getNgModuleDef" - }, - { - "name": "getNodeInjectable" - }, - { - "name": "getNullInjector" - }, - { - "name": "getOrCreateInjectable" - }, - { - "name": "getOrCreateNodeInjectorForNode" - }, - { - "name": "getOrCreateTComponentView" - }, - { - "name": "getOrCreateTNode" - }, - { - "name": "getOrCreateViewRefs" - }, - { - "name": "getOriginalError" - }, - { - "name": "getOwnDefinition" - }, - { - "name": "getParentInjectorIndex" - }, - { - "name": "getParentInjectorLocation" - }, - { - "name": "getParentInjectorView" - }, - { - "name": "getPlatform" - }, - { - "name": "getPreviousIndex" - }, - { - "name": "getPromiseCtor" - }, - { - "name": "getSelectedIndex" - }, - { - "name": "getSelectedTNode" - }, - { - "name": "getSimpleChangesStore" - }, - { - "name": "getSymbolIterator" - }, - { - "name": "getSymbolIterator" - }, - { - "name": "getTNode" - }, - { - "name": "getTStylingRangeNext" - }, - { - "name": "getTStylingRangePrev" - }, - { - "name": "getTView" - }, - { - "name": "getViewRefs" - }, - { - "name": "handleError" - }, - { - "name": "hasParentInjector" - }, - { - "name": "hasTagAndTypeMatch" - }, - { - "name": "hasValidLength" - }, - { - "name": "hostReportError" - }, - { - "name": "icuContainerIterate" - }, - { - "name": "identity" - }, - { - "name": "includeViewProviders" - }, - { - "name": "incrementInitPhaseFlags" - }, - { - "name": "indexOf" - }, - { - "name": "inheritContentQueries" - }, - { - "name": "inheritHostBindings" - }, - { - "name": "inheritViewQuery" - }, - { - "name": "initTNodeFlags" - }, - { - "name": "injectArgs" - }, - { - "name": "injectInjectorOnly" - }, - { - "name": "injectRootLimpMode" - }, - { - "name": "injectableDefOrInjectorDefFactory" - }, - { - "name": "insertBloom" - }, - { - "name": "instructionState" - }, - { - "name": "invertObject" - }, - { - "name": "invokeHostBindingsInCreationMode" - }, - { - "name": "isAnimationProp" - }, - { - "name": "isArray" - }, - { - "name": "isArrayLike" - }, - { - "name": "isComponentDef" - }, - { - "name": "isComponentHost" - }, - { - "name": "isContentQueryHost" - }, - { - "name": "isCssClassMatching" - }, - { - "name": "isCurrentTNodeParent" - }, - { - "name": "isDirectiveHost" - }, - { - "name": "isEmptyInputValue" - }, - { - "name": "isForwardRef" - }, - { - "name": "isFunction" - }, - { - "name": "isInCheckNoChangesMode" - }, - { - "name": "isInlineTemplate" - }, - { - "name": "isJsObject" - }, - { - "name": "isLContainer" - }, - { - "name": "isLView" - }, - { - "name": "isListLikeIterable" - }, - { - "name": "isNodeMatchingSelector" - }, - { - "name": "isNodeMatchingSelectorList" - }, - { - "name": "isObject" - }, - { - "name": "isOptionsObj" - }, - { - "name": "isPositive" - }, - { - "name": "isPresent" - }, - { - "name": "isProceduralRenderer" - }, - { - "name": "isPromise" - }, - { - "name": "isPromise" - }, - { - "name": "isPropertyUpdated" - }, - { - "name": "isScheduler" - }, - { - "name": "isStylingMatch" - }, - { - "name": "isStylingValuePresent" - }, - { - "name": "isTypeProvider" - }, - { - "name": "isValueProvider" - }, - { - "name": "iterator" - }, - { - "name": "keyValDiff" - }, - { - "name": "keyValueArrayGet" - }, - { - "name": "keyValueArrayIndexOf" - }, - { - "name": "keyValueArraySet" - }, - { - "name": "leaveDI" - }, - { - "name": "leaveView" - }, - { - "name": "leaveViewLight" - }, - { - "name": "localeEn" - }, - { - "name": "lookupTokenUsingModuleInjector" - }, - { - "name": "makeParamDecorator" - }, - { - "name": "makeRecord" - }, - { - "name": "map" - }, - { - "name": "markAsComponentHost" - }, - { - "name": "markDuplicates" - }, - { - "name": "markViewDirty" - }, - { - "name": "maybeUnwrapEmpty" - }, - { - "name": "maybeUnwrapFn" - }, - { - "name": "maybeWrapInNotSelector" - }, - { - "name": "mergeAll" - }, - { - "name": "mergeErrors" - }, - { - "name": "mergeHostAttribute" - }, - { - "name": "mergeHostAttrs" - }, - { - "name": "mergeValidators" - }, - { - "name": "modelGroupProvider" - }, - { - "name": "modules" - }, - { - "name": "multiFactoryAdd" - }, - { - "name": "multiProvidersFactoryResolver" - }, - { - "name": "multiResolve" - }, - { - "name": "multiViewProvidersFactoryResolver" - }, - { - "name": "nativeAppendChild" - }, - { - "name": "nativeAppendOrInsertBefore" - }, - { - "name": "nativeInsertBefore" - }, - { - "name": "nativeParentNode" - }, - { - "name": "nextBindingIndex" - }, - { - "name": "nextNgElementId" - }, - { - "name": "ngOnChangesSetInput" - }, - { - "name": "noSideEffects" - }, - { - "name": "noop" - }, - { - "name": "noop" - }, - { - "name": "normalizeValidators" - }, - { - "name": "notFoundValueOrThrow" - }, - { - "name": "observable" - }, - { - "name": "onEnter" - }, - { - "name": "onLeave" - }, - { - "name": "optionsReducer" - }, - { - "name": "pickAsyncValidators" - }, - { - "name": "pickValidators" - }, - { - "name": "pipeFromArray" - }, - { - "name": "platformBrowser" - }, - { - "name": "platformCore" - }, - { - "name": "promise" - }, - { - "name": "providerToFactory" - }, - { - "name": "readPatchedLView" - }, - { - "name": "refCount" - }, - { - "name": "refreshComponent" - }, - { - "name": "refreshContentQueries" - }, - { - "name": "refreshView" - }, - { - "name": "registerDestroyHooksIfSupported" - }, - { - "name": "registerHostBindingOpCodes" - }, - { - "name": "registerOnValidatorChange" - }, - { - "name": "registerPostOrderHooks" - }, - { - "name": "rememberChangeHistoryAndInvokeOnChangesHook" - }, - { - "name": "remove" - }, - { - "name": "removeFromArray" - }, - { - "name": "removeListItem" - }, - { - "name": "renderComponent" - }, - { - "name": "renderComponentOrTemplate" - }, - { - "name": "renderStringify" - }, - { - "name": "renderView" - }, - { - "name": "resetPreOrderHookFlags" - }, - { - "name": "resolveDirectives" - }, - { - "name": "resolveForwardRef" - }, - { - "name": "resolveProvider" - }, - { - "name": "resolvedPromise" - }, - { - "name": "resolvedPromise" - }, - { - "name": "rxSubscriber" - }, - { - "name": "saveNameToExportMap" - }, - { - "name": "saveResolvedLocalsInData" - }, - { - "name": "scheduleArray" - }, - { - "name": "scheduleMicroTask" - }, - { - "name": "searchTokensOnInjector" - }, - { - "name": "selectIndexInternal" - }, - { - "name": "selectValueAccessor" - }, - { - "name": "setBindingRootForHostBindings" - }, - { - "name": "setCurrentDirectiveIndex" - }, - { - "name": "setCurrentInjector" - }, - { - "name": "setCurrentQueryIndex" - }, - { - "name": "setCurrentTNode" - }, - { - "name": "setDirectiveInputsWhichShadowsStyling" - }, - { - "name": "setIncludeViewProviders" - }, - { - "name": "setInjectImplementation" - }, - { - "name": "setInputsForProperty" - }, - { - "name": "setInputsFromAttrs" - }, - { - "name": "setIsInCheckNoChangesMode" - }, - { - "name": "setLocaleId" - }, - { - "name": "setSelectedIndex" - }, - { - "name": "setTStylingRangeNext" - }, - { - "name": "setTStylingRangeNextDuplicate" - }, - { - "name": "setTStylingRangePrevDuplicate" - }, - { - "name": "setUpAttributes" - }, - { - "name": "setUpControl" - }, - { - "name": "setUpFormContainer" - }, - { - "name": "setUpValidators" - }, - { - "name": "shareSubjectFactory" - }, - { - "name": "shouldSearchParent" - }, - { - "name": "stringify" - }, - { - "name": "stringifyCSSSelector" - }, - { - "name": "stringifyForError" - }, - { - "name": "subscribeTo" - }, - { - "name": "subscribeToArray" - }, - { - "name": "syncPendingControls" - }, - { - "name": "throwProviderNotFoundError" - }, - { - "name": "toObservable" - }, - { - "name": "toRefArray" - }, - { - "name": "toTStylingRange" - }, - { - "name": "trackByIdentity" - }, - { - "name": "u" - }, - { - "name": "unwrapRNode" - }, - { - "name": "updateControl" - }, - { - "name": "updateMicroTaskStatus" - }, - { - "name": "updateTransplantedViewCount" - }, - { - "name": "viewAttachedToChangeDetector" - }, - { - "name": "wrapListener" - }, - { - "name": "writeDirectClass" - }, - { - "name": "writeDirectStyle" - }, - { - "name": "ɵAbstractFormGroupDirective_BaseFactory" - }, - { - "name": "ɵInternalFormsSharedModule" - }, - { - "name": "ɵNgNoValidate" - }, - { - "name": "ɵɵInheritDefinitionFeature" - }, - { - "name": "ɵɵNgOnChangesFeature" - }, - { - "name": "ɵɵProvidersFeature" - }, - { - "name": "ɵɵadvance" - }, - { - "name": "ɵɵattribute" - }, - { - "name": "ɵɵclassProp" - }, - { - "name": "ɵɵdefineComponent" - }, - { - "name": "ɵɵdefineDirective" - }, - { - "name": "ɵɵdefineInjectable" - }, - { - "name": "ɵɵdefineInjector" - }, - { - "name": "ɵɵdefineNgModule" - }, - { - "name": "ɵɵdirectiveInject" - }, - { - "name": "ɵɵelement" - }, - { - "name": "ɵɵelementEnd" - }, - { - "name": "ɵɵelementStart" - }, - { - "name": "ɵɵgetFactoryOf" - }, - { - "name": "ɵɵgetInheritedFactory" - }, - { - "name": "ɵɵinject" - }, - { - "name": "ɵɵlistener" - }, - { - "name": "ɵɵnextContext" - }, - { - "name": "ɵɵproperty" - }, - { - "name": "ɵɵtemplate" - }, - { - "name": "ɵɵtext" - } -] \ No newline at end of file diff --git a/packages/core/test/bundling/forms/forms_e2e_spec.ts b/packages/core/test/bundling/forms/forms_e2e_spec.ts deleted file mode 100644 index eacb295420..0000000000 --- a/packages/core/test/bundling/forms/forms_e2e_spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import '@angular/compiler'; -import {ɵwhenRendered as whenRendered} from '@angular/core'; -import {withBody} from '@angular/private/testing'; -import * as path from 'path'; - -const PACKAGE = 'angular/packages/core/test/bundling/forms'; -const BUNDLES = ['bundle.js', 'bundle.min_debug.js', 'bundle.min.js']; - -describe('functional test for forms', () => { - BUNDLES.forEach((bundle) => { - describe(`using ${bundle} bundle`, () => { - it('should render template form', withBody('<app-root></app-root>', async () => { - require(path.join(PACKAGE, bundle)); - await (window as any).waitForApp; - - // Template forms - const templateFormsComponent = (window as any).templateFormsComponent; - await whenRendered(templateFormsComponent); - - const templateForm = document.querySelector('app-template-forms')!; - - // Check for inputs - const iputs = templateForm.querySelectorAll('input'); - expect(iputs.length).toBe(5); - - // Check for button - const templateButtons = templateForm.querySelectorAll('button'); - expect(templateButtons.length).toBe(1); - expect(templateButtons[0]).toBeDefined(); - - // Make sure button click works - const templateFormSpy = spyOn(templateFormsComponent, 'addCity'); - templateButtons[0].click(); - expect(templateFormSpy).toHaveBeenCalled(); - - // Reactive forms - const reactiveFormsComponent = (window as any).reactiveFormsComponent; - await whenRendered(reactiveFormsComponent); - - const reactiveForm = document.querySelector('app-reactive-forms')!; - - // Check for inputs - const inputs = reactiveForm.querySelectorAll('input'); - expect(inputs.length).toBe(5); - - // Check for button - const reactiveButtons = reactiveForm.querySelectorAll('button'); - expect(reactiveButtons.length).toBe(1); - expect(reactiveButtons[0]).toBeDefined(); - - // Make sure button click works - const reactiveFormSpy = spyOn(reactiveFormsComponent, 'addCity').and.callThrough(); - reactiveButtons[0].click(); - expect(reactiveFormSpy).toHaveBeenCalled(); - expect(reactiveFormsComponent.addresses.length).toBe(2); - })); - }); - }); -}); diff --git a/packages/core/test/bundling/forms/index.html b/packages/core/test/bundling/forms/index.html deleted file mode 100644 index 0703f0dbb5..0000000000 --- a/packages/core/test/bundling/forms/index.html +++ /dev/null @@ -1,32 +0,0 @@ -<!doctype html> - -<html> - <head> - <title>Angular Forms Example - - - - - - - - - - diff --git a/packages/core/test/bundling/forms/index.ts b/packages/core/test/bundling/forms/index.ts deleted file mode 100644 index 4a2e8d1294..0000000000 --- a/packages/core/test/bundling/forms/index.ts +++ /dev/null @@ -1,138 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -import {Component, NgModule, ɵNgModuleFactory as NgModuleFactory} from '@angular/core'; -import {FormArray, FormBuilder, FormControl, FormGroup, FormsModule, NgForm, ReactiveFormsModule, Validators} from '@angular/forms'; -import {BrowserModule, platformBrowser} from '@angular/platform-browser'; - -@Component({ - selector: 'app-template-forms', - template: ` -
    -
    -
    - First Name: - -
    -
    - Last Name: - -
    -
    - Subscribe: - -
    - -
    Disabled:
    - -
    - City -
    - - -
    - `, -}) -class TemplateFormsComponent { - name = {first: 'Nancy', last: 'Drew', subscribed: true}; - addresses = [{city: 'Toronto'}]; - constructor() { - // We use this reference in our test - (window as any).templateFormsComponent = this; - } - - addCity() { - this.addresses.push(({city: ''})); - } -} - -@Component({ - selector: 'app-reactive-forms', - template: ` -
    -
    - First Name: - -
    -
    - Last Name: - -
    - -
    - Subscribe: - -
    - -
    Disabled:
    -
    -
    -
    City:
    -
    -
    - - `, -}) -class ReactiveFormsComponent { - profileForm!: FormGroup; - addresses!: FormArray; - - get itemControls() { - return (this.profileForm.get('addresses') as FormArray).controls; - } - - constructor(private formBuilder: FormBuilder) { - // We use this reference in our test - (window as any).reactiveFormsComponent = this; - } - - ngOnInit() { - this.profileForm = new FormGroup({ - firstName: new FormControl('', Validators.required), - lastName: new FormControl(''), - addresses: new FormArray([]), - subscribed: new FormControl(), - disabledInput: new FormControl({value: '', disabled: true}), - }); - - this.addCity(); - } - - createItem(): FormGroup { - return this.formBuilder.group({ - city: '', - }); - } - - addCity(): void { - this.addresses = this.profileForm.get('addresses') as FormArray; - this.addresses.push(this.createItem()); - } -} - -@Component({ - selector: 'app-root', - template: ` - - - ` -}) -class RootComponent { -} - -@NgModule({ - declarations: [RootComponent, TemplateFormsComponent, ReactiveFormsComponent], - imports: [BrowserModule, FormsModule, ReactiveFormsModule] -}) -class FormsExampleModule { - ngDoBootstrap(app: any) { - app.bootstrap(RootComponent); - } -} - -(window as any).waitForApp = platformBrowser().bootstrapModuleFactory( - new NgModuleFactory(FormsExampleModule), {ngZone: 'noop'}); diff --git a/packages/core/test/bundling/forms/treeshaking_spec.ts b/packages/core/test/bundling/forms/treeshaking_spec.ts deleted file mode 100644 index 546a88ee0a..0000000000 --- a/packages/core/test/bundling/forms/treeshaking_spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import '@angular/compiler'; -import * as fs from 'fs'; -import * as path from 'path'; - -const UTF8 = { - encoding: 'utf-8' -}; -const PACKAGE = 'angular/packages/core/test/bundling/forms'; - -describe('treeshaking with uglify', () => { - let content: string; - // We use the debug version as otherwise symbols/identifiers would be mangled (and the test would - // always pass) - const contentPath = require.resolve(path.join(PACKAGE, 'bundle.min_debug.js')); - beforeAll(() => { - content = fs.readFileSync(contentPath, UTF8); - }); - - 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'); - }); -}); diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index 6f5521b982..9f68ceb496 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -242,6 +242,9 @@ { "name": "viewAttachedToChangeDetector" }, + { + "name": "ɵɵdefineComponent" + }, { "name": "ɵɵtext" } diff --git a/packages/core/test/bundling/injection/bundle.golden_symbols.json b/packages/core/test/bundling/injection/bundle.golden_symbols.json index eee96403a7..2333422a61 100644 --- a/packages/core/test/bundling/injection/bundle.golden_symbols.json +++ b/packages/core/test/bundling/injection/bundle.golden_symbols.json @@ -11,9 +11,6 @@ { "name": "INJECTOR_SCOPE" }, - { - "name": "Inject" - }, { "name": "InjectFlags" }, @@ -47,21 +44,15 @@ { "name": "NullInjector" }, - { - "name": "Optional" - }, { "name": "R3Injector" }, + { + "name": "RuntimeError" + }, { "name": "ScopedService" }, - { - "name": "Self" - }, - { - "name": "SkipSelf" - }, { "name": "THROW_IF_NOT_FOUND" }, @@ -110,9 +101,6 @@ { "name": "isValueProvider" }, - { - "name": "makeParamDecorator" - }, { "name": "makeRecord" }, diff --git a/packages/core/test/bundling/injection/usage.ts b/packages/core/test/bundling/injection/usage.ts index 325e886396..bc79a90866 100644 --- a/packages/core/test/bundling/injection/usage.ts +++ b/packages/core/test/bundling/injection/usage.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Injector, ɵcreateInjector as createInjector, ɵɵdefineInjectable, ɵɵdefineInjector} from '@angular/core'; +import {ɵcreateInjector as createInjector, ɵɵdefineInjectable, ɵɵdefineInjector} from '@angular/core'; export class RootService { static ɵprov = ɵɵdefineInjectable({ @@ -31,7 +31,6 @@ export class ScopedService { export class DefinedInjector { static ɵinj = ɵɵdefineInjector({ - factory: () => new DefinedInjector(), providers: [ScopedService], }); } diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 795e037553..ef5302071d 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -80,6 +80,9 @@ { "name": "BrowserViewportScroller" }, + { + "name": "BrowserXhr" + }, { "name": "CIRCULAR" }, @@ -236,9 +239,6 @@ { "name": "EMPTY_ARRAY" }, - { - "name": "EMPTY_ARRAY" - }, { "name": "EMPTY_OBJ" }, @@ -728,9 +728,6 @@ { "name": "SecurityContext" }, - { - "name": "Self" - }, { "name": "ShadowDomRenderer" }, @@ -968,6 +965,9 @@ { "name": "applyView" }, + { + "name": "attachInjectFlag" + }, { "name": "attachPatchData" }, @@ -1121,9 +1121,15 @@ { "name": "defaultIterableDiffers" }, + { + "name": "defaultIterableDiffersFactory" + }, { "name": "defaultKeyValueDiffers" }, + { + "name": "defaultKeyValueDiffersFactory" + }, { "name": "defaultMalformedUriErrorHandler" }, @@ -1193,6 +1199,9 @@ { "name": "equalPath" }, + { + "name": "exactMatchOptions" + }, { "name": "executeCheckHooks" }, @@ -1229,12 +1238,6 @@ { "name": "findPath" }, - { - "name": "fireActivationStart" - }, - { - "name": "fireChildActivationStart" - }, { "name": "first" }, @@ -1328,9 +1331,6 @@ { "name": "getInjectorIndex" }, - { - "name": "getLCleanup" - }, { "name": "getLView" }, @@ -1361,6 +1361,9 @@ { "name": "getOrCreateInjectable" }, + { + "name": "getOrCreateLViewCleanup" + }, { "name": "getOrCreateNodeInjectorForNode" }, @@ -1370,6 +1373,9 @@ { "name": "getOrCreateTNode" }, + { + "name": "getOrCreateTViewCleanup" + }, { "name": "getOrCreateViewRefs" }, @@ -1400,6 +1406,9 @@ { "name": "getPreviousIndex" }, + { + "name": "getProjectionNodes" + }, { "name": "getPromiseCtor" }, @@ -1445,6 +1454,9 @@ { "name": "handleError" }, + { + "name": "hasEmptyPathConfig" + }, { "name": "hasParentInjector" }, @@ -1530,13 +1542,13 @@ "name": "isDirectiveHost" }, { - "name": "isEmptyPathRedirect" + "name": "isFunction" }, { "name": "isFunction" }, { - "name": "isFunction" + "name": "isImmediateMatch" }, { "name": "isInCheckNoChangesMode" @@ -1571,6 +1583,9 @@ { "name": "isObject" }, + { + "name": "isObservable" + }, { "name": "isPositive" }, @@ -1634,9 +1649,6 @@ { "name": "map" }, - { - "name": "mapChildrenIntoArray" - }, { "name": "markAsComponentHost" }, @@ -1652,6 +1664,9 @@ { "name": "materializeViewResults" }, + { + "name": "matrixParamsMatch" + }, { "name": "maybeUnwrapFn" }, @@ -1670,9 +1685,6 @@ { "name": "mergeMap" }, - { - "name": "mergeTrivialChildren" - }, { "name": "modules" }, @@ -1694,6 +1706,9 @@ { "name": "navigationCancelingError" }, + { + "name": "newObservableError" + }, { "name": "nextBindingIndex" }, @@ -1703,6 +1718,12 @@ { "name": "ngOnChangesSetInput" }, + { + "name": "noLeftoversInUrl" + }, + { + "name": "noMatch" + }, { "name": "noMatch" }, @@ -1739,6 +1760,12 @@ { "name": "optionsReducer" }, + { + "name": "paramCompareMap" + }, + { + "name": "pathCompareMap" + }, { "name": "pipeFromArray" }, @@ -1814,18 +1841,15 @@ { "name": "routerNgProbeToken" }, - { - "name": "runCanActivate" - }, - { - "name": "runCanActivateChild" - }, { "name": "rxSubscriber" }, { "name": "saveNameToExportMap" }, + { + "name": "scan" + }, { "name": "scheduleArray" }, @@ -1898,9 +1922,15 @@ { "name": "shouldSearchParent" }, + { + "name": "sortByMatchingOutlets" + }, { "name": "split" }, + { + "name": "squashSegmentGroup" + }, { "name": "standardizeConfig" }, @@ -1931,6 +1961,9 @@ { "name": "subscribeToResult" }, + { + "name": "subsetMatchOptions" + }, { "name": "supportsState" }, @@ -1970,6 +2003,9 @@ { "name": "u" }, + { + "name": "unwrapElementRef" + }, { "name": "unwrapRNode" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 2162f45566..057cd33ab4 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -14,9 +14,6 @@ { "name": "EMPTY_ARRAY" }, - { - "name": "EMPTY_ARRAY" - }, { "name": "EMPTY_OBJ" }, @@ -221,6 +218,9 @@ { "name": "assertTemplate" }, + { + "name": "attachInjectFlag" + }, { "name": "attachPatchData" }, @@ -281,6 +281,9 @@ { "name": "defaultErrorLogger" }, + { + "name": "defaultIterableDiffersFactory" + }, { "name": "defaultScheduler" }, @@ -374,9 +377,6 @@ { "name": "getInjectorIndex" }, - { - "name": "getLCleanup" - }, { "name": "getLView" }, @@ -401,6 +401,9 @@ { "name": "getOrCreateInjectable" }, + { + "name": "getOrCreateLViewCleanup" + }, { "name": "getOrCreateNodeInjectorForNode" }, @@ -431,6 +434,9 @@ { "name": "getPreviousIndex" }, + { + "name": "getProjectionNodes" + }, { "name": "getSelectedIndex" }, 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 bc6a9040ea..ac244d0714 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 @@ -561,6 +561,65 @@ class ComplexItem { })); }); + it('should keep the order of duplicates', () => { + const l1 = [ + new ComplexItem('a', 'blue'), + new ComplexItem('b', 'yellow'), + new ComplexItem('c', 'orange'), + new ComplexItem('a', 'red'), + ]; + differ.check(l1); + + const l2 = [ + new ComplexItem('b', 'yellow'), + new ComplexItem('a', 'blue'), + new ComplexItem('c', 'orange'), + new ComplexItem('a', 'red'), + ]; + differ.check(l2); + + expect(iterableDifferToString(differ)).toEqual(iterableChangesAsString({ + collection: [ + '{id: b, color: yellow}[1->0]', '{id: a, color: blue}[0->1]', '{id: c, color: orange}', + '{id: a, color: red}' + ], + identityChanges: [ + '{id: b, color: yellow}[1->0]', '{id: a, color: blue}[0->1]', '{id: c, color: orange}', + '{id: a, color: red}' + ], + previous: [ + '{id: a, color: blue}[0->1]', '{id: b, color: yellow}[1->0]', '{id: c, color: orange}', + '{id: a, color: red}' + ], + moves: ['{id: b, color: yellow}[1->0]', '{id: a, color: blue}[0->1]'], + })); + }); + + it('should not have identity changed', () => { + const l1 = [ + new ComplexItem('a', 'blue'), + new ComplexItem('b', 'yellow'), + new ComplexItem('c', 'orange'), + new ComplexItem('a', 'red'), + ]; + differ.check(l1); + + const l2 = [l1[1], l1[0], l1[2], l1[3]]; + differ.check(l2); + + expect(iterableDifferToString(differ)).toEqual(iterableChangesAsString({ + collection: [ + '{id: b, color: yellow}[1->0]', '{id: a, color: blue}[0->1]', '{id: c, color: orange}', + '{id: a, color: red}' + ], + previous: [ + '{id: a, color: blue}[0->1]', '{id: b, color: yellow}[1->0]', '{id: c, color: orange}', + '{id: a, color: red}' + ], + moves: ['{id: b, color: yellow}[1->0]', '{id: a, color: blue}[0->1]'], + })); + }); + it('should track removals normally', () => { const l = buildItemList(['a', 'b', 'c']); differ.check(l); diff --git a/packages/core/test/change_detection/differs/iterable_differs_spec.ts b/packages/core/test/change_detection/differs/iterable_differs_spec.ts index 5563a85d51..19cdd0e691 100644 --- a/packages/core/test/change_detection/differs/iterable_differs_spec.ts +++ b/packages/core/test/change_detection/differs/iterable_differs_spec.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {Injector} from '@angular/core'; -import {IterableDiffers} from '@angular/core/src/change_detection/differs/iterable_differs'; +import {Injector, IterableDiffer, IterableDifferFactory, IterableDiffers, NgModule, TrackByFunction} from '@angular/core'; +import {TestBed} from '@angular/core/testing'; import {SpyIterableDifferFactory} from '../../spies'; @@ -49,13 +49,6 @@ import {SpyIterableDifferFactory} from '../../spies'; }); describe('.extend()', () => { - it('should throw if calling extend when creating root injector', () => { - const injector = Injector.create([IterableDiffers.extend([])]); - - expect(() => injector.get(IterableDiffers)) - .toThrowError(/Cannot extend IterableDiffers without a parent injector/); - }); - it('should extend di-inherited differs', () => { const parent = new IterableDiffers([factory1]); const injector = Injector.create([{provide: IterableDiffers, useValue: parent}]); @@ -66,6 +59,32 @@ import {SpyIterableDifferFactory} from '../../spies'; factory2, factory1 ]); }); + + it('should support .extend in root NgModule', () => { + const DIFFER: IterableDiffer = {} as any; + const log: string[] = []; + class MyIterableDifferFactory implements IterableDifferFactory { + supports(objects: any): boolean { + log.push('supports', objects); + return true; + } + create(trackByFn?: TrackByFunction): IterableDiffer { + log.push('create'); + return DIFFER; + } + } + + + @NgModule({providers: [IterableDiffers.extend([new MyIterableDifferFactory()])]}) + class MyModule { + } + + TestBed.configureTestingModule({imports: [MyModule]}); + const differs = TestBed.inject(IterableDiffers); + const differ = differs.find('VALUE').create(null!); + expect(differ).toEqual(DIFFER); + expect(log).toEqual(['supports', 'VALUE', 'create']); + }); }); }); } diff --git a/packages/core/test/di/r3_injector_spec.ts b/packages/core/test/di/r3_injector_spec.ts index 94b804492e..07ade1cd4a 100644 --- a/packages/core/test/di/r3_injector_spec.ts +++ b/packages/core/test/di/r3_injector_spec.ts @@ -138,8 +138,8 @@ describe('InjectorDef-based createInjector()', () => { deepModuleCreated = true; } + static ɵfac = () => new DeepModule(ɵɵinject(EagerService)); static ɵinj = ɵɵdefineInjector({ - factory: () => new DeepModule(ɵɵinject(EagerService)), imports: undefined, providers: [ @@ -164,7 +164,6 @@ describe('InjectorDef-based createInjector()', () => { class IntermediateModule { static ɵinj = ɵɵdefineInjector({ - factory: () => new IntermediateModule(), imports: [DeepModule.safe()], providers: [], }); @@ -173,9 +172,8 @@ describe('InjectorDef-based createInjector()', () => { class InjectorWithDep { constructor(readonly service: Service) {} - static ɵinj = ɵɵdefineInjector({ - factory: () => new InjectorWithDep(ɵɵinject(Service)), - }); + static ɵfac = () => new InjectorWithDep(ɵɵinject(Service)); + static ɵinj = ɵɵdefineInjector({}); } class ChildService extends ServiceWithDep {} @@ -191,7 +189,6 @@ describe('InjectorDef-based createInjector()', () => { class Module { static ɵinj = ɵɵdefineInjector({ - factory: () => new Module(), imports: [IntermediateModule], providers: [ @@ -222,7 +219,6 @@ describe('InjectorDef-based createInjector()', () => { class OtherModule { static ɵinj = ɵɵdefineInjector({ - factory: () => new OtherModule(), imports: undefined, providers: [], }); @@ -230,7 +226,6 @@ describe('InjectorDef-based createInjector()', () => { class ModuleWithMissingDep { static ɵinj = ɵɵdefineInjector({ - factory: () => new ModuleWithMissingDep(), imports: undefined, providers: [ServiceWithMissingDep], }); @@ -240,7 +235,6 @@ describe('InjectorDef-based createInjector()', () => { class ImportsNotAModule { static ɵinj = ɵɵdefineInjector({ - factory: () => new ImportsNotAModule(), imports: [NotAModule], providers: [], }); @@ -269,26 +263,22 @@ describe('InjectorDef-based createInjector()', () => { class MultiProviderA { static ɵinj = ɵɵdefineInjector({ - factory: () => new MultiProviderA(), providers: [{provide: LOCALE, multi: true, useValue: 'A'}], }); } class MultiProviderB { static ɵinj = ɵɵdefineInjector({ - factory: () => new MultiProviderB(), providers: [{provide: LOCALE, multi: true, useValue: 'B'}], }); } 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: [], }); } @@ -305,7 +295,6 @@ describe('InjectorDef-based createInjector()', () => { class ChildModule { static ɵinj = ɵɵdefineInjector({ - factory: () => new ChildModule(), imports: undefined, providers: [], }); @@ -316,7 +305,6 @@ describe('InjectorDef-based createInjector()', () => { class RootModule { static ɵinj = ɵɵdefineInjector({ - factory: () => new RootModule(), imports: [ChildModule], providers: [], }); @@ -499,8 +487,7 @@ describe('InjectorDef-based createInjector()', () => { constructor(missingType: any) {} } class ErrorModule { - static ɵinj = - ɵɵdefineInjector({factory: () => new ErrorModule(), providers: [MissingArgumentType]}); + static ɵinj = ɵɵdefineInjector({providers: [MissingArgumentType]}); } expect(() => createInjector(ErrorModule).get(MissingArgumentType)) .toThrowError('Can\'t resolve all parameters for MissingArgumentType: (?).'); diff --git a/packages/core/test/dom/dom_adapter_spec.ts b/packages/core/test/dom/dom_adapter_spec.ts index ba1dab5bc1..859b12a716 100644 --- a/packages/core/test/dom/dom_adapter_spec.ts +++ b/packages/core/test/dom/dom_adapter_spec.ts @@ -14,7 +14,7 @@ import {isTextNode} from '@angular/platform-browser/testing/src/browser_util'; describe('dom adapter', () => { let defaultDoc: any; beforeEach(() => { - defaultDoc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument(); + defaultDoc = getDOM().supportsDOMEvents ? document : getDOM().createHtmlDocument(); }); it('should be able to create text nodes and use them with the other APIs', () => { @@ -36,7 +36,7 @@ import {isTextNode} from '@angular/platform-browser/testing/src/browser_util'; expect(() => getDOM().remove(d)).not.toThrow(); }); - if (getDOM().supportsDOMEvents()) { + if (getDOM().supportsDOMEvents) { describe('getBaseHref', () => { beforeEach(() => getDOM().resetBaseElement()); diff --git a/packages/core/test/linker/integration_spec.ts b/packages/core/test/linker/integration_spec.ts index 46fd11e60f..11ee3e15a3 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, NgModule, NgModuleRef, NO_ERRORS_SCHEMA, 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, ViewChild, 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'; @@ -109,8 +109,7 @@ function declareTests(config?: {useJit: boolean}) { fixture.componentInstance.ctxProp = 'Hello World!'; fixture.detectChanges(); - expect(getDOM().getProperty(fixture.debugElement.children[0].nativeElement, 'id')) - .toEqual('Hello World!'); + expect(fixture.debugElement.children[0].nativeElement.id).toEqual('Hello World!'); }); it('should consume binding to aria-* attributes', () => { @@ -168,13 +167,11 @@ function declareTests(config?: {useJit: boolean}) { const fixture = TestBed.createComponent(MyComp); fixture.detectChanges(); - expect(getDOM().getProperty(fixture.debugElement.children[0].nativeElement, 'tabIndex')) - .toEqual(0); + expect(fixture.debugElement.children[0].nativeElement.tabIndex).toEqual(0); fixture.componentInstance.ctxNumProp = 5; fixture.detectChanges(); - expect(getDOM().getProperty(fixture.debugElement.children[0].nativeElement, 'tabIndex')) - .toEqual(5); + expect(fixture.debugElement.children[0].nativeElement.tabIndex).toEqual(5); }); it('should consume binding to camel-cased properties', () => { @@ -184,13 +181,11 @@ function declareTests(config?: {useJit: boolean}) { const fixture = TestBed.createComponent(MyComp); fixture.detectChanges(); - expect(getDOM().getProperty(fixture.debugElement.children[0].nativeElement, 'readOnly')) - .toBeFalsy(); + expect(fixture.debugElement.children[0].nativeElement.readOnly).toBeFalsy(); fixture.componentInstance.ctxBoolProp = true; fixture.detectChanges(); - expect(getDOM().getProperty(fixture.debugElement.children[0].nativeElement, 'readOnly')) - .toBeTruthy(); + expect(fixture.debugElement.children[0].nativeElement.readOnly).toBeTruthy(); }); it('should consume binding to innerHtml', () => { @@ -236,7 +231,7 @@ function declareTests(config?: {useJit: boolean}) { fixture.debugElement.componentInstance.ctxProp = 'foo'; fixture.detectChanges(); - expect(getDOM().getProperty(nativeEl, 'htmlFor')).toBe('foo'); + expect(nativeEl.htmlFor).toBe('foo'); }); it('should consume directive watch expression change.', () => { @@ -617,7 +612,7 @@ function declareTests(config?: {useJit: boolean}) { expect(cmp.numberOfChecks).toEqual(2); }); - if (getDOM().supportsDOMEvents()) { + if (getDOM().supportsDOMEvents) { it('should allow to destroy a component from within a host event handler', fakeAsync(() => { TestBed.configureTestingModule({declarations: [MyComp, [[PushCmpWithHostEvent]]]}); @@ -922,7 +917,7 @@ function declareTests(config?: {useJit: boolean}) { fixture.detectChanges(); - expect(getDOM().getProperty(tc.nativeElement, 'id')).toEqual('newId'); + expect(tc.nativeElement.id).toEqual('newId'); }); it('should not use template variables for expressions in hostProperties', () => { @@ -996,7 +991,7 @@ function declareTests(config?: {useJit: boolean}) { - if (getDOM().supportsDOMEvents()) { + if (getDOM().supportsDOMEvents) { it('should support preventing default on render events', () => { TestBed.configureTestingModule({ declarations: @@ -1641,6 +1636,159 @@ function declareTests(config?: {useJit: boolean}) { expect(fixture.nativeElement).toHaveText(''); }); + + describe('moving embedded views of projectable nodes in a dynamic component', () => { + @Component({selector: 'menu-item', template: ''}) + class DynamicMenuItem { + @ViewChild('templateRef', {static: true}) templateRef!: TemplateRef; + itemContent!: string; + } + + @NgModule({ + declarations: [DynamicMenuItem], + entryComponents: [DynamicMenuItem], + }) + class DynamicMenuItemModule { + } + + @Component({selector: 'test', template: ``}) + class TestCmp { + constructor(public cfr: ComponentFactoryResolver) {} + @ViewChild('menuItemsContainer', {static: true, read: ViewContainerRef}) + menuItemsContainer!: ViewContainerRef; + } + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [TestCmp], + imports: [DynamicMenuItemModule], + }); + }); + + const createElWithContent = (content: string, tagName = 'span') => { + const element = document.createElement(tagName); + element.textContent = content; + return element; + }; + + it('should support moving embedded views of projectable nodes', () => { + TestBed.overrideTemplate( + DynamicMenuItem, ``); + + const fixture = TestBed.createComponent(TestCmp); + const menuItemsContainer = fixture.componentInstance.menuItemsContainer; + const dynamicCmptFactory = + fixture.componentInstance.cfr.resolveComponentFactory(DynamicMenuItem); + + const cmptRefWithAa = + dynamicCmptFactory.create(Injector.NULL, [[createElWithContent('Aa')]]); + const cmptRefWithBb = + dynamicCmptFactory.create(Injector.NULL, [[createElWithContent('Bb')]]); + const cmptRefWithCc = + dynamicCmptFactory.create(Injector.NULL, [[createElWithContent('Cc')]]); + + menuItemsContainer.insert(cmptRefWithAa.instance.templateRef.createEmbeddedView({})); + menuItemsContainer.insert(cmptRefWithBb.instance.templateRef.createEmbeddedView({})); + menuItemsContainer.insert(cmptRefWithCc.instance.templateRef.createEmbeddedView({})); + + menuItemsContainer.move(menuItemsContainer.get(0)!, 1); + expect(fixture.nativeElement.textContent).toBe('BbAaCc'); + menuItemsContainer.move(menuItemsContainer.get(2)!, 1); + expect(fixture.nativeElement.textContent).toBe('BbCcAa'); + }); + + it('should support moving embedded views of projectable nodes in multiple slots', () => { + TestBed.overrideTemplate( + DynamicMenuItem, + ``); + + const fixture = TestBed.createComponent(TestCmp); + const menuItemsContainer = fixture.componentInstance.menuItemsContainer; + const dynamicCmptFactory = + fixture.componentInstance.cfr.resolveComponentFactory(DynamicMenuItem); + + const cmptRefWithAa = dynamicCmptFactory.create( + Injector.NULL, [[createElWithContent('A')], [createElWithContent('a', 'button')]]); + const cmptRefWithBb = dynamicCmptFactory.create( + Injector.NULL, [[createElWithContent('B')], [createElWithContent('b', 'button')]]); + const cmptRefWithCc = dynamicCmptFactory.create( + Injector.NULL, [[createElWithContent('C')], [createElWithContent('c', 'button')]]); + + menuItemsContainer.insert(cmptRefWithAa.instance.templateRef.createEmbeddedView({})); + menuItemsContainer.insert(cmptRefWithBb.instance.templateRef.createEmbeddedView({})); + menuItemsContainer.insert(cmptRefWithCc.instance.templateRef.createEmbeddedView({})); + + menuItemsContainer.move(menuItemsContainer.get(0)!, 1); + expect(fixture.nativeElement.textContent).toBe('BbAaCc'); + menuItemsContainer.move(menuItemsContainer.get(2)!, 1); + expect(fixture.nativeElement.textContent).toBe('BbCcAa'); + }); + + it('should support moving embedded views of projectable nodes in multiple slots and interpolations', + () => { + TestBed.overrideTemplate( + DynamicMenuItem, + `{{itemContent}}`); + + TestBed.configureTestingModule( + {declarations: [TestCmp], imports: [DynamicMenuItemModule]}); + + const fixture = TestBed.createComponent(TestCmp); + const menuItemsContainer = fixture.componentInstance.menuItemsContainer; + const dynamicCmptFactory = + fixture.componentInstance.cfr.resolveComponentFactory(DynamicMenuItem); + + const cmptRefWithAa = dynamicCmptFactory.create( + Injector.NULL, [[createElWithContent('A')], [createElWithContent('a', 'button')]]); + const cmptRefWithBb = dynamicCmptFactory.create( + Injector.NULL, [[createElWithContent('B')], [createElWithContent('b', 'button')]]); + const cmptRefWithCc = dynamicCmptFactory.create( + Injector.NULL, [[createElWithContent('C')], [createElWithContent('c', 'button')]]); + + menuItemsContainer.insert(cmptRefWithAa.instance.templateRef.createEmbeddedView({})); + menuItemsContainer.insert(cmptRefWithBb.instance.templateRef.createEmbeddedView({})); + menuItemsContainer.insert(cmptRefWithCc.instance.templateRef.createEmbeddedView({})); + + cmptRefWithAa.instance.itemContent = '0'; + cmptRefWithBb.instance.itemContent = '1'; + cmptRefWithCc.instance.itemContent = '2'; + + fixture.detectChanges(); + + menuItemsContainer.move(menuItemsContainer.get(0)!, 1); + expect(fixture.nativeElement.textContent).toBe('B1bA0aC2c'); + menuItemsContainer.move(menuItemsContainer.get(2)!, 1); + expect(fixture.nativeElement.textContent).toBe('B1bC2cA0a'); + }); + + it('should support moving embedded views with empty projectable slots', () => { + TestBed.overrideTemplate( + DynamicMenuItem, ``); + + const fixture = TestBed.createComponent(TestCmp); + const menuItemsContainer = fixture.componentInstance.menuItemsContainer; + const dynamicCmptFactory = + fixture.componentInstance.cfr.resolveComponentFactory(DynamicMenuItem); + + const cmptRefWithAa = dynamicCmptFactory.create(Injector.NULL, [[]]); + const cmptRefWithBb = + dynamicCmptFactory.create(Injector.NULL, [[createElWithContent('Bb')]]); + const cmptRefWithCc = + dynamicCmptFactory.create(Injector.NULL, [[createElWithContent('Cc')]]); + + menuItemsContainer.insert(cmptRefWithAa.instance.templateRef.createEmbeddedView({})); + menuItemsContainer.insert(cmptRefWithBb.instance.templateRef.createEmbeddedView({})); + menuItemsContainer.insert(cmptRefWithCc.instance.templateRef.createEmbeddedView({})); + + menuItemsContainer.move(menuItemsContainer.get(0)!, 1); // [ Bb, NULL, Cc] + expect(fixture.nativeElement.textContent).toBe('BbCc'); + menuItemsContainer.move(menuItemsContainer.get(2)!, 1); // [ Bb, Cc, NULL] + expect(fixture.nativeElement.textContent).toBe('BbCc'); + menuItemsContainer.move(menuItemsContainer.get(0)!, 1); // [ Cc, Bb, NULL] + expect(fixture.nativeElement.textContent).toBe('CcBb'); + }); + }); + describe('Property bindings', () => { modifiedInIvy('Unknown property error throws an error instead of logging it') .it('should throw on bindings to unknown properties', () => { @@ -1728,7 +1876,7 @@ function declareTests(config?: {useJit: boolean}) { fixture.detectChanges(); const el = fixture.nativeElement.querySelector('span'); - expect(getDOM().getProperty(el, 'title')).toEqual('TITLE'); + expect(el.title).toEqual('TITLE'); }); }); @@ -1944,7 +2092,7 @@ function declareTests(config?: {useJit: boolean}) { expect(fixture.debugElement.children[0].nativeElement.outerHTML).toContain('my-attr="aaa"'); }); - if (getDOM().supportsDOMEvents()) { + if (getDOM().supportsDOMEvents) { it('should support event decorators', fakeAsync(() => { TestBed.configureTestingModule({ declarations: [MyComp, DirectiveWithPropDecorators], @@ -2047,7 +2195,7 @@ function declareTests(config?: {useJit: boolean}) { })); }); - if (getDOM().supportsDOMEvents()) { + if (getDOM().supportsDOMEvents) { describe('svg', () => { it('should support svg elements', () => { TestBed.configureTestingModule({declarations: [MyComp]}); @@ -2058,12 +2206,10 @@ function declareTests(config?: {useJit: boolean}) { const el = fixture.nativeElement; const svg = el.childNodes[0]; const use = svg.childNodes[0]; - expect(getDOM().getProperty(svg, 'namespaceURI')) - .toEqual('http://www.w3.org/2000/svg'); - expect(getDOM().getProperty(use, 'namespaceURI')) - .toEqual('http://www.w3.org/2000/svg'); + expect(svg.namespaceURI).toEqual('http://www.w3.org/2000/svg'); + expect(use.namespaceURI).toEqual('http://www.w3.org/2000/svg'); - const firstAttribute = getDOM().getProperty(use, 'attributes')[0]; + const firstAttribute = use.attributes[0]; expect(firstAttribute.name).toEqual('xlink:href'); expect(firstAttribute.namespaceURI).toEqual('http://www.w3.org/1999/xlink'); }); @@ -2079,12 +2225,9 @@ function declareTests(config?: {useJit: boolean}) { const svg = el.childNodes[0]; const foreignObject = svg.childNodes[0]; const p = foreignObject.childNodes[0]; - expect(getDOM().getProperty(svg, 'namespaceURI')) - .toEqual('http://www.w3.org/2000/svg'); - expect(getDOM().getProperty(foreignObject, 'namespaceURI')) - .toEqual('http://www.w3.org/2000/svg'); - expect(getDOM().getProperty(p, 'namespaceURI')) - .toEqual('http://www.w3.org/1999/xhtml'); + expect(svg.namespaceURI).toEqual('http://www.w3.org/2000/svg'); + expect(foreignObject.namespaceURI).toEqual('http://www.w3.org/2000/svg'); + expect(p.namespaceURI).toEqual('http://www.w3.org/1999/xhtml'); }); }); diff --git a/packages/core/test/linker/ng_module_integration_spec.ts b/packages/core/test/linker/ng_module_integration_spec.ts index d3dc195b99..6f3e70a3d4 100644 --- a/packages/core/test/linker/ng_module_integration_spec.ts +++ b/packages/core/test/linker/ng_module_integration_spec.ts @@ -1419,7 +1419,7 @@ function declareTests(config?: {useJit: boolean}) { } class Bar { - static ɵprov: ɵɵInjectableDef = ɵɵdefineInjectable({ + static ɵprov = ɵɵdefineInjectable({ token: Bar, factory: () => new Bar(), providedIn: SomeModule, @@ -1452,7 +1452,7 @@ function declareTests(config?: {useJit: boolean}) { } class Bar { - static ɵprov: ɵɵInjectableDef = ɵɵdefineInjectable({ + static ɵprov = ɵɵdefineInjectable({ token: Bar, factory: () => new Bar(), providedIn: SomeModule, diff --git a/packages/core/test/linker/projection_integration_spec.ts b/packages/core/test/linker/projection_integration_spec.ts index d97673918f..b6f43389c8 100644 --- a/packages/core/test/linker/projection_integration_spec.ts +++ b/packages/core/test/linker/projection_integration_spec.ts @@ -508,7 +508,7 @@ describe('projection', () => { }); } - if (getDOM().supportsDOMEvents()) { + if (getDOM().supportsDOMEvents) { it('should support non emulated styles', () => { TestBed.configureTestingModule({declarations: [OtherComp]}); TestBed.overrideComponent(MainComp, { diff --git a/packages/core/test/linker/query_list_spec.ts b/packages/core/test/linker/query_list_spec.ts index f79b137548..c5b1fb7070 100644 --- a/packages/core/test/linker/query_list_spec.ts +++ b/packages/core/test/linker/query_list_spec.ts @@ -156,7 +156,7 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin expect(data.length).toBe(0); }); - if (getDOM().supportsDOMEvents()) { + if (getDOM().supportsDOMEvents) { describe('simple observable interface', () => { it('should fire callbacks on change', fakeAsync(() => { let fires = 0; diff --git a/packages/core/test/linker/regression_integration_spec.ts b/packages/core/test/linker/regression_integration_spec.ts index d4a1956368..b92e1a04ca 100644 --- a/packages/core/test/linker/regression_integration_spec.ts +++ b/packages/core/test/linker/regression_integration_spec.ts @@ -462,7 +462,7 @@ function declareTestsUsingBootstrap() { destroyPlatform(); }); - if (getDOM().supportsDOMEvents()) { + if (getDOM().supportsDOMEvents) { // This test needs a real DOM.... it('should keep change detecting if there was an error', (done) => { diff --git a/packages/core/test/linker/security_integration_spec.ts b/packages/core/test/linker/security_integration_spec.ts index 65fb81296a..006be4f715 100644 --- a/packages/core/test/linker/security_integration_spec.ts +++ b/packages/core/test/linker/security_integration_spec.ts @@ -49,13 +49,9 @@ function declareTests(config?: {useJit: boolean}) { }); }); - let originalLog: (msg: any) => any; beforeEach(() => { - originalLog = getDOM().log; - getDOM().log = (msg) => { /* disable logging */ }; - }); - afterEach(() => { - getDOM().log = originalLog; + // Disable logging for these tests. + spyOn(console, 'log').and.callFake(() => {}); }); describe('events', () => { @@ -128,7 +124,7 @@ function declareTests(config?: {useJit: boolean}) { cmp.detectChanges(); const div = cmp.debugElement.children[0]; expect(div.injector.get(OnPrefixDir).onclick).toBe(value); - expect(getDOM().getProperty(div.nativeElement, 'onclick')).not.toBe(value); + expect(div.nativeElement.onclick).not.toBe(value); expect(div.nativeElement.hasAttribute('onclick')).toEqual(false); }); }); @@ -145,7 +141,7 @@ function declareTests(config?: {useJit: boolean}) { const trusted = sanitizer.bypassSecurityTrustUrl('javascript:alert(1)'); ci.ctxProp = trusted; fixture.detectChanges(); - expect(getDOM().getProperty(e, 'href')).toEqual('javascript:alert(1)'); + expect(e.getAttribute('href')).toEqual('javascript:alert(1)'); }); it('should error when using the wrong trusted value', () => { @@ -171,25 +167,21 @@ function declareTests(config?: {useJit: boolean}) { const ci = fixture.componentInstance; ci.ctxProp = trusted; fixture.detectChanges(); - expect(getDOM().getProperty(e, 'href')).toMatch(/SafeValue(%20| )must(%20| )use/); + expect(e.href).toMatch(/SafeValue(%20| )must(%20| )use/); }); }); describe('sanitizing', () => { - function checkEscapeOfHrefProperty(fixture: ComponentFixture, isAttribute: boolean) { + function checkEscapeOfHrefProperty(fixture: ComponentFixture) { const e = fixture.debugElement.children[0].nativeElement; const ci = fixture.componentInstance; ci.ctxProp = 'hello'; fixture.detectChanges(); - // In the browser, reading href returns an absolute URL. On the server side, - // it just echoes back the property. - let value = isAttribute ? e.getAttribute('href') : getDOM().getProperty(e, 'href'); - expect(value).toMatch(/.*\/?hello$/); + expect(e.getAttribute('href')).toMatch(/.*\/?hello$/); ci.ctxProp = 'javascript:alert(1)'; fixture.detectChanges(); - value = isAttribute ? e.getAttribute('href') : getDOM().getProperty(e, 'href'); - expect(value).toEqual('unsafe:javascript:alert(1)'); + expect(e.getAttribute('href')).toEqual('unsafe:javascript:alert(1)'); } it('should escape unsafe properties', () => { @@ -197,7 +189,7 @@ function declareTests(config?: {useJit: boolean}) { TestBed.overrideComponent(SecuredComponent, {set: {template}}); const fixture = TestBed.createComponent(SecuredComponent); - checkEscapeOfHrefProperty(fixture, false); + checkEscapeOfHrefProperty(fixture); }); it('should escape unsafe attributes', () => { @@ -205,7 +197,7 @@ function declareTests(config?: {useJit: boolean}) { TestBed.overrideComponent(SecuredComponent, {set: {template}}); const fixture = TestBed.createComponent(SecuredComponent); - checkEscapeOfHrefProperty(fixture, true); + checkEscapeOfHrefProperty(fixture); }); it('should escape unsafe properties if they are used in host bindings', () => { @@ -220,7 +212,7 @@ function declareTests(config?: {useJit: boolean}) { TestBed.overrideComponent(SecuredComponent, {set: {template}}); const fixture = TestBed.createComponent(SecuredComponent); - checkEscapeOfHrefProperty(fixture, false); + checkEscapeOfHrefProperty(fixture); }); it('should escape unsafe attributes if they are used in host bindings', () => { @@ -235,7 +227,7 @@ function declareTests(config?: {useJit: boolean}) { TestBed.overrideComponent(SecuredComponent, {set: {template}}); const fixture = TestBed.createComponent(SecuredComponent); - checkEscapeOfHrefProperty(fixture, true); + checkEscapeOfHrefProperty(fixture); }); modifiedInIvy('Unknown property error thrown during update mode, not creation mode') diff --git a/packages/core/test/linker/view_injector_integration_spec.ts b/packages/core/test/linker/view_injector_integration_spec.ts index ecb9b2ab5b..9e2d88d400 100644 --- a/packages/core/test/linker/view_injector_integration_spec.ts +++ b/packages/core/test/linker/view_injector_integration_spec.ts @@ -628,7 +628,8 @@ describe('View injector', () => { .it('should not instantiate a directive with cyclic dependencies', () => { TestBed.configureTestingModule({declarations: [CycleDirective]}); expect(() => createComponent('
    ')) - .toThrowError('NG0200: Circular dependency in DI detected for CycleDirective'); + .toThrowError( + 'NG0200: Circular dependency in DI detected for CycleDirective. Find more at https://angular.io/errors/NG0200'); }); obsoleteInIvy('This error is no longer generated by the compiler') @@ -661,7 +662,8 @@ describe('View injector', () => { SimpleComponent, {set: {template: '
    '}}); expect(() => createComponent('
    ')) - .toThrowError('NG0201: No provider for service found in NodeInjector'); + .toThrowError( + 'NG0201: No provider for service found in NodeInjector. Find more at https://angular.io/errors/NG0201'); }); obsoleteInIvy('This error is no longer generated by the compiler') @@ -694,7 +696,8 @@ describe('View injector', () => { SimpleComponent, {set: {template: '
    '}}); expect(() => createComponent('
    ')) - .toThrowError('NG0201: No provider for service found in NodeInjector'); + .toThrowError( + 'NG0201: No provider for service found in NodeInjector. Find more at https://angular.io/errors/NG0201'); }); obsoleteInIvy('This error is no longer generated by the compiler') @@ -717,7 +720,8 @@ describe('View injector', () => { expect( () => createComponent( '
    ')) - .toThrowError('NG0201: No provider for SimpleDirective found in NodeInjector'); + .toThrowError( + 'NG0201: No provider for SimpleDirective found in NodeInjector. Find more at https://angular.io/errors/NG0201'); }); it('should instantiate directives that depend on other directives', fakeAsync(() => { @@ -779,7 +783,8 @@ describe('View injector', () => { TestBed.overrideComponent( SimpleComponent, {set: {template: '
    '}}); expect(() => createComponent('
    ')) - .toThrowError('NG0201: No provider for SimpleDirective found in NodeInjector'); + .toThrowError( + 'NG0201: No provider for SimpleDirective found in NodeInjector. Find more at https://angular.io/errors/NG0201'); }); it('should allow to use the NgModule injector from a root ViewContainerRef.parentInjector', diff --git a/packages/core/test/reflection/reflector_spec.ts b/packages/core/test/reflection/reflector_spec.ts index 87123b7087..137edb7f88 100644 --- a/packages/core/test/reflection/reflector_spec.ts +++ b/packages/core/test/reflection/reflector_spec.ts @@ -202,13 +202,48 @@ class TestObj { }); // See: https://github.com/angular/angular/issues/38453 - it('should support ES2015 downleveled classes', () => { - const {ChildNoCtor, ChildNoCtorPrivateProps, ChildWithCtor} = - require('./es5_downleveled_inheritance_fixture'); + it('should support ES2015 downleveled classes (workspace TypeScript version) (downlevelIteration=true)', + () => { + const {ChildNoCtor, ChildNoCtorPrivateProps, ChildWithCtor} = + require('./es5_downleveled_inheritance_fixture'); - expect(isDelegateCtor(ChildNoCtor.toString())).toBe(true); - expect(isDelegateCtor(ChildNoCtorPrivateProps.toString())).toBe(true); - expect(isDelegateCtor(ChildWithCtor.toString())).toBe(false); + expect(isDelegateCtor(ChildNoCtor.toString())).toBe(true); + expect(isDelegateCtor(ChildNoCtorPrivateProps.toString())).toBe(true); + expect(isDelegateCtor(ChildWithCtor.toString())).toBe(false); + }); + + it('should support ES2015 downleveled classes ( { + const ChildNoCtor = `function ChildNoCtor() { + return _super !== null && _super.apply(this, arguments) || this; + }`; + const ChildNoCtorPrivateProps = `function ChildNoCtorPrivateProps() { + var _this = _super.apply(this, __spread(arguments)) || this; + _this.x = 10; + return _this; + }`; + const ChildWithCtor = `function ChildWithCtor() { + return _super.call(this) || this; + }`; + expect(isDelegateCtor(ChildNoCtor)).toBe(true); + expect(isDelegateCtor(ChildNoCtorPrivateProps)).toBe(true); + expect(isDelegateCtor(ChildWithCtor)).toBe(false); + }); + + it('should support ES2015 downleveled classes (>=TS4.2) (downlevelIteration=true)', () => { + const ChildNoCtor = `function ChildNoCtor() { + return _super !== null && _super.apply(this, arguments) || this; + }`; + const ChildNoCtorPrivateProps = `function ChildNoCtorPrivateProps() { + var _this = _super.apply(this, __spreadArray([], __read(arguments))) || this; + _this.x = 10; + return _this; + }`; + const ChildWithCtor = `function ChildWithCtor() { + return _super.call(this) || this; + }`; + expect(isDelegateCtor(ChildNoCtor)).toBe(true); + expect(isDelegateCtor(ChildNoCtorPrivateProps)).toBe(true); + expect(isDelegateCtor(ChildWithCtor)).toBe(false); }); it('should support ES2015 classes when minified', () => { diff --git a/packages/core/test/render3/BUILD.bazel b/packages/core/test/render3/BUILD.bazel index cce0a84309..aab2ac37be 100644 --- a/packages/core/test/render3/BUILD.bazel +++ b/packages/core/test/render3/BUILD.bazel @@ -80,6 +80,9 @@ jasmine_node_test( ":domino_es5", "//tools/testing:node_es5", ], + tags = [ + "ivy-only", + ], deps = [ ":render3_node_lib", "//packages/zone.js/lib", @@ -88,6 +91,9 @@ jasmine_node_test( karma_web_test_suite( name = "render3_web", + tags = [ + "ivy-only", + ], deps = [ ":render3_lib", ], diff --git a/packages/core/test/render3/component_spec.ts b/packages/core/test/render3/component_spec.ts index 8bc748575c..0bd0f99b23 100644 --- a/packages/core/test/render3/component_spec.ts +++ b/packages/core/test/render3/component_spec.ts @@ -94,10 +94,8 @@ describe('component', () => { } class MyModule { - static ɵinj = ɵɵdefineInjector({ - factory: () => new MyModule(), - providers: [{provide: MyService, useValue: new MyService('injector')}] - }); + static ɵinj = ɵɵdefineInjector( + {providers: [{provide: MyService, useValue: new MyService('injector')}]}); } it('should support bootstrapping without injector', () => { diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index 32aab952a7..64d20d865f 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -107,7 +107,9 @@ describe('di', () => { (DirA as any)['__NG_ELEMENT_ID__'] = 1; (DirC as any)['__NG_ELEMENT_ID__'] = 257; new ComponentFixture(App); - }).toThrowError('NG0201: No provider for DirB found in NodeInjector'); + }) + .toThrowError( + 'NG0201: No provider for DirB found in NodeInjector. Find more at https://angular.io/errors/NG0201'); }); }); }); @@ -180,6 +182,9 @@ describe('di', () => { class Dir231 { /** @internal */ static __NG_ELEMENT_ID__ = 231; } + class Dir260 { + /** @internal */ static __NG_ELEMENT_ID__ = 260; + } it('should add values', () => { bloomAdd(0, mockTView, Dir0); @@ -198,6 +203,8 @@ describe('di', () => { expect(bloomState()).toEqual([0, 64, 32, 16, 8, 4, 2, 1]); bloomAdd(0, mockTView, Dir231); expect(bloomState()).toEqual([128, 64, 32, 16, 8, 4, 2, 1]); + bloomAdd(0, mockTView, Dir260); + expect(bloomState()).toEqual([128, 64, 32, 16, 8, 4, 2, 17 /* 1 + 2^(260-256) */]); }); it('should query values', () => { @@ -209,6 +216,7 @@ describe('di', () => { bloomAdd(0, mockTView, Dir165); bloomAdd(0, mockTView, Dir198); bloomAdd(0, mockTView, Dir231); + bloomAdd(0, mockTView, Dir260); expect(bloomHasToken(bloomHash(Dir0) as number, 0, mockTView.data)).toEqual(true); expect(bloomHasToken(bloomHash(Dir1) as number, 0, mockTView.data)).toEqual(false); @@ -219,6 +227,7 @@ describe('di', () => { expect(bloomHasToken(bloomHash(Dir165) as number, 0, mockTView.data)).toEqual(true); expect(bloomHasToken(bloomHash(Dir198) as number, 0, mockTView.data)).toEqual(true); expect(bloomHasToken(bloomHash(Dir231) as number, 0, mockTView.data)).toEqual(true); + expect(bloomHasToken(bloomHash(Dir260) as number, 0, mockTView.data)).toEqual(true); }); }); }); diff --git a/packages/core/test/render3/global_utils_spec.ts b/packages/core/test/render3/global_utils_spec.ts index 577eddecf7..ea19965a1d 100644 --- a/packages/core/test/render3/global_utils_spec.ts +++ b/packages/core/test/render3/global_utils_spec.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import {setProfiler} from '@angular/core/src/render3/profiler'; import {applyChanges} from '../../src/render3/util/change_detection_utils'; import {getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getOwningComponent, getRootComponents} from '../../src/render3/util/discovery_utils'; import {GLOBAL_PUBLISH_EXPANDO_KEY, GlobalDevModeContainer, publishDefaultGlobalUtils, publishGlobalUtil} from '../../src/render3/util/global_utils'; @@ -60,6 +61,10 @@ describe('global utils', () => { it('should publish applyChanges', () => { assertPublished('applyChanges', applyChanges); }); + + it('should publish ɵsetProfiler', () => { + assertPublished('ɵsetProfiler', setProfiler); + }); }); }); diff --git a/packages/core/test/render3/instructions/lview_debug_spec.ts b/packages/core/test/render3/instructions/lview_debug_spec.ts index 3098ea3587..56219550e3 100644 --- a/packages/core/test/render3/instructions/lview_debug_spec.ts +++ b/packages/core/test/render3/instructions/lview_debug_spec.ts @@ -6,15 +6,19 @@ * found in the LICENSE file at https://angular.io/license */ -import {ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵProvidersFeature} from '@angular/core/src/core'; +import {Component, Injectable, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵProvidersFeature} from '@angular/core/src/core'; +import {ComponentDef, DirectiveDef} from '@angular/core/src/render3'; +import {readPatchedData} from '@angular/core/src/render3/context_discovery'; import {ɵɵelement, ɵɵelementEnd, ɵɵelementStart} from '@angular/core/src/render3/instructions/element'; import {TNodeDebug} from '@angular/core/src/render3/instructions/lview_debug'; import {createTNode, createTView} from '@angular/core/src/render3/instructions/shared'; import {TNodeType} from '@angular/core/src/render3/interfaces/node'; -import {LView, TView, TViewType} from '@angular/core/src/render3/interfaces/view'; +import {LView, LViewDebug, TView, TViewType} 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'; +import {getComponentLView} from '@angular/core/src/render3/util/discovery_utils'; import {KeyValueArray} from '@angular/core/src/util/array_utils'; +import {TestBed} from '@angular/core/testing'; import {TemplateFixture} from '../render_util'; describe('lView_debug', () => { @@ -223,7 +227,10 @@ describe('lView_debug', () => { expect(myCompNode.injector).toEqual({ bloom: jasmine.anything(), cumulativeBloom: jasmine.anything(), - providers: [DepA, String, MyComponent.ɵcmp, MyDirective.ɵdir], + providers: [ + DepA, String, MyComponent.ɵcmp as ComponentDef, + MyDirective.ɵdir as DirectiveDef + ], viewProviders: [DepB, Number], parentInjectorIndex: -1, }); @@ -239,4 +246,62 @@ describe('lView_debug', () => { }); }); }); + + describe('debugNodeInjectorPath', () => { + @Injectable() + class MyService { + } + + @Injectable() + class OtherService { + } + + @Component({ + selector: 'parent', + template: `Parent: []`, + providers: [MyService], + }) + class ParentComponent { + } + @Component({ + selector: 'child', + template: `Child!`, + providers: [OtherService], + }) + class ChildComponent { + constructor(private myService: MyService, private otherService: OtherService) {} + } + + it('should display injection path', () => { + expect(ngDevMode).toBeTruthy(); + TestBed.configureTestingModule({declarations: [ParentComponent, ChildComponent]}); + const parentFixture = TestBed.createComponent(ParentComponent); + const parentHostElement = parentFixture.nativeElement as HTMLElement; + const childElement = parentHostElement.querySelector('child')! as HTMLElement; + if (!readPatchedData(childElement)) { + // In these browsers: + // - Chrome Mobile 72.0.3626 (Android 0.0.0) + // - IE 11.0.0 (Windows 8.1.0.0) + // Retrieving `LContext` does not work for unknown reasons, and we are unable to debug it. + // Exiting tests early to prevent breaking the test suite. + return; + } + const childLViewDebug = getComponentLView(childElement).debug!; + const parentLViewDebug = childLViewDebug.parent as LViewDebug; + const rootLViewDebug = parentLViewDebug.parent! as LViewDebug; + const childRootNode = childLViewDebug.nodes[0]; + expect(childRootNode.injector.bloom).toEqual('NO_NODE_INJECTOR'); + expect(childRootNode.injector.cumulativeBloom).toEqual('NO_NODE_INJECTOR'); + const injectorResolutionPath = childRootNode.injectorResolutionPath; + expect(injectorResolutionPath.length).toEqual(2); + expect(injectorResolutionPath[0].injector) + .toEqual( + parentLViewDebug.nodes[1].injector, + ); + expect(injectorResolutionPath[1].injector) + .toEqual( + rootLViewDebug.nodes[0].injector, + ); + }); + }); }); diff --git a/packages/core/test/render3/integration_spec.ts b/packages/core/test/render3/integration_spec.ts index afc8334410..fdd830d44b 100644 --- a/packages/core/test/render3/integration_spec.ts +++ b/packages/core/test/render3/integration_spec.ts @@ -8,10 +8,9 @@ import {RElement} from '@angular/core/src/render3/interfaces/renderer_dom'; import {RendererType2} from '../../src/render/api_flags'; -import {getLContext} from '../../src/render3/context_discovery'; +import {getLContext, readPatchedData} from '../../src/render3/context_discovery'; import {AttributeMarker, ɵɵadvance, ɵɵattribute, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵhostProperty, ɵɵproperty} from '../../src/render3/index'; import {ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵprojection, ɵɵprojectionDef, ɵɵtemplate, ɵɵtext} from '../../src/render3/instructions/all'; -import {MONKEY_PATCH_KEY_NAME} from '../../src/render3/interfaces/context'; import {RenderFlags} from '../../src/render3/interfaces/definition'; import {domRendererFactory3, Renderer3, RendererFactory3} from '../../src/render3/interfaces/renderer'; import {CONTEXT, HEADER_OFFSET} from '../../src/render3/interfaces/view'; @@ -269,8 +268,8 @@ describe('element discovery', () => { const parent = host.querySelector('div') as any; const child = host.querySelector('p') as any; - expect(parent[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); - expect(child[MONKEY_PATCH_KEY_NAME]).toBeFalsy(); + expect(readPatchedData(parent)).toBeTruthy(); + expect(readPatchedData(child)).toBeFalsy(); }); it('should only monkey-patch immediate child nodes in a sub component', () => { @@ -317,12 +316,12 @@ describe('element discovery', () => { const host = fixture.hostElement; const child = host.querySelector('child-comp') as any; - expect(child[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); + expect(readPatchedData(child)).toBeTruthy(); const [kid1, kid2, kid3] = Array.from(host.querySelectorAll('child-comp > *')); - expect(kid1[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); - expect(kid2[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); - expect(kid3[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); + expect(readPatchedData(kid1)).toBeTruthy(); + expect(readPatchedData(kid2)).toBeTruthy(); + expect(readPatchedData(kid3)).toBeTruthy(); }); it('should only monkey-patch immediate child nodes in an embedded template container', () => { @@ -364,16 +363,16 @@ describe('element discovery', () => { const [section, div1, p, div2] = Array.from(host.querySelectorAll('section, div, p')); expect(section.nodeName.toLowerCase()).toBe('section'); - expect(section[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); + expect(readPatchedData(section)).toBeTruthy(); expect(div1.nodeName.toLowerCase()).toBe('div'); - expect(div1[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); + expect(readPatchedData(div1)).toBeTruthy(); expect(p.nodeName.toLowerCase()).toBe('p'); - expect(p[MONKEY_PATCH_KEY_NAME]).toBeFalsy(); + expect(readPatchedData(p)).toBeFalsy(); expect(div2.nodeName.toLowerCase()).toBe('div'); - expect(div2[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); + expect(readPatchedData(div2)).toBeTruthy(); }); it('should return a context object from a given dom node', () => { @@ -436,11 +435,11 @@ describe('element discovery', () => { fixture.update(); const section = fixture.hostElement.querySelector('section')! as any; - const result1 = section[MONKEY_PATCH_KEY_NAME]; + const result1 = readPatchedData(section); expect(Array.isArray(result1)).toBeTruthy(); const context = getLContext(section)!; - const result2 = section[MONKEY_PATCH_KEY_NAME]; + const result2 = readPatchedData(section) as any; expect(Array.isArray(result2)).toBeFalsy(); expect(result2).toBe(context); @@ -471,14 +470,14 @@ describe('element discovery', () => { fixture.update(); const section = fixture.hostElement.querySelector('section')! as any; - expect(section[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); + expect(readPatchedData(section)).toBeTruthy(); const p = fixture.hostElement.querySelector('p')! as any; - expect(p[MONKEY_PATCH_KEY_NAME]).toBeFalsy(); + expect(readPatchedData(p)).toBeFalsy(); const pContext = getLContext(p)!; expect(pContext.native).toBe(p); - expect(p[MONKEY_PATCH_KEY_NAME]).toBe(pContext); + expect(readPatchedData(p)).toBe(pContext); }); it('should be able to pull in element context data even if the element is decorated using styling', @@ -503,14 +502,14 @@ describe('element discovery', () => { fixture.update(); const section = fixture.hostElement.querySelector('section')! as any; - const result1 = section[MONKEY_PATCH_KEY_NAME]; + const result1 = readPatchedData(section) as any; expect(Array.isArray(result1)).toBeTruthy(); const elementResult = result1[HEADER_OFFSET]; // first element expect(elementResult).toBe(section); const context = getLContext(section)!; - const result2 = section[MONKEY_PATCH_KEY_NAME]; + const result2 = readPatchedData(section); expect(Array.isArray(result2)).toBeFalsy(); expect(context.native).toBe(section); @@ -596,14 +595,14 @@ describe('element discovery', () => { expect(projectorComp.children).toContain(header); expect(h1.children).toContain(p); - expect(textNode[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); - expect(section[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); - expect(projectorComp[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); - expect(header[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); - expect(h1[MONKEY_PATCH_KEY_NAME]).toBeFalsy(); - expect(p[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); - expect(pText[MONKEY_PATCH_KEY_NAME]).toBeFalsy(); - expect(projectedTextNode[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); + expect(readPatchedData(textNode)).toBeTruthy(); + expect(readPatchedData(section)).toBeTruthy(); + expect(readPatchedData(projectorComp)).toBeTruthy(); + expect(readPatchedData(header)).toBeTruthy(); + expect(readPatchedData(h1)).toBeFalsy(); + expect(readPatchedData(p)).toBeTruthy(); + expect(readPatchedData(pText)).toBeFalsy(); + expect(readPatchedData(projectedTextNode)).toBeTruthy(); const parentContext = getLContext(section)!; const shadowContext = getLContext(header)!; @@ -676,10 +675,10 @@ describe('element discovery', () => { const hostElm = fixture.hostElement; const component = fixture.component; - const componentLView = (component as any)[MONKEY_PATCH_KEY_NAME]; + const componentLView = readPatchedData(component); expect(Array.isArray(componentLView)).toBeTruthy(); - const hostLView = (hostElm as any)[MONKEY_PATCH_KEY_NAME]; + const hostLView = readPatchedData(hostElm) as any; expect(hostLView).toBe(componentLView); const context1 = getLContext(hostElm)!; @@ -745,9 +744,9 @@ describe('element discovery', () => { expect(componentView).toContain(myDir2Instance); expect(componentView).toContain(myDir3Instance); - expect(Array.isArray((myDir1Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy(); - expect(Array.isArray((myDir2Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy(); - expect(Array.isArray((myDir3Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy(); + expect(Array.isArray(readPatchedData(myDir1Instance))).toBeTruthy(); + expect(Array.isArray(readPatchedData(myDir2Instance))).toBeTruthy(); + expect(Array.isArray(readPatchedData(myDir3Instance))).toBeTruthy(); const d1Context = getLContext(myDir1Instance)!; const d2Context = getLContext(myDir2Instance)!; @@ -757,9 +756,9 @@ describe('element discovery', () => { expect(d2Context.lView).toEqual(componentView); expect(d3Context.lView).toEqual(componentView); - expect((myDir1Instance as any)[MONKEY_PATCH_KEY_NAME]).toBe(d1Context); - expect((myDir2Instance as any)[MONKEY_PATCH_KEY_NAME]).toBe(d2Context); - expect((myDir3Instance as any)[MONKEY_PATCH_KEY_NAME]).toBe(d3Context); + expect(readPatchedData(myDir1Instance)).toBe(d1Context); + expect(readPatchedData(myDir2Instance)).toBe(d2Context); + expect(readPatchedData(myDir3Instance)).toBe(d3Context); expect(d1Context.nodeIndex).toEqual(HEADER_OFFSET); expect(d1Context.native).toBe(div1); @@ -829,11 +828,11 @@ describe('element discovery', () => { const childCompHostElm = fixture.hostElement.querySelector('child-comp')! as any; - const lView = childCompHostElm[MONKEY_PATCH_KEY_NAME]; + const lView = readPatchedData(childCompHostElm); expect(Array.isArray(lView)).toBeTruthy(); - expect((myDir1Instance as any)[MONKEY_PATCH_KEY_NAME]).toBe(lView); - expect((myDir2Instance as any)[MONKEY_PATCH_KEY_NAME]).toBe(lView); - expect((childComponentInstance as any)[MONKEY_PATCH_KEY_NAME]).toBe(lView); + expect(readPatchedData(myDir1Instance)).toBe(lView); + expect(readPatchedData(myDir2Instance)).toBe(lView); + expect(readPatchedData(childComponentInstance)).toBe(lView); const childNodeContext = getLContext(childCompHostElm)!; expect(childNodeContext.component).toBeFalsy(); @@ -864,7 +863,7 @@ describe('element discovery', () => { assertMonkeyPatchValueIsLView(childComponentInstance, false); function assertMonkeyPatchValueIsLView(value: any, yesOrNo = true) { - expect(Array.isArray((value as any)[MONKEY_PATCH_KEY_NAME])).toBe(yesOrNo); + expect(Array.isArray(readPatchedData(value))).toBe(yesOrNo); } }); @@ -913,18 +912,18 @@ describe('element discovery', () => { const host = fixture.hostElement; const child = host.querySelector('child-comp') as any; - expect(child[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); + expect(readPatchedData(child)).toBeTruthy(); const context = getLContext(child)!; - expect(child[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); + expect(readPatchedData(child)).toBeTruthy(); const componentData = context.lView[context.nodeIndex]; const component = componentData[CONTEXT]; expect(component instanceof ChildComp).toBeTruthy(); - expect(component[MONKEY_PATCH_KEY_NAME]).toBe(context.lView); + expect(readPatchedData(component)).toBe(context.lView); const componentContext = getLContext(component)!; - expect(component[MONKEY_PATCH_KEY_NAME]).toBe(componentContext); + expect(readPatchedData(component)).toBe(componentContext); expect(componentContext.nodeIndex).toEqual(context.nodeIndex); expect(componentContext.native).toEqual(context.native); expect(componentContext.lView).toEqual(context.lView); diff --git a/packages/core/test/render3/ivy/jit_spec.ts b/packages/core/test/render3/ivy/jit_spec.ts index ff6b1dc442..276156b08c 100644 --- a/packages/core/test/render3/ivy/jit_spec.ts +++ b/packages/core/test/render3/ivy/jit_spec.ts @@ -191,13 +191,14 @@ ivyEnabled && describe('render3 jit', () => { constructor(public token: Token) {} } - const injectorDef: ɵɵInjectorDef = (Module as any).ɵinj; - const instance = injectorDef.factory(); + const factory: FactoryFn = (Module as any).ɵfac; + const instance = factory(); // Since the instance was created outside of an injector using the module, the // injection will use the default provider, not the provider from the module. expect(instance.token).toBe('default'); + const injectorDef: ɵɵInjectorDef = (Module as any).ɵinj; expect(injectorDef.providers).toEqual([{provide: Token, useValue: 'test'}]); }); diff --git a/packages/core/test/render3/jit/directive_spec.ts b/packages/core/test/render3/jit/directive_spec.ts index 1e3aaee15c..5b41d82ec0 100644 --- a/packages/core/test/render3/jit/directive_spec.ts +++ b/packages/core/test/render3/jit/directive_spec.ts @@ -51,13 +51,15 @@ describe('jit directive helper functions', () => { isViewQuery: false, read: undefined, static: false, + emitDistinctChangesOnly: false, })).toEqual({ propertyName: 'propName', predicate: ['localRef'], descendants: false, first: false, read: null, - static: false + static: false, + emitDistinctChangesOnly: false, }); }); @@ -69,13 +71,15 @@ describe('jit directive helper functions', () => { isViewQuery: true, read: undefined, static: false, + emitDistinctChangesOnly: false, })).toEqual({ propertyName: 'propName', predicate: ['foo', 'bar', 'baz'], descendants: true, first: true, read: null, - static: false + static: false, + emitDistinctChangesOnly: false, }); }); @@ -88,7 +92,8 @@ describe('jit directive helper functions', () => { first: true, isViewQuery: true, read: Directive, - static: false + static: false, + emitDistinctChangesOnly: false, }); expect(converted.predicate).toEqual(Directive); diff --git a/packages/core/test/render3/jit_environment_spec.ts b/packages/core/test/render3/jit_environment_spec.ts index 4f09a0910b..e3c12a83eb 100644 --- a/packages/core/test/render3/jit_environment_spec.ts +++ b/packages/core/test/render3/jit_environment_spec.ts @@ -12,12 +12,13 @@ import {Identifiers} from '@angular/compiler/src/render3/r3_identifiers'; import {angularCoreEnv} from '../../src/render3/jit/environment'; const INTERFACE_EXCEPTIONS = new Set([ - 'ɵɵComponentDefWithMeta', - 'ɵɵDirectiveDefWithMeta', + 'ɵɵComponentDeclaration', + 'ɵɵDirectiveDeclaration', + 'ɵɵInjectorDeclaration', 'ɵɵInjectorDef', - 'ɵɵNgModuleDefWithMeta', - 'ɵɵPipeDefWithMeta', - 'ɵɵFactoryDef', + 'ɵɵNgModuleDeclaration', + 'ɵɵPipeDeclaration', + 'ɵɵFactoryDeclaration', 'ModuleWithProviders', ]); @@ -28,6 +29,11 @@ const INTERFACE_EXCEPTIONS = new Set([ const PARTIAL_ONLY = new Set([ 'ɵɵngDeclareDirective', 'ɵɵngDeclareComponent', + 'ɵɵngDeclareFactory', + 'ɵɵngDeclareInjector', + 'ɵɵngDeclareNgModule', + 'ɵɵngDeclarePipe', + 'ɵɵFactoryTarget', 'ChangeDetectionStrategy', 'ViewEncapsulation', ]); diff --git a/packages/core/test/render3/perf/BUILD.bazel b/packages/core/test/render3/perf/BUILD.bazel index 22cd841d2d..144b8f5eb7 100644 --- a/packages/core/test/render3/perf/BUILD.bazel +++ b/packages/core/test/render3/perf/BUILD.bazel @@ -255,3 +255,16 @@ ng_benchmark( name = "view_destroy_hook", bundle = ":view_destroy_hook_lib", ) + +ng_rollup_bundle( + name = "render_stringify_lib", + entry_point = ":render_stringify/index.ts", + deps = [ + ":perf_lib", + ], +) + +ng_benchmark( + name = "render_stringify", + bundle = ":render_stringify_lib", +) diff --git a/packages/core/test/render3/providers_spec.ts b/packages/core/test/render3/providers_spec.ts index 5d2f6f5077..82d505b664 100644 --- a/packages/core/test/render3/providers_spec.ts +++ b/packages/core/test/render3/providers_spec.ts @@ -370,8 +370,7 @@ describe('providers', () => { describe('single', () => { class MyModule { - static ɵinj = ɵɵdefineInjector( - {factory: () => new MyModule(), providers: [{provide: String, useValue: 'From module'}]}); + static ɵinj = ɵɵdefineInjector({providers: [{provide: String, useValue: 'From module'}]}); } describe('without directives', () => { @@ -637,10 +636,8 @@ describe('providers', () => { describe('multi', () => { class MyModule { - static ɵinj = ɵɵdefineInjector({ - factory: () => new MyModule(), - providers: [{provide: String, useValue: 'From module', multi: true}] - }); + static ɵinj = + ɵɵdefineInjector({providers: [{provide: String, useValue: 'From module', multi: true}]}); } describe('without directives', () => { @@ -956,10 +953,7 @@ describe('providers', () => { it('should work with a module', () => { class MyModule { - static ɵinj = ɵɵdefineInjector({ - factory: () => new MyModule(), - providers: [{provide: String, useValue: 'From module'}] - }); + static ɵinj = ɵɵdefineInjector({providers: [{provide: String, useValue: 'From module'}]}); } @Injectable({providedIn: MyModule}) @@ -1084,7 +1078,6 @@ describe('providers', () => { class MyAppModule { static ɵinj = ɵɵdefineInjector({ - factory: () => new MyAppModule(), imports: [], providers: [ @@ -1169,8 +1162,7 @@ describe('providers', () => { describe('injection flags', () => { class MyModule { - static ɵinj = ɵɵdefineInjector( - {factory: () => new MyModule(), providers: [{provide: String, useValue: 'Module'}]}); + static ɵinj = ɵɵdefineInjector({providers: [{provide: String, useValue: 'Module'}]}); } it('should not fall through to ModuleInjector if flags limit the scope', () => { expectProvidersScenario({ diff --git a/packages/core/test/render3/query_spec.ts b/packages/core/test/render3/query_spec.ts index df8c46bf46..b0bd6ece63 100644 --- a/packages/core/test/render3/query_spec.ts +++ b/packages/core/test/render3/query_spec.ts @@ -7,6 +7,7 @@ */ import {ElementRef, QueryList, TemplateRef, ViewContainerRef} from '@angular/core'; +import {QueryFlags} from '@angular/core/src/render3/interfaces/query'; import {HEADER_OFFSET} from '@angular/core/src/render3/interfaces/view'; import {AttributeMarker, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵProvidersFeature} from '../../src/render3/index'; @@ -80,8 +81,8 @@ describe('query', () => { 2, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(Child, false); - ɵɵviewQuery(Child, true); + ɵɵviewQuery(Child, QueryFlags.none); + ɵɵviewQuery(Child, QueryFlags.descendants); } if (rf & RenderFlags.Update) { let tmp: any; @@ -119,7 +120,7 @@ describe('query', () => { 1, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(Child, false, ElementRef); + ɵɵviewQuery(Child, QueryFlags.none, ElementRef); } if (rf & RenderFlags.Update) { let tmp: any; @@ -158,7 +159,7 @@ describe('query', () => { 1, 0, [Child, OtherChild], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(Child, false, OtherChild); + ɵɵviewQuery(Child, QueryFlags.none, OtherChild); } if (rf & RenderFlags.Update) { let tmp: any; @@ -193,7 +194,7 @@ describe('query', () => { 1, 0, [Child, OtherChild], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(Child, false, OtherChild); + ɵɵviewQuery(Child, QueryFlags.none, OtherChild); } if (rf & RenderFlags.Update) { let tmp: any; @@ -263,9 +264,9 @@ describe('query', () => { viewQuery: function(rf: RenderFlags, ctx: App) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(MyDirective, false); - ɵɵviewQuery(Service, false); - ɵɵviewQuery(Alias, false); + ɵɵviewQuery(MyDirective, QueryFlags.none); + ɵɵviewQuery(Service, QueryFlags.none); + ɵɵviewQuery(Alias, QueryFlags.none); } if (rf & RenderFlags.Update) { let tmp: any; @@ -315,7 +316,7 @@ describe('query', () => { function(rf: RenderFlags, ctx: App) { let tmp: any; if (rf & RenderFlags.Create) { - ɵɵviewQuery(MyDirective, false, Alias); + ɵɵviewQuery(MyDirective, QueryFlags.none, Alias); } if (rf & RenderFlags.Update) { ɵɵqueryRefresh(tmp = ɵɵloadQuery>()) && @@ -353,7 +354,7 @@ describe('query', () => { 3, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], false); + ɵɵviewQuery(['foo'], QueryFlags.none); } if (rf & RenderFlags.Update) { let tmp: any; @@ -392,8 +393,8 @@ describe('query', () => { 4, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], false); - ɵɵviewQuery(['bar'], false); + ɵɵviewQuery(['foo'], QueryFlags.none); + ɵɵviewQuery(['bar'], QueryFlags.none); } if (rf & RenderFlags.Update) { let tmp: any; @@ -441,7 +442,7 @@ describe('query', () => { 5, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo', 'bar'], false); + ɵɵviewQuery(['foo', 'bar'], QueryFlags.none); } if (rf & RenderFlags.Update) { let tmp: any; @@ -479,7 +480,7 @@ describe('query', () => { 3, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], false); + ɵɵviewQuery(['foo'], QueryFlags.none); } if (rf & RenderFlags.Update) { let tmp: any; @@ -517,7 +518,7 @@ describe('query', () => { 2, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], false, ElementRef); + ɵɵviewQuery(['foo'], QueryFlags.none, ElementRef); } if (rf & RenderFlags.Update) { let tmp: any; @@ -554,7 +555,7 @@ describe('query', () => { 2, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], true); + ɵɵviewQuery(['foo'], QueryFlags.descendants); } if (rf & RenderFlags.Update) { let tmp: any; @@ -588,7 +589,7 @@ describe('query', () => { 2, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], false, ViewContainerRef); + ɵɵviewQuery(['foo'], QueryFlags.none, ViewContainerRef); } if (rf & RenderFlags.Update) { let tmp: any; @@ -621,7 +622,7 @@ describe('query', () => { 2, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], false, ViewContainerRef); + ɵɵviewQuery(['foo'], QueryFlags.none, ViewContainerRef); } if (rf & RenderFlags.Update) { let tmp: any; @@ -655,7 +656,7 @@ describe('query', () => { 2, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], false, ElementRef); + ɵɵviewQuery(['foo'], QueryFlags.none, ElementRef); } if (rf & RenderFlags.Update) { let tmp: any; @@ -690,7 +691,7 @@ describe('query', () => { 2, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], false); + ɵɵviewQuery(['foo'], QueryFlags.none); } if (rf & RenderFlags.Update) { let tmp: any; @@ -724,7 +725,7 @@ describe('query', () => { 2, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], false, TemplateRef); + ɵɵviewQuery(['foo'], QueryFlags.none, TemplateRef); } if (rf & RenderFlags.Update) { let tmp: any; @@ -763,7 +764,7 @@ describe('query', () => { 2, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], true); + ɵɵviewQuery(['foo'], QueryFlags.descendants); } if (rf & RenderFlags.Update) { let tmp: any; @@ -810,7 +811,7 @@ describe('query', () => { 2, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], true); + ɵɵviewQuery(['foo'], QueryFlags.descendants); } if (rf & RenderFlags.Update) { let tmp: any; @@ -850,7 +851,7 @@ describe('query', () => { 2, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], true); + ɵɵviewQuery(['foo'], QueryFlags.descendants); } if (rf & RenderFlags.Update) { let tmp: any; @@ -891,7 +892,7 @@ describe('query', () => { 3, 0, [Child1, Child2], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo', 'bar'], true); + ɵɵviewQuery(['foo', 'bar'], QueryFlags.descendants); } if (rf & RenderFlags.Update) { let tmp: any; @@ -932,8 +933,8 @@ describe('query', () => { 3, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], true); - ɵɵviewQuery(['bar'], true); + ɵɵviewQuery(['foo'], QueryFlags.descendants); + ɵɵviewQuery(['bar'], QueryFlags.descendants); } if (rf & RenderFlags.Update) { let tmp: any; @@ -977,7 +978,7 @@ describe('query', () => { 2, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], false, ElementRef); + ɵɵviewQuery(['foo'], QueryFlags.none, ElementRef); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1017,7 +1018,7 @@ describe('query', () => { 3, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo', 'bar'], false); + ɵɵviewQuery(['foo', 'bar'], QueryFlags.none); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1053,7 +1054,7 @@ describe('query', () => { 2, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], false, Child); + ɵɵviewQuery(['foo'], QueryFlags.none, Child); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1088,7 +1089,7 @@ describe('query', () => { 1, 0, [Child, OtherChild], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(Child, false, OtherChild); + ɵɵviewQuery(Child, QueryFlags.none, OtherChild); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1123,7 +1124,7 @@ describe('query', () => { 1, 0, [Child, OtherChild], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(OtherChild, false, Child); + ɵɵviewQuery(OtherChild, QueryFlags.none, Child); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1155,7 +1156,7 @@ describe('query', () => { 1, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(TemplateRef as any, false, ElementRef); + ɵɵviewQuery(TemplateRef as any, QueryFlags.none, ElementRef); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1188,7 +1189,7 @@ describe('query', () => { 2, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], false, Child); + ɵɵviewQuery(['foo'], QueryFlags.none, Child); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1223,7 +1224,7 @@ describe('query', () => { 1, 0, [Child], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(TemplateRef as any, false); + ɵɵviewQuery(TemplateRef as any, QueryFlags.none); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1270,8 +1271,8 @@ describe('query', () => { 6, 0, [], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(TemplateRef as any, false); - ɵɵviewQuery(TemplateRef as any, false, ElementRef); + ɵɵviewQuery(TemplateRef as any, QueryFlags.none); + ɵɵviewQuery(TemplateRef as any, QueryFlags.none, ElementRef); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1334,7 +1335,7 @@ describe('query', () => { 3, 0, [SomeDir], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], true); + ɵɵviewQuery(['foo'], QueryFlags.descendants); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1376,7 +1377,7 @@ describe('query', () => { contentQueries: (rf: RenderFlags, ctx: any, dirIndex: number) => { if (rf & RenderFlags.Create) { - ɵɵcontentQuery(dirIndex, ['foo'], true); + ɵɵcontentQuery(dirIndex, ['foo'], QueryFlags.descendants); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1463,7 +1464,7 @@ describe('query', () => { 5, 0, [WithContentDirective], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo', 'bar'], true); + ɵɵviewQuery(['foo', 'bar'], QueryFlags.descendants); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1505,7 +1506,7 @@ describe('query', () => { 5, 0, [WithContentDirective], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['bar'], true); + ɵɵviewQuery(['bar'], QueryFlags.descendants); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1533,7 +1534,7 @@ describe('query', () => { // @ContentChildren('foo, bar, baz', {descendants: true}) // fooBars: QueryList; if (rf & RenderFlags.Create) { - ɵɵcontentQuery(dirIndex, ['foo', 'bar', 'baz'], true); + ɵɵcontentQuery(dirIndex, ['foo', 'bar', 'baz'], QueryFlags.descendants); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1602,7 +1603,7 @@ describe('query', () => { // @ContentChildren('foo', {descendants: true}) // fooBars: QueryList; if (rf & RenderFlags.Create) { - ɵɵcontentQuery(dirIndex, ['foo'], false); + ɵɵcontentQuery(dirIndex, ['foo'], QueryFlags.none); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1662,7 +1663,7 @@ describe('query', () => { // @ContentChildren('foo', {descendants: true}) // fooBars: QueryList; if (rf & RenderFlags.Create) { - ɵɵcontentQuery(dirIndex, ['foo'], false); + ɵɵcontentQuery(dirIndex, ['foo'], QueryFlags.none); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1726,7 +1727,7 @@ describe('query', () => { // @ContentChildren('foo', {descendants: false}) // foos: QueryList; if (rf & RenderFlags.Create) { - ɵɵcontentQuery(dirIndex, ['foo'], false); + ɵɵcontentQuery(dirIndex, ['foo'], QueryFlags.none); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1748,7 +1749,7 @@ describe('query', () => { // @ContentChildren('foo', {descendants: true}) // foos: QueryList; if (rf & RenderFlags.Create) { - ɵɵcontentQuery(dirIndex, ['foo'], true); + ɵɵcontentQuery(dirIndex, ['foo'], QueryFlags.descendants); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1824,7 +1825,7 @@ describe('query', () => { // @ContentChildren(TextDirective, {descendants: true}) // texts: QueryList; if (rf & RenderFlags.Create) { - ɵɵcontentQuery(dirIndex, TextDirective, true); + ɵɵcontentQuery(dirIndex, TextDirective, QueryFlags.descendants); } if (rf & RenderFlags.Update) { let tmp: any; @@ -1910,7 +1911,7 @@ describe('query', () => { function(rf: RenderFlags, ctx: ViewQueryComponent) { let tmp: any; if (rf & RenderFlags.Create) { - ɵɵviewQuery(TextDirective, true); + ɵɵviewQuery(TextDirective, QueryFlags.descendants); } if (rf & RenderFlags.Update) { ɵɵqueryRefresh(tmp = ɵɵloadQuery>()) && diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index da369c4426..3c9d467393 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -8,6 +8,7 @@ import {RendererStyleFlags2, RendererType2} from '@angular/core'; import {ChangeDetectorRef} from '@angular/core/src/change_detection/change_detector_ref'; +import {InjectFlags} from '@angular/core/src/di'; import {Provider} from '@angular/core/src/di/interface/provider'; import {ElementRef} from '@angular/core/src/linker/element_ref'; import {TemplateRef} from '@angular/core/src/linker/template_ref'; @@ -294,13 +295,13 @@ export function renderTemplate( providedRendererFactory, renderer, null, null); enterView(hostLView); - const def: ComponentDef = ɵɵdefineComponent({ - type: Object, - template: templateFn, - decls: decls, - vars: vars, - consts: consts, - }); + const def = ɵɵdefineComponent({ + type: Object, + template: templateFn, + decls: decls, + vars: vars, + consts: consts, + }) as ComponentDef; def.directiveDefs = directives || null; def.pipeDefs = pipes || null; @@ -459,7 +460,8 @@ export function enableIvyInjectableFactories() { (ElementRef as any)[NG_ELEMENT_ID] = () => R3_ELEMENT_REF_FACTORY(); (TemplateRef as any)[NG_ELEMENT_ID] = () => R3_TEMPLATE_REF_FACTORY(); (ViewContainerRef as any)[NG_ELEMENT_ID] = () => R3_VIEW_CONTAINER_REF_FACTORY(); - (ChangeDetectorRef as any)[NG_ELEMENT_ID] = () => R3_CHANGE_DETECTOR_REF_FACTORY(); + (ChangeDetectorRef as any)[NG_ELEMENT_ID] = (flags: InjectFlags) => + R3_CHANGE_DETECTOR_REF_FACTORY(flags); (Renderer2 as any)[NG_ELEMENT_ID] = () => R3_RENDERER2_FACTORY(); } diff --git a/packages/core/test/render3/view_container_ref_spec.ts b/packages/core/test/render3/view_container_ref_spec.ts index a837c93c27..877bec2de8 100644 --- a/packages/core/test/render3/view_container_ref_spec.ts +++ b/packages/core/test/render3/view_container_ref_spec.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import {QueryFlags} from '@angular/core/src/render3/interfaces/query'; import {HEADER_OFFSET} from '@angular/core/src/render3/interfaces/view'; import {ChangeDetectorRef, Component as _Component, ComponentFactoryResolver, ElementRef, QueryList, TemplateRef, ViewContainerRef, ViewRef} from '../../src/core'; import {ViewEncapsulation} from '../../src/metadata'; @@ -368,7 +369,7 @@ describe('ViewContainerRef', () => { viewQuery: function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], true); + ɵɵviewQuery(['foo'], QueryFlags.descendants); } if (rf & RenderFlags.Update) { let tmp: any; diff --git a/packages/core/test/sanitization/html_sanitizer_spec.ts b/packages/core/test/sanitization/html_sanitizer_spec.ts index aae4a18421..01d805c9be 100644 --- a/packages/core/test/sanitization/html_sanitizer_spec.ts +++ b/packages/core/test/sanitization/html_sanitizer_spec.ts @@ -252,5 +252,14 @@ function sanitizeHtml(defaultDoc: any, unsafeHtmlInput: string): string { .toMatch(/CLICKME<\/a>/); }); } + + if (isDOMParserAvailable()) { + it('should work even if DOMParser returns a null body', () => { + // Simulate `DOMParser.parseFromString()` returning a null body. + // See https://github.com/angular/angular/issues/39834 + spyOn(window.DOMParser.prototype, 'parseFromString').and.returnValue({body: null} as any); + expect(sanitizeHtml(defaultDoc, 'Hello, World')).toEqual('Hello, World'); + }); + } }); } diff --git a/packages/core/test/sanitization/sanitization_spec.ts b/packages/core/test/sanitization/sanitization_spec.ts index 20ef6c96f5..2ce9a6447c 100644 --- a/packages/core/test/sanitization/sanitization_spec.ts +++ b/packages/core/test/sanitization/sanitization_spec.ts @@ -12,7 +12,7 @@ import {LView} from '@angular/core/src/render3/interfaces/view'; import {enterView, leaveView} from '@angular/core/src/render3/state'; import {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl} from '../../src/sanitization/bypass'; -import {getUrlSanitizer, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl, ɵɵsanitizeUrlOrResourceUrl} from '../../src/sanitization/sanitization'; +import {getUrlSanitizer, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl, ɵɵsanitizeUrlOrResourceUrl, ɵɵtrustConstantHtml, ɵɵtrustConstantResourceUrl} from '../../src/sanitization/sanitization'; import {SecurityContext} from '../../src/sanitization/security'; function fakeLView(): LView { @@ -134,4 +134,13 @@ describe('sanitization', () => { expect(ɵɵsanitizeUrlOrResourceUrl(bypassSanitizationTrustUrl('javascript:true'), 'a', 'href')) .toEqual('javascript:true'); }); + + it('should only trust constant strings from template literal tags without interpolation', () => { + expect(ɵɵtrustConstantHtml`

    good

    `.toString()).toEqual('

    good

    '); + expect(ɵɵtrustConstantResourceUrl`http://good.com`.toString()).toEqual('http://good.com'); + expect(() => (ɵɵtrustConstantHtml as any) `

    ${'evil'}

    `) + .toThrowError(/Unexpected interpolation in trusted HTML constant/); + expect(() => (ɵɵtrustConstantResourceUrl as any) `http://${'evil'}.com`) + .toThrowError(/Unexpected interpolation in trusted URL constant/); + }); }); diff --git a/packages/core/test/strict_types/inheritance_spec.ts b/packages/core/test/strict_types/inheritance_spec.ts index a21ce06936..e376872ea7 100644 --- a/packages/core/test/strict_types/inheritance_spec.ts +++ b/packages/core/test/strict_types/inheritance_spec.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {ɵɵComponentDefWithMeta, ɵɵPipeDefWithMeta as PipeDefWithMeta} from '@angular/core'; +import {ɵɵComponentDeclaration, ɵɵPipeDeclaration} from '@angular/core'; declare class SuperComponent { - static ɵcmp: ɵɵComponentDefWithMeta; + static ɵcmp: ɵɵComponentDeclaration; } declare class SubComponent extends SuperComponent { @@ -18,17 +18,17 @@ declare class SubComponent extends SuperComponent { // would produce type errors when the "strictFunctionTypes" option is enabled. onlyInSubtype: string; - static ɵcmp: ɵɵComponentDefWithMeta; + static ɵcmp: ɵɵComponentDeclaration; } declare class SuperPipe { - static ɵpipe: PipeDefWithMeta; + static ɵpipe: ɵɵPipeDeclaration; } declare class SubPipe extends SuperPipe { onlyInSubtype: string; - static ɵpipe: PipeDefWithMeta; + static ɵpipe: ɵɵPipeDeclaration; } describe('inheritance strict type checking', () => { diff --git a/packages/core/test/test_bed_spec.ts b/packages/core/test/test_bed_spec.ts index e1d78c0cba..3031801161 100644 --- a/packages/core/test/test_bed_spec.ts +++ b/packages/core/test/test_bed_spec.ts @@ -627,12 +627,6 @@ describe('TestBed', () => { }); it('should handle overrides for a provider that has `ChangeDetectorRef` as a dependency', () => { - // Note: we specifically check an @Injectable with `ChangeDetectorRef` here due to the fact that - // in Ivy there is a special instruction that injects `ChangeDetectorRef` token for Pipes - // (ɵɵinjectPipeChangeDetectorRef) and using that function for other types causes problems, - // for example when we try to override an @Injectable. The test below captures a use-case that - // triggers a problem in case incompatible function is used to inject `ChangeDetectorRef` as a - // dependency. @Injectable({providedIn: 'root'}) class MyService { token = 'original'; @@ -1270,8 +1264,7 @@ describe('TestBed', () => { // because it depends on code generated by ngcc. class TestingModule { static ɵmod = defineNgModule({type: TestingModule}); - static ɵinj = - defineInjector({factory: () => new TestingModule(), imports: [DependencyModule]}); + static ɵinj = defineInjector({imports: [DependencyModule]}); } setNgModuleScope(TestingModule, {imports: () => [DependencyModule]}); diff --git a/packages/core/test/util/global_spec.ts b/packages/core/test/util/global_spec.ts index 0ea8522378..e7f91e055c 100644 --- a/packages/core/test/util/global_spec.ts +++ b/packages/core/test/util/global_spec.ts @@ -8,9 +8,6 @@ import {global} from '../../src/util/global'; -// Not yet available in TypeScript: https://github.com/Microsoft/TypeScript/pull/29332 -declare var globalThis: any /** TODO #9100 */; - { describe('global', () => { it('should be global this value', () => { diff --git a/packages/core/test/view/element_spec.ts b/packages/core/test/view/element_spec.ts index d2228dbb69..9977909ff4 100644 --- a/packages/core/test/view/element_spec.ts +++ b/packages/core/test/view/element_spec.ts @@ -87,8 +87,8 @@ const removeEventListener = 'removeEventListener'; Services.checkAndUpdateView(view); const el = rootNodes[0]; - expect(getDOM().getProperty(el, 'title')).toBe('v1'); - expect(getDOM().getProperty(el, 'value')).toBe('v2'); + expect(el.title).toBe('v1'); + expect(el.value).toBe('v2'); }); }); }); diff --git a/packages/core/test/view/helper.ts b/packages/core/test/view/helper.ts index d362f6646f..52cd8ae419 100644 --- a/packages/core/test/view/helper.ts +++ b/packages/core/test/view/helper.ts @@ -12,7 +12,7 @@ import {ArgumentType, initServicesIfNeeded, NodeCheckFn, NodeDef, rootRenderNode import {TestBed} from '@angular/core/testing'; export function isBrowser() { - return getDOM().supportsDOMEvents(); + return getDOM().supportsDOMEvents; } export const ARG_TYPE_VALUES = [ArgumentType.Inline, ArgumentType.Dynamic]; diff --git a/packages/core/test/view/ng_module_spec.ts b/packages/core/test/view/ng_module_spec.ts index 937568cfb6..29daf7da35 100644 --- a/packages/core/test/view/ng_module_spec.ts +++ b/packages/core/test/view/ng_module_spec.ts @@ -10,7 +10,7 @@ import {NgModuleRef, ɵINJECTOR_SCOPE as INJECTOR_SCOPE} from '@angular/core'; import {inject, InjectFlags} from '@angular/core/src/di'; import {Injector} from '@angular/core/src/di/injector'; import {INJECTOR} from '@angular/core/src/di/injector_token'; -import {ɵɵdefineInjectable, ɵɵInjectableDef} from '@angular/core/src/di/interface/defs'; +import {ɵɵdefineInjectable} 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'; @@ -25,7 +25,7 @@ class MyChildModule {} class NotMyModule {} class Bar { - static ɵprov: ɵɵInjectableDef = ɵɵdefineInjectable({ + static ɵprov = ɵɵdefineInjectable({ token: Bar, factory: () => new Bar(), providedIn: MyModule, @@ -33,7 +33,7 @@ class Bar { } class Baz { - static ɵprov: ɵɵInjectableDef = ɵɵdefineInjectable({ + static ɵprov = ɵɵdefineInjectable({ token: Baz, factory: () => new Baz(), providedIn: NotMyModule, @@ -43,7 +43,7 @@ class Baz { class HasNormalDep { constructor(public foo: Foo) {} - static ɵprov: ɵɵInjectableDef = ɵɵdefineInjectable({ + static ɵprov = ɵɵdefineInjectable({ token: HasNormalDep, factory: () => new HasNormalDep(inject(Foo)), providedIn: MyModule, @@ -53,7 +53,7 @@ class HasNormalDep { class HasDefinedDep { constructor(public bar: Bar) {} - static ɵprov: ɵɵInjectableDef = ɵɵdefineInjectable({ + static ɵprov = ɵɵdefineInjectable({ token: HasDefinedDep, factory: () => new HasDefinedDep(inject(Bar)), providedIn: MyModule, @@ -63,7 +63,7 @@ class HasDefinedDep { class HasOptionalDep { constructor(public baz: Baz|null) {} - static ɵprov: ɵɵInjectableDef = ɵɵdefineInjectable({ + static ɵprov = ɵɵdefineInjectable({ token: HasOptionalDep, factory: () => new HasOptionalDep(inject(Baz, InjectFlags.Optional)), providedIn: MyModule, @@ -71,7 +71,7 @@ class HasOptionalDep { } class ChildDep { - static ɵprov: ɵɵInjectableDef = ɵɵdefineInjectable({ + static ɵprov = ɵɵdefineInjectable({ token: ChildDep, factory: () => new ChildDep(), providedIn: MyChildModule, @@ -80,7 +80,7 @@ class ChildDep { class FromChildWithOptionalDep { constructor(public baz: Baz|null) {} - static ɵprov: ɵɵInjectableDef = ɵɵdefineInjectable({ + static ɵprov = ɵɵdefineInjectable({ token: FromChildWithOptionalDep, factory: () => new FromChildWithOptionalDep(inject(Baz, InjectFlags.Default)), providedIn: MyChildModule, @@ -91,7 +91,7 @@ class FromChildWithSkipSelfDep { constructor( public skipSelfChildDep: ChildDep|null, public selfChildDep: ChildDep|null, public optionalSelfBar: Bar|null) {} - static ɵprov: ɵɵInjectableDef = ɵɵdefineInjectable({ + static ɵprov = ɵɵdefineInjectable({ token: FromChildWithSkipSelfDep, factory: () => new FromChildWithSkipSelfDep( inject(ChildDep, InjectFlags.SkipSelf|InjectFlags.Optional), @@ -228,7 +228,7 @@ describe('NgModuleRef_ injector', () => { Service.destroyed++; } - static ɵprov: ɵɵInjectableDef = ɵɵdefineInjectable({ + static ɵprov = ɵɵdefineInjectable({ token: Service, factory: () => new Service(), providedIn: 'root', diff --git a/packages/core/test/zone/ng_zone_spec.ts b/packages/core/test/zone/ng_zone_spec.ts index 3f11518a0e..dd9b6cc5ad 100644 --- a/packages/core/test/zone/ng_zone_spec.ts +++ b/packages/core/test/zone/ng_zone_spec.ts @@ -982,6 +982,7 @@ function commonTests() { describe('shouldCoalesceEventChangeDetection = true, shouldCoalesceRunChangeDetection = false', () => { let nativeRequestAnimationFrame: (fn: FrameRequestCallback) => void; + let nativeSetTimeout: any = (global as any)[Zone.__symbol__('setTimeout')]; if (!(global as any).requestAnimationFrame) { nativeRequestAnimationFrame = function(fn: Function) { (global as any)[Zone.__symbol__('setTimeout')](fn, 16); @@ -1042,6 +1043,32 @@ function commonTests() { }); }); + it('should only emit onMicroTaskEmpty once within the same event loop for ngZone.run in onMicrotaskEmpty subscription', + (done: DoneFn) => { + const tasks: Task[] = []; + coalesceZone.onMicrotaskEmpty.subscribe(() => { + coalesceZone.run(() => {}); + }); + coalesceZone.run(() => { + tasks.push(Zone.current.scheduleEventTask('myEvent', () => { + logs.push('eventTask1'); + }, undefined, () => {})); + }); + coalesceZone.run(() => { + tasks.push(Zone.current.scheduleEventTask('myEvent', () => { + logs.push('eventTask2'); + }, undefined, () => {})); + }); + tasks.forEach(t => t.invoke()); + expect(logs).toEqual(['microTask empty', 'microTask empty', 'eventTask1', 'eventTask2']); + nativeSetTimeout(() => { + expect(logs).toEqual([ + 'microTask empty', 'microTask empty', 'eventTask1', 'eventTask2', 'microTask empty' + ]); + done(); + }, 100); + }); + it('should emit onMicroTaskEmpty once within the same event loop for not only event tasks, but event tasks are before other tasks', (done: DoneFn) => { const tasks: Task[] = []; @@ -1094,6 +1121,7 @@ function commonTests() { describe('shouldCoalesceRunChangeDetection = true', () => { let nativeRequestAnimationFrame: (fn: FrameRequestCallback) => void; + let nativeSetTimeout: any = (global as any)[Zone.__symbol__('setTimeout')]; if (!(global as any).requestAnimationFrame) { nativeRequestAnimationFrame = function(fn: Function) { (global as any)[Zone.__symbol__('setTimeout')](fn, 16); @@ -1139,6 +1167,20 @@ function commonTests() { }); }); + it('should only emit onMicroTaskEmpty once within the same event loop for ngZone.run in onMicrotaskEmpty subscription', + (done: DoneFn) => { + coalesceZone.onMicrotaskEmpty.subscribe(() => { + coalesceZone.run(() => {}); + }); + coalesceZone.run(() => {}); + coalesceZone.run(() => {}); + expect(logs).toEqual([]); + nativeSetTimeout(() => { + expect(logs).toEqual(['microTask empty']); + done(); + }, 100); + }); + it('should only emit onMicroTaskEmpty once within the same event loop for multiple tasks', (done: DoneFn) => { const tasks: Task[] = []; diff --git a/packages/core/testing/src/async.ts b/packages/core/testing/src/async.ts index 288f48ca21..e43372ec42 100644 --- a/packages/core/testing/src/async.ts +++ b/packages/core/testing/src/async.ts @@ -10,13 +10,8 @@ * complete when all asynchronous calls within this zone are done. Can be used * to wrap an {@link inject} call. * - * 把一个测试函数包装进一个异步测试 Zone。当该 Zone 中的所有异步调用都已完成时,该测试将会自动完成。 - * 可用于包装 {@link inject} 调用。 - * * Example: * - * 例子: - * * ``` * it('...', waitForAsync(inject([AClass], (object) => { * object.doSomething.then(() => { @@ -33,7 +28,7 @@ export function waitForAsync(fn: Function): (done: any) => any { return function() { return Promise.reject( 'Zone is needed for the waitForAsync() test helper but could not be found. ' + - 'Please make sure that your environment includes zone.js/dist/zone.js'); + 'Please make sure that your environment includes zone.js'); }; } const asyncTest = _Zone && _Zone[_Zone.__symbol__('asyncTest')]; @@ -43,15 +38,12 @@ export function waitForAsync(fn: Function): (done: any) => any { return function() { return Promise.reject( 'zone-testing.js is needed for the async() test helper but could not be found. ' + - 'Please make sure that your environment includes zone.js/dist/zone-testing.js'); + 'Please make sure that your environment includes zone.js/testing'); }; } /** * @deprecated use `waitForAsync()`, (expected removal in v12) - * - * 改用 `waitForAsync()`(将在 v12 中删除) - * * @see {@link waitForAsync} * @publicApi * */ diff --git a/packages/core/testing/src/component_fixture.ts b/packages/core/testing/src/component_fixture.ts index 421c5acfb5..6cd2853d61 100644 --- a/packages/core/testing/src/component_fixture.ts +++ b/packages/core/testing/src/component_fixture.ts @@ -12,48 +12,31 @@ import {ChangeDetectorRef, ComponentRef, DebugElement, ElementRef, getDebugNode, /** * Fixture for debugging and testing a component. * - * 用于调试和测试组件的夹具。 - * * @publicApi */ export class ComponentFixture { /** * The DebugElement associated with the root element of this component. - * - * 与该组件的根元素关联的 DebugElement。 - * */ debugElement: DebugElement; /** * The instance of the root component class. - * - * 根组件类的实例。 - * */ componentInstance: T; /** * The native element at the root of the component. - * - * 组件根部的原生元素。 - * */ nativeElement: any; /** * The ElementRef for the element at the root of the component. - * - * 位于组件根目录的元素的 ElementRef。 - * */ elementRef: ElementRef; /** * The ChangeDetectorRef for the component - * - * 组件的 ChangeDetectorRef - * */ changeDetectorRef: ChangeDetectorRef; @@ -135,9 +118,6 @@ export class ComponentFixture { /** * Trigger a change detection cycle for the component. - * - * 触发组件的变更检测周期。 - * */ detectChanges(checkNoChanges: boolean = true): void { if (this.ngZone != null) { @@ -154,9 +134,6 @@ export class ComponentFixture { /** * Do a change detection run to make sure there were no changes. - * - * 进行变更检测以确保没有更改。 - * */ checkNoChanges(): void { this.changeDetectorRef.checkNoChanges(); @@ -165,12 +142,7 @@ export class ComponentFixture { /** * Set whether the fixture should autodetect changes. * - * 设置夹具是否应自动检测变化。 - * * Also runs detectChanges once so that any existing change is detected. - * - * 还运行一次 detectChanges,以检测出任何现有更改。 - * */ autoDetectChanges(autoDetect: boolean = true) { if (this.ngZone == null) { @@ -183,9 +155,6 @@ export class ComponentFixture { /** * 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; @@ -194,13 +163,8 @@ export class ComponentFixture { /** * Get a promise that resolves when the fixture is stable. * - * 当夹具稳定时解析的承诺。 - * * This can be used to resume testing after events have triggered asynchronous activity or * asynchronous change detection. - * - * 当事件已触发异步活动或异步变更检测后,可用此方法继续执行测试。 - * */ whenStable(): Promise { if (this.isStable()) { @@ -225,9 +189,6 @@ export class ComponentFixture { /** * Get a promise that resolves when the ui state is stable following animations. - * - * 获得一个承诺,可以解决以下动画中 ui 状态何时稳定的问题。 - * */ whenRenderingDone(): Promise { const renderer = this._getRenderer(); @@ -239,9 +200,6 @@ export class ComponentFixture { /** * Trigger component destruction. - * - * 触发组件的销毁。 - * */ destroy(): void { if (!this._isDestroyed) { diff --git a/packages/core/testing/src/fake_async.ts b/packages/core/testing/src/fake_async.ts index 3db24b5291..1c57da22c3 100644 --- a/packages/core/testing/src/fake_async.ts +++ b/packages/core/testing/src/fake_async.ts @@ -9,15 +9,13 @@ const _Zone: any = typeof Zone !== 'undefined' ? Zone : null; const fakeAsyncTestModule = _Zone && _Zone[_Zone.__symbol__('fakeAsyncTest')]; const fakeAsyncTestModuleNotLoadedErrorMessage = - `zone-testing.js is needed for the async() test helper but could not be found. - Please make sure that your environment includes zone.js/dist/zone-testing.js`; + `zone-testing.js is needed for the fakeAsync() test helper but could not be found. + Please make sure that your environment includes zone.js/testing`; /** * Clears out the shared fake async zone for a test. * To be called in a global `beforeEach`. * - * 清除共享的伪异步 Zone 以进行测试。在全局 `beforeEach` 中调用。 - * * @publicApi */ export function resetFakeAsyncZone(): void { @@ -29,38 +27,21 @@ export function resetFakeAsyncZone(): void { /** * Wraps a function to be executed in the fakeAsync zone: - * - * 包装一个函数,以便在 fakeAsync Zone 中执行: - * * - microtasks are manually executed by calling `flushMicrotasks()`, - * - * 通过调用 `flushMicrotasks()` 手动执行微任务, - * * - timers are synchronous, `tick()` simulates the asynchronous passage of time. * - * 计时器是同步的,用 `tick()` 模拟异步时间的流逝。 - * * If there are any pending timers at the end of the function, an exception will be thrown. * - * 如果函数末尾有任何待处理的计时器,则将引发异常。 - * * Can be used to wrap inject() calls. * - * 可用于包装 inject() 调用。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * {@example core/testing/ts/fake_async.ts region='basic'} * * @param fn * @returns The function wrapped to be executed in the fakeAsync zone * - * 要包装为在 fakeAsync Zone 中执行的函数 - * * @publicApi */ export function fakeAsync(fn: Function): (...args: any[]) => any { @@ -73,19 +54,12 @@ export function fakeAsync(fn: Function): (...args: any[]) => any { /** * Simulates the asynchronous passage of time for the timers in the fakeAsync zone. * - * 为 fakeAsync Zone 中的计时器模拟异步时间流逝。 - * * The microtasks queue is drained at the very start of this function and after any timer callback * has been executed. * - * 在此函数开始时以及执行任何计时器回调之后,微任务队列就会耗尽。 - * * @usageNotes - * * ### Example * - * ### 例子 - * * {@example core/testing/ts/fake_async.ts region='basic'} * * @param millis, the number of millisecond to advance the virtual timer @@ -143,13 +117,9 @@ export function tick( * draining the macrotask queue until it is empty. The returned value is the milliseconds * of time that would have been elapsed. * - * 通过清空宏任务队列直到其为空,来为 fakeAsync Zone 中的计时器模拟异步时间流逝。返回的值是本应经过的毫秒数。 - * * @param maxTurns * @returns The simulated time elapsed, in millis. * - * 已流逝的模拟时间(以毫秒为单位)。 - * * @publicApi */ export function flush(maxTurns?: number): number { @@ -162,8 +132,6 @@ export function flush(maxTurns?: number): number { /** * Discard all remaining periodic tasks. * - * 丢弃所有剩余的定期任务。 - * * @publicApi */ export function discardPeriodicTasks(): void { @@ -176,8 +144,6 @@ export function discardPeriodicTasks(): void { /** * Flush any pending microtasks. * - * 刷新所有未完成的微任务。 - * * @publicApi */ export function flushMicrotasks(): void { diff --git a/packages/core/testing/src/metadata_override.ts b/packages/core/testing/src/metadata_override.ts index 2d62612dd4..6e90bee622 100644 --- a/packages/core/testing/src/metadata_override.ts +++ b/packages/core/testing/src/metadata_override.ts @@ -9,8 +9,6 @@ /** * Type used for modifications to metadata * - * 用于修改元数据的类型 - * * @publicApi */ export type MetadataOverride = { diff --git a/packages/core/testing/src/r3_test_bed.ts b/packages/core/testing/src/r3_test_bed.ts index 83fba80430..74804de7d6 100644 --- a/packages/core/testing/src/r3_test_bed.ts +++ b/packages/core/testing/src/r3_test_bed.ts @@ -56,19 +56,13 @@ export class TestBedRender3 implements TestBed { * Initialize the environment for testing with a compiler factory, a PlatformRef, and an * angular module. These are common to every test in the suite. * - * 使用编译器工厂、PlatformRef 和 angular 模块来初始化测试环境。这些对于套件中的每个测试都是公共的。 - * * This may only be called once, to set up the common providers for the current test * suite on the current platform. If you absolutely need to change the providers, * first use `resetTestEnvironment`. * - * 这只能调用一次,以在当前平台上为当前测试套件设置公用提供者。如果你必须要更改提供者,请首先使用 `resetTestEnvironment` 。 - * * Test modules and platforms for individual platforms are available from * '@angular//testing'. * - * 可从 '@angular/<platform_name>/testing' 获得适用于各个平台的测试模块和平台。 - * * @publicApi */ static initTestEnvironment( @@ -81,8 +75,6 @@ export class TestBedRender3 implements TestBed { /** * Reset the providers for the test injector. * - * 重置测试注入器的提供者。 - * * @publicApi */ static resetTestEnvironment(): void { @@ -210,19 +202,13 @@ export class TestBedRender3 implements TestBed { * Initialize the environment for testing with a compiler factory, a PlatformRef, and an * angular module. These are common to every test in the suite. * - * 使用编译器工厂、PlatformRef 和 angular 模块来初始化测试环境。这些对于套件中的每个测试都是公共的。 - * * This may only be called once, to set up the common providers for the current test * suite on the current platform. If you absolutely need to change the providers, * first use `resetTestEnvironment`. * - * 这只能调用一次,以在当前平台上为当前测试套件设置公用提供者。如果你必须要更改提供者,请首先使用 `resetTestEnvironment` 。 - * * Test modules and platforms for individual platforms are available from * '@angular//testing'. * - * 可从 '@angular/<platform_name>/testing' 获得适用于各个平台的测试模块和平台。 - * * @publicApi */ initTestEnvironment( @@ -238,8 +224,6 @@ export class TestBedRender3 implements TestBed { /** * Reset the providers for the test injector. * - * 重置测试注入器的提供者。 - * * @publicApi */ resetTestEnvironment(): void { diff --git a/packages/core/testing/src/test_bed.ts b/packages/core/testing/src/test_bed.ts index 4deb699e8e..5a40de6703 100644 --- a/packages/core/testing/src/test_bed.ts +++ b/packages/core/testing/src/test_bed.ts @@ -30,28 +30,18 @@ export interface TestBed { * Initialize the environment for testing with a compiler factory, a PlatformRef, and an * angular module. These are common to every test in the suite. * - * 使用编译器工厂、PlatformRef 和 angular 模块来初始化测试环境。这些对于套件中的每个测试都是公共的。 - * * This may only be called once, to set up the common providers for the current test * suite on the current platform. If you absolutely need to change the providers, * first use `resetTestEnvironment`. * - * 这只能调用一次,以在当前平台上为当前测试套件设置公用提供者。如果你必须要更改提供者,请首先使用 `resetTestEnvironment` 。 - * * Test modules and platforms for individual platforms are available from * '@angular//testing'. - * - * 可从 '@angular/<platform_name>/testing' 获得适用于各个平台的测试模块和平台。 - * */ initTestEnvironment( ngModule: Type|Type[], platform: PlatformRef, aotSummaries?: () => any[]): void; /** * Reset the providers for the test injector. - * - * 重置测试注入器的提供者。 - * */ resetTestEnvironment(): void; @@ -69,19 +59,9 @@ export interface TestBed { token: Type|InjectionToken|AbstractType, notFoundValue: null, flags?: InjectFlags): T |null; - /** - * @deprecated from v9.0.0 use TestBed.inject - * - * 从 v9.0.0 开始使用 TestBed.inject - * - */ + /** @deprecated from v9.0.0 use TestBed.inject */ get(token: Type|InjectionToken, notFoundValue?: T, flags?: InjectFlags): any; - /** - * @deprecated from v9.0.0 use TestBed.inject - * - * 从 v9.0.0 开始使用 TestBed.inject - * - */ + /** @deprecated from v9.0.0 use TestBed.inject */ get(token: any, notFoundValue?: any): any; execute(tokens: any[], fn: Function, context?: any): any; @@ -96,9 +76,6 @@ export interface TestBed { /** * Overwrites all providers for the given token with the given provider definition. - * - * 使用给定的提供者定义覆盖给定令牌的所有提供者。 - * */ overrideProvider(token: any, provider: { useFactory: Function, @@ -654,17 +631,11 @@ export class TestBedViewEngine implements TestBed { * Configures and initializes environment for unit testing and provides methods for * creating components and services in unit tests. * - * 配置和初始化用于单元测试的环境,并提供用于在单元测试中创建组件和服务的方法。 - * * `TestBed` is the primary api for writing unit tests for Angular applications and libraries. * - * `TestBed` 是用于为 Angular 应用程序和库编写单元测试的主要 API。 - * * Note: Use `TestBed` in tests. It will be set to either `TestBedViewEngine` or `TestBedRender3` * according to the compiler used. * - * 注意:在测试中使用 `TestBed`。根据使用的编译器不同,它将被设置为 `TestBedViewEngine` 或 `TestBedRender3`。 - * * @publicApi */ export const TestBed: TestBedStatic = @@ -673,12 +644,8 @@ export const TestBed: TestBedStatic = /** * Returns a singleton of the applicable `TestBed`. * - * 返回适用的 `TestBed` 单例。 - * * It will be either an instance of `TestBedViewEngine` or `TestBedRender3`. * - * 这将是 `TestBedViewEngine` 或 `TestBedRender3` 的实例。 - * * @publicApi */ export const getTestBed: () => TestBed = ivyEnabled ? _getTestBedRender3 : _getTestBedViewEngine; @@ -692,12 +659,8 @@ function _getTestBedViewEngine(): TestBedViewEngine { /** * Allows injecting dependencies in `beforeEach()` and `it()`. * - * 允许在 `beforeEach()` 和 `it()` 中注入依赖项。 - * * Example: * - * 例如: - * * ``` * beforeEach(inject([Dependency, AClass], (dep, object) => { * // some code that uses `dep` and `object` @@ -711,14 +674,10 @@ function _getTestBedViewEngine(): TestBedViewEngine { * ``` * * Notes: - * - * 注意: - * * - inject is currently a function because of some Traceur limitation the syntax should * eventually * becomes `it('...', @Inject (object: AClass, async: AsyncTestCompleter) => { ... });` * - * 由于某些 Traceur 的限制,inject 当前是一个函数,此语法最终会变为 `it('...', @Inject (object: AClass, async: AsyncTestCompleter) => { ... });` * @publicApi */ export function inject(tokens: any[], fn: Function): () => any { diff --git a/packages/core/testing/src/test_bed_common.ts b/packages/core/testing/src/test_bed_common.ts index 07a3e37957..48cfad0452 100644 --- a/packages/core/testing/src/test_bed_common.ts +++ b/packages/core/testing/src/test_bed_common.ts @@ -15,8 +15,6 @@ import {TestBed} from './test_bed'; /** * An abstract class for inserting the root test component element in a platform independent way. * - * 一个用于以与平台无关的方式插入根测试组件元素的抽象类。 - * * @publicApi */ export class TestComponentRenderer { @@ -48,8 +46,6 @@ export type TestModuleMetadata = { /** * Static methods implemented by the `TestBedViewEngine` and `TestBedRender3` * - * `TestBedViewEngine` 和 `TestBedRender3` 实现的静态方法 - * * @publicApi */ export interface TestBedStatic { @@ -60,9 +56,6 @@ export interface TestBedStatic { /** * Reset the providers for the test injector. - * - * 重置测试注入器的提供者。 - * */ resetTestEnvironment(): void; @@ -71,18 +64,12 @@ export interface TestBedStatic { /** * Allows overriding default compiler providers and settings * which are defined in test_injector.js - * - * 允许覆盖在 test_injector.js 中定义的默认编译器提供者和设置 - * */ configureCompiler(config: {providers?: any[]; useJit?: boolean;}): TestBedStatic; /** * Allows overriding default providers, directives, pipes, modules of the test injector, * which are defined in test_injector.js - * - * 允许覆盖测试注入器的默认提供者、指令、管道、模块,它们在 test_injector.js 中定义 - * */ configureTestingModule(moduleDef: TestModuleMetadata): TestBedStatic; @@ -90,9 +77,6 @@ export interface TestBedStatic { * Compile components with a `templateUrl` for the test's NgModule. * It is necessary to call this function * as fetching urls is asynchronous. - * - * 使用测试用的 NgModule `templateUrl` 编译组件。由于提取网址是异步的,因此必须调用此函数。 - * */ compileComponents(): Promise; @@ -110,23 +94,14 @@ export interface TestBedStatic { * Overrides the template of the given component, compiling the template * in the context of the TestingModule. * - * 覆盖给定组件的模板,在 TestingModule 的上下文中编译该模板。 - * * Note: This works for JIT and AOTed components as well. - * - * 注意:这也适用于 JIT 和 AOT 的组件。 */ overrideTemplateUsingTestingModule(component: Type, template: string): TestBedStatic; /** * Overwrites all providers for the given token with the given provider definition. * - * 使用给定的提供者定义覆盖给定令牌的所有提供者。 - * * Note: This works for JIT and AOTed components as well. - * - * 注意:这也适用于 JIT 和 AOT 的组件。 - * */ overrideProvider(token: any, provider: { useFactory: Function, @@ -145,19 +120,9 @@ export interface TestBedStatic { token: Type|InjectionToken|AbstractType, notFoundValue: null, flags?: InjectFlags): T |null; - /** - * @deprecated from v9.0.0 use TestBed.inject - * - * 从 v9.0.0 开始使用 TestBed.inject - * - */ + /** @deprecated from v9.0.0 use TestBed.inject */ get(token: Type|InjectionToken, notFoundValue?: T, flags?: InjectFlags): any; - /** - * @deprecated from v9.0.0 use TestBed.inject - * - * 从 v9.0.0 开始使用 TestBed.inject - * - */ + /** @deprecated from v9.0.0 use TestBed.inject */ get(token: any, notFoundValue?: any): any; createComponent(component: Type): ComponentFixture; diff --git a/packages/core/testing/src/test_compiler.ts b/packages/core/testing/src/test_compiler.ts index b8c9f17efa..0f14bb1a4f 100644 --- a/packages/core/testing/src/test_compiler.ts +++ b/packages/core/testing/src/test_compiler.ts @@ -17,8 +17,6 @@ function unimplemented(): any { /** * Special interface to the compiler only used by testing * - * 编译器的特殊接口,仅用于测试 - * * @publicApi */ @Injectable() @@ -41,9 +39,6 @@ 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. - * - * 允许将编译摘要从 AOT 编译传递到 JIT 编译器,以便它可以使用 AOT 生成的代码。 - * */ loadAotSummaries(summaries: () => any[]) { throw unimplemented(); @@ -53,9 +48,6 @@ export class TestingCompiler extends Compiler { * Gets the component factory for the given component. * This assumes that the component has been compiled before calling this call using * `compileModuleAndAllComponents*`. - * - * 获取给定组件的组件工厂。这里假定此组件在这次调用之前已经用 `compileModuleAndAllComponents*` 编译过。 - * */ getComponentFactory(component: Type): ComponentFactory { throw unimplemented(); @@ -64,9 +56,6 @@ export class TestingCompiler extends Compiler { /** * Returns the component type that is stored in the given error. * This can be used for errors created by compileModule... - * - * 返回存储在给定错误中的组件类型。这可以用于由 compileModule 创建的错误... - * */ getComponentFromError(error: Error): Type|null { throw unimplemented(); @@ -76,8 +65,6 @@ export class TestingCompiler extends Compiler { /** * A factory for creating a Compiler * - * 用于创建编译器的工厂 - * * @publicApi */ export abstract class TestingCompilerFactory { diff --git a/packages/elements/package.json b/packages/elements/package.json index b805c9cf0a..e1a1dd38bd 100644 --- a/packages/elements/package.json +++ b/packages/elements/package.json @@ -5,7 +5,7 @@ "author": "angular", "license": "MIT", "dependencies": { - "tslib": "^2.0.0" + "tslib": "^2.1.0" }, "peerDependencies": { "@angular/core": "0.0.0-PLACEHOLDER", diff --git a/packages/elements/schematics/ng-add/index_spec.ts b/packages/elements/schematics/ng-add/index_spec.ts index 153cba8bb4..844c524c78 100644 --- a/packages/elements/schematics/ng-add/index_spec.ts +++ b/packages/elements/schematics/ng-add/index_spec.ts @@ -58,7 +58,7 @@ describe('Elements Schematics', () => { const tree = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, appTree).toPromise(); const pkgJsonText = tree.readContent('/package.json'); - const pkgJson = JSON.parse(pkgJsonText); + const pkgJson = JSON.parse(pkgJsonText) as {dependencies: any}; const {dependencies} = pkgJson; expect(dependencies['document-register-element']).toBeDefined(); }); diff --git a/packages/elements/src/component-factory-strategy.ts b/packages/elements/src/component-factory-strategy.ts index 1815bad1b2..0d013c26b5 100644 --- a/packages/elements/src/component-factory-strategy.ts +++ b/packages/elements/src/component-factory-strategy.ts @@ -21,8 +21,6 @@ const DESTROY_DELAY = 10; * Factory that creates new ComponentNgElementStrategy instance. Gets the component factory with the * constructor's injector's factory resolver and passes that factory to each strategy. * - * 本工厂用来创建新的 ComponentNgElementStrategy 实例。使用构造函数的注入器的工厂解析器来获取组件工厂,并将该工厂传递给每个策略。 - * * @publicApi */ export class ComponentNgElementStrategyFactory implements NgElementStrategyFactory { @@ -42,112 +40,54 @@ export class ComponentNgElementStrategyFactory implements NgElementStrategyFacto * Creates and destroys a component ref using a component factory and handles change detection * in response to input changes. * - * 使用组件工厂创建和销毁组件引用,并在输入属性发生变化时处理变更检测。 - * * @publicApi */ export class ComponentNgElementStrategy implements NgElementStrategy { // Subject of `NgElementStrategyEvent` observables corresponding to the component's outputs. private eventEmitters = new ReplaySubject[]>(1); - /** - * Merged stream of the component's output events. - * - * 合并本组件的各个输出事件流。 - * - */ + /** Merged stream of the component's output events. */ readonly events = this.eventEmitters.pipe(switchMap(emitters => merge(...emitters))); - /** - * Reference to the component that was created on connect. - * - * 指向连接时创建的组件的引用。 - * - */ + /** Reference to the component that was created on connect. */ private componentRef: ComponentRef|null = null; - /** - * Reference to the component view's `ChangeDetectorRef`. - * - * 引用组件视图的 `ChangeDetectorRef` 。 - * - */ + /** Reference to the component view's `ChangeDetectorRef`. */ private viewChangeDetectorRef: ChangeDetectorRef|null = null; /** * Changes that have been made to component inputs since the last change detection run. * (NOTE: These are only recorded if the component implements the `OnChanges` interface.) - * - * 自上次变更检测运行以来,对组件输入进行的更改。(注意:仅当组件实现了 `OnChanges` 接口时才需要记录这些内容。) - * */ private inputChanges: SimpleChanges|null = null; - /** - * Whether changes have been made to component inputs since the last change detection run. - * - * 自上次运行变更检测以来,是否对组件的输入进行过更改。 - * - */ + /** Whether changes have been made to component inputs since the last change detection run. */ private hasInputChanges = false; - /** - * Whether the created component implements the `OnChanges` interface. - * - * 创建的组件是否实现了 `OnChanges` 接口。 - * - */ + /** Whether the created component implements the `OnChanges` interface. */ private implementsOnChanges = false; - /** - * Whether a change detection has been scheduled to run on the component. - * - * 是否已计划在组件上运行变更检测。 - * - */ + /** Whether a change detection has been scheduled to run on the component. */ private scheduledChangeDetectionFn: (() => void)|null = null; - /** - * Callback function that when called will cancel a scheduled destruction on the component. - * - * 回调函数,调用该函数将取消已计划过的组件销毁。 - * - */ + /** Callback function that when called will cancel a scheduled destruction on the component. */ private scheduledDestroyFn: (() => void)|null = null; - /** - * Initial input values that were set before the component was created. - * - * 在创建组件之前设置的初始输入值。 - * - */ + /** Initial input values that were set before the component was created. */ private readonly initialInputValues = new Map(); /** * Set of component inputs that have not yet changed, i.e. for which `recordInputChange()` has not * fired. * (This helps detect the first change of an input, even if it is explicitly set to `undefined`.) - * - * 尚未更改的组件输入属性集,也就是尚未触发 `recordInputChange()`(这有助于检测输入的首次更改,即使将其显式设置为 `undefined` 也算。) - * */ private readonly unchangedInputs = new Set(this.componentFactory.inputs.map(({propName}) => propName)); - /** - * Service for setting zone context. - * - * 用于设置 Zone 上下文的服务。 - * - */ + /** Service for setting zone context. */ private readonly ngZone = this.injector.get(NgZone); - /** - * The zone the element was created in or `null` if Zone.js is not loaded. - * - * 元素创建时所在的 Zone;如果未加载 Zone.js,则为 `null` - * - */ + /** The zone the element was created in or `null` if Zone.js is not loaded. */ private readonly elementZone = (typeof Zone === 'undefined') ? null : this.ngZone.run(() => Zone.current); @@ -156,9 +96,6 @@ export class ComponentNgElementStrategy implements NgElementStrategy { /** * Initializes a new component if one has not yet been created and cancels any scheduled * destruction. - * - * 如果尚未创建一个新组件,则将其初始化,并取消任何已计划的销毁。 - * */ connect(element: HTMLElement) { this.runInZone(() => { @@ -179,9 +116,6 @@ export class ComponentNgElementStrategy implements NgElementStrategy { /** * Schedules the component to be destroyed after some small delay in case the element is just * being moved across the DOM. - * - * 计划在短暂延迟后销毁组件,用于处理元素仅在 DOM 中移动的情况。 - * */ disconnect() { this.runInZone(() => { @@ -205,9 +139,6 @@ export class ComponentNgElementStrategy implements NgElementStrategy { /** * Returns the component property value. If the component has not yet been created, the value is * retrieved from the cached initialization values. - * - * 返回组件属性值。如果尚未创建组件,则从缓存的初始化值中检索该值。 - * */ getInputValue(property: string): any { return this.runInZone(() => { @@ -222,9 +153,6 @@ export class ComponentNgElementStrategy implements NgElementStrategy { /** * Sets the input value for the property. If the component has not yet been created, the value is * cached and set when the component is created. - * - * 设置属性的输入值。如果尚未创建组件,则在创建组件时将缓存并设置该值。 - * */ setInputValue(property: string, value: any): void { this.runInZone(() => { @@ -256,9 +184,6 @@ export class ComponentNgElementStrategy implements NgElementStrategy { /** * Creates a new component through the component factory with the provided element host and * sets up its initial inputs, listens for outputs changes, and runs an initial change detection. - * - * 通过带有提供的元素宿主的组件工厂创建一个新组件,并设置其初始输入属性,监听输出更改并运行初始变更检测。 - * */ protected initializeComponent(element: HTMLElement) { const childInjector = Injector.create({providers: [], parent: this.injector}); @@ -278,12 +203,7 @@ export class ComponentNgElementStrategy implements NgElementStrategy { applicationRef.attachView(this.componentRef.hostView); } - /** - * Set any stored initial inputs on the component's properties. - * - * 在组件的属性上设置任何已存储的初始输入。 - * - */ + /** Set any stored initial inputs on the component's properties. */ protected initializeInputs(): void { this.componentFactory.inputs.forEach(({propName}) => { if (this.initialInputValues.has(propName)) { @@ -296,12 +216,7 @@ export class ComponentNgElementStrategy implements NgElementStrategy { this.initialInputValues.clear(); } - /** - * Sets up listeners for the component's outputs so that the events stream emits the events. - * - * 为组件的输出设置监听器,以便事件流发出事件。 - * - */ + /** Sets up listeners for the component's outputs so that the events stream emits the events. */ protected initializeOutputs(componentRef: ComponentRef): void { const eventEmitters: Observable[] = this.componentFactory.outputs.map(({propName, templateName}) => { @@ -312,12 +227,7 @@ export class ComponentNgElementStrategy implements NgElementStrategy { this.eventEmitters.next(eventEmitters); } - /** - * Calls ngOnChanges with all the inputs that have changed since the last call. - * - * 使用自上次调用以来的所有已更改的输入来调用 ngOnChanges。 - * - */ + /** Calls ngOnChanges with all the inputs that have changed since the last call. */ protected callNgOnChanges(componentRef: ComponentRef): void { if (!this.implementsOnChanges || this.inputChanges === null) { return; @@ -333,9 +243,6 @@ export class ComponentNgElementStrategy implements NgElementStrategy { /** * Marks the component view for check, if necessary. * (NOTE: This is required when the `ChangeDetectionStrategy` is set to `OnPush`.) - * - * 如有必要,将组件视图标记为要检查。 (注意:当把 `ChangeDetectionStrategy` 设为 `OnPush` 时,这是必需的。) - * */ protected markViewForCheck(viewChangeDetectorRef: ChangeDetectorRef): void { if (this.hasInputChanges) { @@ -347,9 +254,6 @@ export class ComponentNgElementStrategy implements NgElementStrategy { /** * Schedules change detection to run on the component. * Ignores subsequent calls if already scheduled. - * - * 安排变更检测以在组件上运行。如果已安排,则忽略后续调用。 - * */ protected scheduleDetectChanges(): void { if (this.scheduledChangeDetectionFn) { @@ -364,9 +268,6 @@ export class ComponentNgElementStrategy implements NgElementStrategy { /** * Records input changes so that the component receives SimpleChanges in its onChanges function. - * - * 记录输入的变化,以便组件在其 onChange 函数中接收一组 SimpleChange。 - * */ protected recordInputChange(property: string, currentValue: any): void { // Do not record the change if the component does not implement `OnChanges`. @@ -391,12 +292,7 @@ export class ComponentNgElementStrategy implements NgElementStrategy { this.inputChanges[property] = new SimpleChange(previousValue, currentValue, isFirstChange); } - /** - * Runs change detection on the component. - * - * 在组件上运行变更检测。 - * - */ + /** Runs change detection on the component. */ protected detectChanges(): void { if (this.componentRef === null) { return; @@ -407,12 +303,7 @@ export class ComponentNgElementStrategy implements NgElementStrategy { this.componentRef.changeDetectorRef.detectChanges(); } - /** - * Runs in the angular zone, if present. - * - * 在 Angular Zone (如果存在)中运行。 - * - */ + /** Runs in the angular zone, if present. */ private runInZone(fn: () => unknown) { return (this.elementZone && Zone.current !== this.elementZone) ? this.ngZone.run(fn) : fn(); } diff --git a/packages/elements/src/create-custom-element.ts b/packages/elements/src/create-custom-element.ts index 9ed034e33f..3198d07f79 100644 --- a/packages/elements/src/create-custom-element.ts +++ b/packages/elements/src/create-custom-element.ts @@ -18,33 +18,20 @@ import {createCustomEvent, getComponentInputs, getDefaultAttributeToPropertyInpu * that can be used for custom element registration. Implemented and returned * by the {@link createCustomElement createCustomElement() function}. * - * 基于 Angular 组件的类构造函数的原型,该原型可用于自定义元素注册。由 {@link createCustomElement createCustomElement() 函数} 实现并返回。 - * * @see [Angular Elements Overview](guide/elements "Turning Angular components into custom elements") * - * [Angular 元素概述](guide/elements "将 Angular 组件变成自定义元素") - * * @publicApi */ export interface NgElementConstructor

    { /** * An array of observed attribute names for the custom element, * derived by transforming input property names from the source component. - * - * 被监视的自定义元素的属性名称的数组,该属性名称是通过转换源组件中的输入属性名称而得出的。 - * */ readonly observedAttributes: string[]; /** * Initializes a constructor instance. - * - * 初始化构造函数实例。 - * * @param injector If provided, overrides the configured injector. - * - * 如果提供,将覆盖已配置的注入器。 - * */ new(injector?: Injector): NgElement&WithProperties

    ; } @@ -52,74 +39,36 @@ export interface NgElementConstructor

    { /** * Implements the functionality needed for a custom element. * - * 实现自定义元素所需的功能。 - * * @publicApi */ export abstract class NgElement extends HTMLElement { /** * The strategy that controls how a component is transformed in a custom element. - * - * 控制如何把组件转换为自定义元素的策略。 - * */ protected abstract 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. - * - * 无。 - * */ abstract attributeChangedCallback( attrName: string, oldValue: string|null, newValue: string, namespace?: string): void; /** * Prototype for a handler that responds to the insertion of the custom element in the DOM. - * - * 本处理器原型用来响应自定义元素在 DOM 中插入。 - * * @returns Nothing. - * - * 无。 - * */ abstract connectedCallback(): void; /** * Prototype for a handler that responds to the deletion of the custom element from the DOM. - * - * 本处理器原型用来响应自 DOM 中删除自定义元素。 - * * @returns Nothing. - * - * 无。 - * */ abstract disconnectedCallback(): void; } @@ -129,8 +78,6 @@ export abstract class NgElement extends HTMLElement { * for properties that are added based * on the inputs and methods of the underlying component. * - * 可以添加到 NgElement 类的其他类型信息,用于基于基础组件的输入和方法添加的属性。 - * * @publicApi */ export type WithProperties

    = { @@ -142,24 +89,16 @@ export type WithProperties

    = { * dependencies and strategy it needs to transform a component into * a custom element class. * - * 一种配置,它使用将组件转换为自定义元素类所需的依赖项和策略来初始化 NgElementConstructor。 - * * @publicApi */ export interface NgElementConfig { /** * The injector to use for retrieving the component's factory. - * - * 本注入器用于检索组件工厂。 - * */ injector: Injector; /** * An optional custom strategy factory to use instead of the default. * The strategy controls how the transformation is performed. - * - * 要使用的可选自定义策略工厂,而不是默认工厂。该策略控制转换的执行方式。 - * */ strategyFactory?: NgElementStrategyFactory; } @@ -167,39 +106,23 @@ export interface NgElementConfig { /** * @description Creates a custom element class based on an Angular component. * - * 基于 Angular 组件创建自定义元素类。 - * * Builds a class that encapsulates the functionality of the provided component and * uses the configuration information to provide more context to the class. * Takes the component factory's inputs and outputs to convert them to the proper * custom element API and add hooks to input changes. * - * 构建一个类,该类封装给定组件的功能,并使用配置信息为该类提供更多上下文。获取组件工厂的输入和输出,以将它们转换为适当的自定义元素 API,并为输入变更添加钩子。 - * * The configuration's injector is the initial injector set on the class, * and used by default for each created instance.This behavior can be overridden with the * static property to affect all newly created instances, or as a constructor argument for * one-off creations. * - * 这里配置的注入器是在类上设置的初始注入器,默认情况下用于每个创建的实例。此行为可以用静态属性覆盖以影响所有新创建的实例,也可以用作一次性创建的构造函数参数。 - * * @see [Angular Elements Overview](guide/elements "Turning Angular components into custom elements") * - * [Angular 元素概述](guide/elements "将 Angular 组件变成自定义元素") - * * @param component The component to transform. - * - * 要转换的组件。 - * * @param config A configuration that provides initialization information to the created class. - * - * 为创建的类提供初始化信息的配置。 - * * @returns The custom-element construction class, which can be registered with * a browser's `CustomElementRegistry`. * - * 自定义元素的构造类,可以注册到浏览器的 `CustomElementRegistry` 中。 - * * @publicApi */ export function createCustomElement

    ( diff --git a/packages/elements/src/element-strategy.ts b/packages/elements/src/element-strategy.ts index 58c988c0ea..d18de56ecc 100644 --- a/packages/elements/src/element-strategy.ts +++ b/packages/elements/src/element-strategy.ts @@ -11,8 +11,6 @@ import {Observable} from 'rxjs'; /** * Interface for the events emitted through the NgElementStrategy. * - * 通过 NgElementStrategy 发出的事件的接口。 - * * @publicApi */ export interface NgElementStrategyEvent { @@ -24,8 +22,6 @@ export interface NgElementStrategyEvent { * Underlying strategy used by the NgElement to create/destroy the component and react to input * changes. * - * NgElement 用来创建/销毁组件并对输入更改做出反应的底层策略。 - * * @publicApi */ export interface NgElementStrategy { @@ -40,16 +36,9 @@ export interface NgElementStrategy { /** * Factory used to create new strategies for each NgElement instance. * - * 本工厂用于为每个 NgElement 实例创建新策略。 - * * @publicApi */ export interface NgElementStrategyFactory { - /** - * Creates a new instance to be used for an NgElement. - * - * 创建一个用于 NgElement 的新实例。 - * - */ + /** Creates a new instance to be used for an NgElement. */ create(injector: Injector): NgElementStrategy; } diff --git a/packages/elements/test/BUILD.bazel b/packages/elements/test/BUILD.bazel index 132083c917..491f6fd091 100644 --- a/packages/elements/test/BUILD.bazel +++ b/packages/elements/test/BUILD.bazel @@ -39,6 +39,10 @@ filegroup( # do not sort srcs = [ "@npm//:node_modules/core-js/client/core.js", + # Required for browsers that do not natively support Custom Elements. + "@npm//:node_modules/@webcomponents/custom-elements/custom-elements.min.js", + # Required for ES5 code to work with a native Custom Elements implementation. + # (See https://www.npmjs.com/package/@webcomponents/custom-elements#es5-vs-es2015.) ":custom_elements_native_shim", "@npm//:node_modules/reflect-metadata/Reflect.js", "//packages/zone.js/bundles:zone.umd.js", diff --git a/packages/examples/common/pipes/ts/currency_pipe.ts b/packages/examples/common/pipes/ts/currency_pipe.ts index f9c2199611..b5d71e726a 100644 --- a/packages/examples/common/pipes/ts/currency_pipe.ts +++ b/packages/examples/common/pipes/ts/currency_pipe.ts @@ -45,20 +45,3 @@ export class CurrencyPipeComponent { b: number = 1.3495; } // #enddocregion - -// #docregion DeprecatedCurrencyPipe -@Component({ - selector: 'deprecated-currency-pipe', - template: `

    - -

    A: {{a | currency:'CAD'}}

    - - -

    B: {{b | currency:'CAD':true:'4.2-2'}}

    -
    ` -}) -export class DeprecatedCurrencyPipeComponent { - a: number = 0.259; - b: number = 1.3495; -} -// #enddocregion diff --git a/packages/examples/common/pipes/ts/date_pipe.ts b/packages/examples/common/pipes/ts/date_pipe.ts index 4c0c3ad764..ddd7d334d1 100644 --- a/packages/examples/common/pipes/ts/date_pipe.ts +++ b/packages/examples/common/pipes/ts/date_pipe.ts @@ -14,7 +14,6 @@ import localeFr from './locale-fr'; // registering french data registerLocaleData(localeFr); -// #docregion DatePipe @Component({ selector: 'date-pipe', template: `
    @@ -44,9 +43,6 @@ export class DatePipeComponent { today = Date.now(); fixedTimezone = '2015-06-15T09:03:01+0900'; } -// #enddocregion - -// #docregion DeprecatedDatePipe @Component({ selector: 'deprecated-date-pipe', template: `
    @@ -66,4 +62,3 @@ export class DatePipeComponent { export class DeprecatedDatePipeComponent { today = Date.now(); } -// #enddocregion 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 be8b80965d..c7532f2cb6 100644 --- a/packages/examples/common/pipes/ts/e2e_test/pipe_spec.ts +++ b/packages/examples/common/pipes/ts/e2e_test/pipe_spec.ts @@ -75,14 +75,10 @@ describe('pipe', () => { browser.get(URL); waitForElement('number-pipe'); const examples = element.all(by.css('number-pipe p')); - expect(examples.get(0).getText()).toEqual('e (no formatting): 2.718'); - expect(examples.get(1).getText()).toEqual('e (3.1-5): 002.71828'); - expect(examples.get(2).getText()).toEqual('e (4.5-5): 0,002.71828'); - expect(examples.get(3).getText()).toEqual('e (french): 0\u202f002,71828'); - expect(examples.get(4).getText()).toEqual('pi (no formatting): 3.14'); - expect(examples.get(5).getText()).toEqual('pi (3.1-5): 003.14'); - expect(examples.get(6).getText()).toEqual('pi (3.5-5): 003.14000'); - expect(examples.get(7).getText()).toEqual('-2.5 (1.0-0): -3'); + expect(examples.get(0).getText()).toEqual('No specified formatting: 3.142'); + expect(examples.get(1).getText()).toEqual('With digitsInfo parameter specified: 0,003.14159'); + expect(examples.get(2).getText()) + .toEqual('With digitsInfo and locale parameters specified: 0\u202f003,14159'); }); }); diff --git a/packages/examples/common/pipes/ts/module.ts b/packages/examples/common/pipes/ts/module.ts index 47b52b508f..aaff0c30ed 100644 --- a/packages/examples/common/pipes/ts/module.ts +++ b/packages/examples/common/pipes/ts/module.ts @@ -10,14 +10,14 @@ import {Component, NgModule} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {AsyncObservablePipeComponent, AsyncPromisePipeComponent} from './async_pipe'; -import {CurrencyPipeComponent, DeprecatedCurrencyPipeComponent} from './currency_pipe'; +import {CurrencyPipeComponent} from './currency_pipe'; import {DatePipeComponent, DeprecatedDatePipeComponent} from './date_pipe'; import {I18nPluralPipeComponent, I18nSelectPipeComponent} from './i18n_pipe'; import {JsonPipeComponent} from './json_pipe'; import {KeyValuePipeComponent} from './keyvalue_pipe'; import {LowerUpperPipeComponent} from './lowerupper_pipe'; -import {DeprecatedNumberPipeComponent, NumberPipeComponent} from './number_pipe'; -import {DeprecatedPercentPipeComponent, PercentPipeComponent} from './percent_pipe'; +import {NumberPipeComponent} from './number_pipe'; +import {PercentPipeComponent} from './percent_pipe'; import {SlicePipeListComponent, SlicePipeStringComponent} from './slice_pipe'; import {TitleCasePipeComponent} from './titlecase_pipe'; @@ -66,10 +66,8 @@ export class AppComponent { declarations: [ AsyncPromisePipeComponent, AsyncObservablePipeComponent, AppComponent, JsonPipeComponent, DatePipeComponent, DeprecatedDatePipeComponent, LowerUpperPipeComponent, TitleCasePipeComponent, - NumberPipeComponent, DeprecatedNumberPipeComponent, PercentPipeComponent, - DeprecatedPercentPipeComponent, CurrencyPipeComponent, DeprecatedCurrencyPipeComponent, - SlicePipeStringComponent, SlicePipeListComponent, I18nPluralPipeComponent, - I18nSelectPipeComponent, KeyValuePipeComponent + NumberPipeComponent, PercentPipeComponent, CurrencyPipeComponent, SlicePipeStringComponent, + SlicePipeListComponent, I18nPluralPipeComponent, I18nSelectPipeComponent, KeyValuePipeComponent ], imports: [BrowserModule], }) diff --git a/packages/examples/common/pipes/ts/number_pipe.ts b/packages/examples/common/pipes/ts/number_pipe.ts index 6ce1689f6e..0a2af30c81 100644 --- a/packages/examples/common/pipes/ts/number_pipe.ts +++ b/packages/examples/common/pipes/ts/number_pipe.ts @@ -11,56 +11,35 @@ import {Component} from '@angular/core'; // we need to import data for the french locale import localeFr from './locale-fr'; -// registering french data -registerLocaleData(localeFr); +registerLocaleData(localeFr, 'fr'); // #docregion NumberPipe @Component({ selector: 'number-pipe', template: `
    - -

    e (no formatting): {{e | number}}

    - -

    e (3.1-5): {{e | number:'3.1-5'}}

    +

    + No specified formatting: + {{pi | number}} + +

    - -

    e (4.5-5): {{e | number:'4.5-5'}}

    +

    + With digitsInfo parameter specified: + {{pi | number:'4.1-5'}} + +

    - -

    e (french): {{e | number:'4.5-5':'fr'}}

    +

    + With digitsInfo and + locale parameters specified: + {{pi | number:'4.1-5':'fr'}} + +

    - -

    pi (no formatting): {{pi | number}}

    - - -

    pi (3.1-5): {{pi | number:'3.1-5'}}

    - - -

    pi (3.5-5): {{pi | number:'3.5-5'}}

    - - -

    -2.5 (1.0-0): {{-2.5 | number:'1.0-0'}}

    ` }) export class NumberPipeComponent { - pi: number = 3.14; - e: number = 2.718281828459045; -} -// #enddocregion - -// #docregion DeprecatedNumberPipe -@Component({ - selector: 'deprecated-number-pipe', - template: `
    -

    e (no formatting): {{e}}

    -

    e (3.1-5): {{e | number:'3.1-5'}}

    -

    pi (no formatting): {{pi}}

    -

    pi (3.5-5): {{pi | number:'3.5-5'}}

    -
    ` -}) -export class DeprecatedNumberPipeComponent { - pi: number = 3.141592; - e: number = 2.718281828459045; + pi: number = 3.14159265359; } // #enddocregion diff --git a/packages/examples/common/pipes/ts/percent_pipe.ts b/packages/examples/common/pipes/ts/percent_pipe.ts index 68cf159b33..15b3c15a48 100644 --- a/packages/examples/common/pipes/ts/percent_pipe.ts +++ b/packages/examples/common/pipes/ts/percent_pipe.ts @@ -33,20 +33,3 @@ export class PercentPipeComponent { b: number = 1.3495; } // #enddocregion - -// #docregion DeprecatedPercentPipe -@Component({ - selector: 'deprecated-percent-pipe', - template: `
    - -

    A: {{a | percent}}

    - - -

    B: {{b | percent:'4.3-5'}}

    -
    ` -}) -export class DeprecatedPercentPipeComponent { - a: number = 0.259; - b: number = 1.3495; -} -// #enddocregion diff --git a/packages/examples/compiler/ts/url_resolver/url_resolver.ts b/packages/examples/compiler/ts/url_resolver/url_resolver.ts index e89c2dee07..e2f20fb047 100644 --- a/packages/examples/compiler/ts/url_resolver/url_resolver.ts +++ b/packages/examples/compiler/ts/url_resolver/url_resolver.ts @@ -15,7 +15,6 @@ import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; class MyApp { } -// #docregion url_resolver class MyUrlResolver extends UrlResolver { resolve(baseUrl: string, url: string): string { // Serve CSS files from a special CDN. @@ -37,4 +36,3 @@ class AppModule { export function main() { platformBrowserDynamic().bootstrapModule(AppModule); } -// #enddocregion diff --git a/packages/examples/core/animation/ts/dsl/animation_example.ts b/packages/examples/core/animation/ts/dsl/animation_example.ts index 6178c4eb27..54857ae2f1 100644 --- a/packages/examples/core/animation/ts/dsl/animation_example.ts +++ b/packages/examples/core/animation/ts/dsl/animation_example.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -// #docregion Component import {animate, state, style, transition, trigger} from '@angular/animations'; import {Component, NgModule} from '@angular/core'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; @@ -59,5 +58,3 @@ export class MyExpandoCmp { {imports: [BrowserAnimationsModule], declarations: [MyExpandoCmp], bootstrap: [MyExpandoCmp]}) export class AppModule { } - -// #enddocregion 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 de7789cf6c..a9ae6e7cea 100644 --- a/packages/examples/core/debug/ts/debug_element/debug_element.ts +++ b/packages/examples/core/debug/ts/debug_element/debug_element.ts @@ -11,6 +11,4 @@ import {DebugElement} from '@angular/core'; let debugElement: DebugElement = undefined!; let predicate: any; -// #docregion scope_all debugElement.query(predicate); -// #enddocregion diff --git a/packages/examples/core/di/ts/metadata_spec.ts b/packages/examples/core/di/ts/metadata_spec.ts index 7fde3262f3..a1e76075bc 100644 --- a/packages/examples/core/di/ts/metadata_spec.ts +++ b/packages/examples/core/di/ts/metadata_spec.ts @@ -103,9 +103,7 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; @Injectable() class NeedsDependency { - constructor(@SkipSelf() public dependency: Dependency) { - this.dependency = dependency; - } + constructor(@SkipSelf() public dependency: Dependency) {} } const parent = Injector.create({providers: [{provide: Dependency, deps: []}]}); diff --git a/packages/examples/core/testing/ts/fake_async.ts b/packages/examples/core/testing/ts/fake_async.ts index a09231d2fe..a26bb90804 100644 --- a/packages/examples/core/testing/ts/fake_async.ts +++ b/packages/examples/core/testing/ts/fake_async.ts @@ -25,7 +25,6 @@ describe('this test', () => { }); // #enddocregion -// #docregion pending describe('this test', () => { it('aborts a periodic timer', fakeAsync((): void => { // This timer is scheduled but doesn't need to complete for the @@ -37,4 +36,3 @@ describe('this test', () => { discardPeriodicTasks(); })); }); -// #enddocregion diff --git a/packages/examples/core/ts/bootstrap/bootstrap.ts b/packages/examples/core/ts/bootstrap/bootstrap.ts index af2f28c610..51d797f2cb 100644 --- a/packages/examples/core/ts/bootstrap/bootstrap.ts +++ b/packages/examples/core/ts/bootstrap/bootstrap.ts @@ -10,7 +10,6 @@ import {Component, NgModule} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; -// #docregion bootstrap @Component({selector: 'my-app', template: 'Hello {{ name }}!'}) class MyApp { name: string = 'World'; @@ -23,5 +22,3 @@ class AppModule { export function main() { platformBrowserDynamic().bootstrapModule(AppModule); } - -// #enddocregion diff --git a/packages/examples/core/ts/metadata/metadata.ts b/packages/examples/core/ts/metadata/metadata.ts index 7b5bec14d9..c46a01dd22 100644 --- a/packages/examples/core/ts/metadata/metadata.ts +++ b/packages/examples/core/ts/metadata/metadata.ts @@ -10,12 +10,10 @@ import {Attribute, Component, Directive, Pipe} from '@angular/core'; class CustomDirective {} -// #docregion component @Component({selector: 'greet', template: 'Hello {{name}}!'}) class Greet { name: string = 'World'; } -// #enddocregion // #docregion attributeFactory @Component({selector: 'page', template: 'Title: {{title}}'}) @@ -36,20 +34,16 @@ class InputAttrDirective { } // #enddocregion -// #docregion directive @Directive({selector: 'input'}) class InputDirective { constructor() { // Add some logic. } } -// #enddocregion -// #docregion pipe @Pipe({name: 'lowercase'}) class Lowercase { 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 665d066b09..a5d50dec1f 100644 --- a/packages/examples/core/ts/prod_mode/prod_mode_example.ts +++ b/packages/examples/core/ts/prod_mode/prod_mode_example.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -// #docregion enableProdMode import {enableProdMode, NgModule} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; @@ -20,4 +19,3 @@ export class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule); -// #enddocregion diff --git a/packages/examples/forms/ts/formBuilder/form_builder_example.ts b/packages/examples/forms/ts/formBuilder/form_builder_example.ts index de94331989..6e1044ad80 100644 --- a/packages/examples/forms/ts/formBuilder/form_builder_example.ts +++ b/packages/examples/forms/ts/formBuilder/form_builder_example.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -// #docregion Component, disabled-control +// #docregion disabled-control import {Component, Inject} from '@angular/core'; import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'; // #enddocregion disabled-control @@ -42,7 +42,6 @@ export class FormBuilderComp { {updateOn: 'change'}); } } -// #enddocregion // #docregion disabled-control @Component({ diff --git a/packages/examples/forms/ts/radioButtons/radio_button_example.ts b/packages/examples/forms/ts/radioButtons/radio_button_example.ts index 5066404bf8..8e10bc7da6 100644 --- a/packages/examples/forms/ts/radioButtons/radio_button_example.ts +++ b/packages/examples/forms/ts/radioButtons/radio_button_example.ts @@ -5,8 +5,6 @@ * 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 */ - -// #docregion TemplateDriven import {Component} from '@angular/core'; @Component({ @@ -25,4 +23,3 @@ import {Component} from '@angular/core'; export class RadioButtonComp { myFood = 'lamb'; } -// #enddocregion diff --git a/packages/examples/platform-browser/dom/debug/ts/debug_element_view_listener/providers.ts b/packages/examples/platform-browser/dom/debug/ts/debug_element_view_listener/providers.ts index b8b4a66dbc..a02769e781 100644 --- a/packages/examples/platform-browser/dom/debug/ts/debug_element_view_listener/providers.ts +++ b/packages/examples/platform-browser/dom/debug/ts/debug_element_view_listener/providers.ts @@ -14,10 +14,7 @@ import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; @Component({selector: 'my-component', template: 'text'}) class MyAppComponent { } - -// #docregion providers @NgModule({imports: [BrowserModule], bootstrap: [MyAppComponent]}) class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule); -// #enddocregion diff --git a/packages/examples/testing/ts/testing.ts b/packages/examples/testing/ts/testing.ts index c17fa3c05b..16db49e745 100644 --- a/packages/examples/testing/ts/testing.ts +++ b/packages/examples/testing/ts/testing.ts @@ -11,16 +11,13 @@ let db: any; class MyService {} class MyMockService implements MyService {} -// #docregion describeIt describe('some component', () => { it('does something', () => { // This is a test. }); }); -// #enddocregion -// #docregion fdescribe /* tslint:disable-next-line:no-jasmine-focus */ fdescribe('some component', () => { it('has a test', @@ -33,9 +30,7 @@ describe('another component', () => { throw 'This test will not run.'; }); }); -// #enddocregion -// #docregion xdescribe xdescribe('some component', () => { it('has a test', () => { throw 'This test will not run.'; @@ -47,9 +42,7 @@ describe('another component', () => { // This test will run. }); }); -// #enddocregion -// #docregion fit describe('some component', () => { /* tslint:disable-next-line:no-jasmine-focus */ fit('has a test', @@ -60,9 +53,7 @@ describe('some component', () => { throw 'This test will not run.'; }); }); -// #enddocregion -// #docregion xit describe('some component', () => { xit('has a test', () => { throw 'This test will not run.'; @@ -72,9 +63,7 @@ describe('some component', () => { // This test will run. }); }); -// #enddocregion -// #docregion beforeEach describe('some component', () => { beforeEach(() => { db.connect(); @@ -84,9 +73,7 @@ describe('some component', () => { // Database is connected. }); }); -// #enddocregion -// #docregion afterEach describe('some component', () => { afterEach((done: Function) => { db.reset().then((_: any) => done()); @@ -97,4 +84,3 @@ describe('some component', () => { // The afterEach will ensure it gets reset. }); }); -// #enddocregion diff --git a/packages/examples/upgrade/static/ts/full/module.ts b/packages/examples/upgrade/static/ts/full/module.ts index 4ca5413201..fd4f29d7e1 100644 --- a/packages/examples/upgrade/static/ts/full/module.ts +++ b/packages/examples/upgrade/static/ts/full/module.ts @@ -26,7 +26,6 @@ export class TextFormatter { } // #enddocregion -// #docregion Angular Stuff // #docregion ng2-heroes // This Angular component will be "downgraded" to be used in AngularJS @Component({ @@ -120,14 +119,10 @@ export class Ng2AppModule { } // #enddocregion bootstrap-ng1 // #enddocregion ng2-module -// #enddocregion -// #docregion Angular 1 Stuff -// #docregion ng1-module // This Angular 1 module represents the AngularJS pieces of the application export const ng1AppModule: ng.IModule = angular.module('ng1AppModule', []); -// #enddocregion // #docregion ng1-hero // This AngularJS component will be "upgraded" to be used in Angular @@ -178,7 +173,6 @@ ng1AppModule.component('exampleApp', { ` }); // #enddocregion -// #enddocregion // #docregion bootstrap-ng2 diff --git a/packages/forms/package.json b/packages/forms/package.json index 90d056a38b..5797bbeb80 100644 --- a/packages/forms/package.json +++ b/packages/forms/package.json @@ -5,7 +5,7 @@ "author": "angular", "license": "MIT", "dependencies": { - "tslib": "^2.0.0" + "tslib": "^2.1.0" }, "peerDependencies": { "@angular/core": "0.0.0-PLACEHOLDER", diff --git a/packages/forms/src/directives.ts b/packages/forms/src/directives.ts index 1ef6b4cef9..92af71a67b 100644 --- a/packages/forms/src/directives.ts +++ b/packages/forms/src/directives.ts @@ -16,7 +16,7 @@ import {NgModel} from './directives/ng_model'; import {NgModelGroup} from './directives/ng_model_group'; import {NgNoValidate} from './directives/ng_no_validate_directive'; import {NumberValueAccessor} from './directives/number_value_accessor'; -import {RadioControlValueAccessor} from './directives/radio_control_value_accessor'; +import {RadioControlRegistryModule, RadioControlValueAccessor} from './directives/radio_control_value_accessor'; import {RangeValueAccessor} from './directives/range_value_accessor'; import {FormControlDirective} from './directives/reactive_directives/form_control_directive'; import {FormControlName} from './directives/reactive_directives/form_control_name'; @@ -24,7 +24,7 @@ import {FormGroupDirective} from './directives/reactive_directives/form_group_di import {FormArrayName, FormGroupName} from './directives/reactive_directives/form_group_name'; import {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor'; import {NgSelectMultipleOption, SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor'; -import {CheckboxRequiredValidator, EmailValidator, MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator} from './directives/validators'; +import {CheckboxRequiredValidator, EmailValidator, MaxLengthValidator, MaxValidator, MinLengthValidator, MinValidator, PatternValidator, RequiredValidator} from './directives/validators'; export {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor'; export {ControlValueAccessor} from './directives/control_value_accessor'; @@ -63,6 +63,8 @@ export const SHARED_FORM_DIRECTIVES: Type[] = [ PatternValidator, CheckboxRequiredValidator, EmailValidator, + MinValidator, + MaxValidator, ]; export const TEMPLATE_DRIVEN_DIRECTIVES: Type[] = [NgModel, NgModelGroup, NgForm]; @@ -75,6 +77,7 @@ export const REACTIVE_DRIVEN_DIRECTIVES: Type[] = */ @NgModule({ declarations: SHARED_FORM_DIRECTIVES, + imports: [RadioControlRegistryModule], exports: SHARED_FORM_DIRECTIVES, }) export class ɵInternalFormsSharedModule { diff --git a/packages/forms/src/directives/abstract_control_directive.ts b/packages/forms/src/directives/abstract_control_directive.ts index d4a3b4f4d6..e1fb2c6e02 100644 --- a/packages/forms/src/directives/abstract_control_directive.ts +++ b/packages/forms/src/directives/abstract_control_directive.ts @@ -18,12 +18,8 @@ import {AsyncValidator, AsyncValidatorFn, ValidationErrors, Validator, Validator * @description * Base class for control directives. * - * 控件指令的基类。 - * * This class is only used internally in the `ReactiveFormsModule` and the `FormsModule`. * - * 此类仅在 `ReactiveFormsModule` 和 `FormsModule` 内部使用。 - * * @publicApi */ export abstract class AbstractControlDirective { @@ -31,21 +27,13 @@ export abstract class AbstractControlDirective { * @description * A reference to the underlying control. * - * 对基础控件的引用。 - * * @returns the control that backs this directive. Most properties fall through to that instance. - * - * 支持此指令的控件。大多数属性都属于该实例。 - * */ abstract get control(): AbstractControl|null; /** * @description * Reports the value of the control if it is present, otherwise null. - * - * 报告控件的值(如果存在),否则为 null。 - * */ get value(): any { return this.control ? this.control.value : null; @@ -56,9 +44,6 @@ export abstract class AbstractControlDirective { * Reports whether the control is valid. A control is considered valid if no * validation errors exist with the current value. * If the control is not present, null is returned. - * - * 报告控件是否有效。如果当前值不存在验证错误,则控件被视为有效。如果控件不存在,则返回 null。 - * */ get valid(): boolean|null { return this.control ? this.control.valid : null; @@ -68,9 +53,6 @@ export abstract class AbstractControlDirective { * @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. - * - * 报告控件是否无效,表示输入值中存在错误。如果控件不存在,则返回 null。 - * */ get invalid(): boolean|null { return this.control ? this.control.invalid : null; @@ -81,9 +63,6 @@ export abstract class AbstractControlDirective { * Reports whether a control is pending, meaning that that async validation is occurring and * errors are not yet available for the input value. If the control is not present, null is * returned. - * - * 报告控件是否处于挂起状态,这意味着异步验证正在发生,并且错误尚未可用于输入值。如果控件不存在,则返回 null。 - * */ get pending(): boolean|null { return this.control ? this.control.pending : null; @@ -94,9 +73,6 @@ export abstract class AbstractControlDirective { * Reports whether the control is disabled, meaning that the control is disabled * 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. - * - * 报告该控件是否被禁用,这意味着该控件在 UI 中被禁用,并且免于进行验证检查,并被排除在祖先控件的聚合值之外。如果控件不存在,则返回 null。 - * */ get disabled(): boolean|null { return this.control ? this.control.disabled : null; @@ -106,9 +82,6 @@ export abstract class AbstractControlDirective { * @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. - * - * 报告控件是否被启用,这意味着控件已包含在祖先的有效性计算或值计算中。如果控件不存在,则返回 null。 - * */ get enabled(): boolean|null { return this.control ? this.control.enabled : null; @@ -117,9 +90,6 @@ export abstract class AbstractControlDirective { /** * @description * Reports the control's validation errors. If the control is not present, null is returned. - * - * 报告控件的验证错误。如果控件不存在,则返回 null。 - * */ get errors(): ValidationErrors|null { return this.control ? this.control.errors : null; @@ -129,9 +99,6 @@ export abstract class AbstractControlDirective { * @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. - * - * 报告控件是否为原始状态,原始的意思是用户尚未更改过 UI 中的值。如果控件不存在,则返回 null。 - * */ get pristine(): boolean|null { return this.control ? this.control.pristine : null; @@ -141,9 +108,6 @@ export abstract class AbstractControlDirective { * @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. - * - * 报告控件是否脏状态,脏的意思是用户已更改过 UI 中的值。如果控件不存在,则返回 null。 - * */ get dirty(): boolean|null { return this.control ? this.control.dirty : null; @@ -153,9 +117,6 @@ export abstract class AbstractControlDirective { * @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. - * - * 报告控件是否被接触过,被接触过的意思是用户已在控件上触发过 `blur` 事件。如果控件不存在,则返回 null。 - * */ get touched(): boolean|null { return this.control ? this.control.touched : null; @@ -166,9 +127,6 @@ export abstract class AbstractControlDirective { * Reports the validation status of the control. Possible values include: * 'VALID', 'INVALID', 'DISABLED', and 'PENDING'. * If the control is not present, null is returned. - * - * 报告控件的验证状态。可能的值包括:“VALID”、“INVALID”、“DISABLED” 和 “PENDING”。如果控件不存在,则返回 null。 - * */ get status(): string|null { return this.control ? this.control.status : null; @@ -178,9 +136,6 @@ export abstract class AbstractControlDirective { * @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. - * - * 报告控件是否未被接触过,未被接触过的意思是用户尚未在其上触发过 `blur` 事件。如果控件不存在,则返回 null。 - * */ get untouched(): boolean|null { return this.control ? this.control.untouched : null; @@ -190,9 +145,6 @@ export abstract class AbstractControlDirective { * @description * Returns a multicasting observable that emits a validation status whenever it is * calculated for the control. If the control is not present, null is returned. - * - * 返回一个多播的可观察对象,它会发出为此控件计算过的验证状态。如果控件不存在,则返回 null。 - * */ get statusChanges(): Observable|null { return this.control ? this.control.statusChanges : null; @@ -203,9 +155,6 @@ export abstract class AbstractControlDirective { * Returns a multicasting observable of value changes for the control that emits every time the * value of the control changes in the UI or programmatically. * If the control is not present, null is returned. - * - * 返回控件值变更的多播可观察对象,它会在每次控件的值在 UI 中或以编程方式更改时触发。如果控件不存在,则返回 null。 - * */ get valueChanges(): Observable|null { return this.control ? this.control.valueChanges : null; @@ -215,9 +164,6 @@ export abstract class AbstractControlDirective { * @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[]|null { return null; @@ -226,26 +172,17 @@ export abstract class AbstractControlDirective { /** * Contains the result of merging synchronous validators into a single validator function * (combined using `Validators.compose`). - * - * 包含将同步验证器合并到单个验证器函数中的结果(使用 `Validators.compose` 组合)。 - * */ private _composedValidatorFn: ValidatorFn|null|undefined; /** * Contains the result of merging asynchronous validators into a single validator function * (combined using `Validators.composeAsync`). - * - * 包含将异步验证器合并为单个验证器函数中的结果(使用 `Validators.composeAsync` 组合)。 - * */ private _composedAsyncValidatorFn: AsyncValidatorFn|null|undefined; /** * Set of synchronous validators as they were provided while calling `setValidators` function. - * - * 调用 `setValidators` 函数时提供的一组同步验证器。 - * * @internal */ _rawValidators: Array = []; @@ -253,18 +190,12 @@ export abstract class AbstractControlDirective { /** * Set of asynchronous validators as they were provided while calling `setAsyncValidators` * function. - * - * 调用 `setAsyncValidators` 函数时提供的一组异步验证器。 - * * @internal */ _rawAsyncValidators: Array = []; /** * Sets synchronous validators for this directive. - * - * 为此指令设置同步验证器。 - * * @internal */ _setValidators(validators: Array|undefined): void { @@ -274,9 +205,6 @@ export abstract class AbstractControlDirective { /** * Sets asynchronous validators for this directive. - * - * 为此指令设置异步验证器。 - * * @internal */ _setAsyncValidators(validators: Array|undefined): void { @@ -288,9 +216,6 @@ export abstract class AbstractControlDirective { * @description * Synchronous validator function composed of all the synchronous validators registered with this * directive. - * - * 同步验证器函数,由使用此指令注册的所有同步验证器组合而成。 - * */ get validator(): ValidatorFn|null { return this._composedValidatorFn || null; @@ -300,9 +225,6 @@ export abstract class AbstractControlDirective { * @description * Asynchronous validator function composed of all the asynchronous validators registered with * this directive. - * - * 异步验证器函数,由使用此指令注册的所有异步验证器组合而成。 - * */ get asyncValidator(): AsyncValidatorFn|null { return this._composedAsyncValidatorFn || null; @@ -316,9 +238,6 @@ export abstract class AbstractControlDirective { /** * Internal function to register callbacks that should be invoked * when directive instance is being destroyed. - * - * 内部函数,用于注册在销毁指令实例时应调用的回调。 - * * @internal */ _registerOnDestroy(fn: () => void): void { @@ -328,9 +247,6 @@ export abstract class AbstractControlDirective { /** * Internal function to invoke all registered "on destroy" callbacks. * Note: calling this function also clears the list of callbacks. - * - * 内部函数,用于调用所有已注册的 “on destroy” 回调。注意:调用此函数还会清除回调列表。 - * * @internal */ _invokeOnDestroyCallbacks(): void { @@ -341,9 +257,6 @@ export abstract class AbstractControlDirective { /** * @description * Resets the control with the provided value if the control is present. - * - * 如果此控件存在,则使用所提供的值重置它。 - * */ reset(value: any = undefined): void { if (this.control) this.control.reset(value); @@ -353,23 +266,13 @@ export abstract class AbstractControlDirective { * @description * Reports whether the control with the given path has the error specified. * - * 报告给定路径下的控件是否具有指定的错误。 - * * @param errorCode The code of the error to check - * - * 要检查的错误代码 - * * @param path A list of control names that designates how to move from the current control * to the control that should be queried for errors. * - * 控件名称列表,用于指定如何从当前控件移至要查询错误的控件。 - * * @usageNotes - * * For example, for the following `FormGroup`: * - * 例如,对于以下 `FormGroup`: - * * ``` * form = new FormGroup({ * address: new FormGroup({ street: new FormControl() }) @@ -378,32 +281,16 @@ export abstract class AbstractControlDirective { * * The path to the 'street' control from the root form would be 'address' -> 'street'. * - * 此 'street' 控件的从根表单开始的路径应该是 'address' -> 'street'。 - * * It can be provided to this method in one of two formats: * - * 调用此方法有两种形式: - * * 1. An array of string control names, e.g. `['address', 'street']` - * - * 控件名称的字符串数组,如 `['address', 'street']` - * * 1. A period-delimited list of control names in one string, e.g. `'address.street'` * - * 以一个字符串表示的句号分隔的控件名称列表,如 `'address.street'` - * * If no path is given, this method checks for the error on the current control. * - * 如果没有给出路径,则此方法检查当前控件上的错误。 - * * @returns whether the given error is present in the control at the given path. * - * 给定路径中的控件中是否存在给定错误。 - * * If the control is not present, false is returned. - * - * 如果控件不存在,则返回 false。 - * */ hasError(errorCode: string, path?: Array|string): boolean { return this.control ? this.control.hasError(errorCode, path) : false; @@ -413,23 +300,13 @@ export abstract class AbstractControlDirective { * @description * Reports error data for the control with the given path. * - * 报告给定路径下的控件的错误数据。 - * * @param errorCode The code of the error to check - * - * 要检查的错误代码 - * * @param path A list of control names that designates how to move from the current control * to the control that should be queried for errors. * - * 控件名称列表,用于指定如何从当前控件移至要查询错误的控件。 - * * @usageNotes - * * For example, for the following `FormGroup`: * - * 比如下面的 `FormGroup`: - * * ``` * form = new FormGroup({ * address: new FormGroup({ street: new FormControl() }) @@ -438,25 +315,13 @@ export abstract class AbstractControlDirective { * * The path to the 'street' control from the root form would be 'address' -> 'street'. * - * 此 'street' 控件的从根表单开始的路径应该是 'address' -> 'street'。 - * * It can be provided to this method in one of two formats: * - * 调用此方法有两种形式: - * * 1. An array of string control names, e.g. `['address', 'street']` - * - * 控件名称的字符串数组,如 `['address', 'street']` - * * 1. A period-delimited list of control names in one string, e.g. `'address.street'` * - * 以一个字符串表示的句号分隔的控件名称列表,如 `'address.street'` - * * @returns error data for that particular error. If the control or error is not present, * null is returned. - * - * 该特定错误的错误数据。如果控件或错误不存在,则返回 null。 - * */ getError(errorCode: string, path?: Array|string): any { return this.control ? this.control.getError(errorCode, path) : null; diff --git a/packages/forms/src/directives/abstract_form_group_directive.ts b/packages/forms/src/directives/abstract_form_group_directive.ts index 45374a5b4c..d5497344ec 100644 --- a/packages/forms/src/directives/abstract_form_group_directive.ts +++ b/packages/forms/src/directives/abstract_form_group_directive.ts @@ -20,8 +20,6 @@ import {controlPath} from './shared'; * @description * A base class for code shared between the `NgModelGroup` and `FormGroupName` directives. * - * `NgModelGroup` 和 `FormGroupName` 指令之间共享代码的基类。 - * * @publicApi */ @Directive() @@ -30,8 +28,6 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn * @description * The parent control for the group * - * 该组的父控件 - * * @internal */ // TODO(issue/24571): remove '!'. @@ -55,9 +51,6 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn /** * @description * The `FormGroup` bound to this directive. - * - * 绑定到此指令的 `FormGroup`。 - * */ get control(): FormGroup { return this.formDirective!.getFormGroup(this); @@ -66,9 +59,6 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn /** * @description * The path to this group from the top-level directive. - * - * 从顶级指令到该组的路径。 - * */ get path(): string[] { return controlPath(this.name == null ? this.name : this.name.toString(), this._parent); @@ -77,9 +67,6 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn /** * @description * The top-level directive for this group if present, otherwise null. - * - * 该组的顶级指令(如果存在),否则为 null。 - * */ get formDirective(): Form|null { return this._parent ? this._parent.formDirective : null; diff --git a/packages/forms/src/directives/checkbox_value_accessor.ts b/packages/forms/src/directives/checkbox_value_accessor.ts index 12babdffa1..c2d97e19c8 100644 --- a/packages/forms/src/directives/checkbox_value_accessor.ts +++ b/packages/forms/src/directives/checkbox_value_accessor.ts @@ -8,7 +8,7 @@ import {Directive, ElementRef, forwardRef, Renderer2} from '@angular/core'; -import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor'; +import {BuiltInControlValueAccessor, ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor'; export const CHECKBOX_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, @@ -21,18 +21,12 @@ export const CHECKBOX_VALUE_ACCESSOR: any = { * A `ControlValueAccessor` for writing a value and listening to changes on a checkbox input * element. * - * 一个 `ControlValueAccessor`,用于写入值并监听复选框输入元素上的更改。 - * * @usageNotes * * ### Using a checkbox with a reactive form. * - * ### 使用带有响应式表单的复选框。 - * * The following example shows how to use a checkbox with a reactive form. * - * 以下示例显示了如何将复选框与响应式表单一起使用。 - * * ```ts * const rememberLoginControl = new FormControl(); * ``` @@ -51,32 +45,26 @@ export const CHECKBOX_VALUE_ACCESSOR: any = { host: {'(change)': 'onChange($event.target.checked)', '(blur)': 'onTouched()'}, providers: [CHECKBOX_VALUE_ACCESSOR] }) -export class CheckboxControlValueAccessor implements ControlValueAccessor { +export class CheckboxControlValueAccessor extends BuiltInControlValueAccessor implements + ControlValueAccessor { /** * The registered callback function called when a change event occurs on the input element. - * - * 当此 input 元素上发生更改事件时,要调用的已注册回调函数。 - * * @nodoc */ onChange = (_: any) => {}; /** * The registered callback function called when a blur event occurs on the input element. - * - * 当此 input 元素上发生失焦事件时,要调用的已注册回调函数。 - * * @nodoc */ onTouched = () => {}; - constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {} + constructor(private _renderer: Renderer2, private _elementRef: ElementRef) { + super(); + } /** * Sets the "checked" property on the input element. - * - * 在此 input 元素上设置“checked”属性。 - * * @nodoc */ writeValue(value: any): void { @@ -85,9 +73,6 @@ export class CheckboxControlValueAccessor implements ControlValueAccessor { /** * Registers a function called when the control value changes. - * - * 注册控件值更改时调用的函数。 - * * @nodoc */ registerOnChange(fn: (_: any) => {}): void { @@ -96,9 +81,6 @@ export class CheckboxControlValueAccessor implements ControlValueAccessor { /** * Registers a function called when the control is touched. - * - * 注册控件被接触过时要调用的函数。 - * * @nodoc */ registerOnTouched(fn: () => {}): void { @@ -107,9 +89,6 @@ export class CheckboxControlValueAccessor implements ControlValueAccessor { /** * Sets the "disabled" property on the input element. - * - * 在此 input 元素上设置“disabled”属性。 - * * @nodoc */ setDisabledState(isDisabled: boolean): void { diff --git a/packages/forms/src/directives/control_container.ts b/packages/forms/src/directives/control_container.ts index 8c35fa8105..3b160a686f 100644 --- a/packages/forms/src/directives/control_container.ts +++ b/packages/forms/src/directives/control_container.ts @@ -15,17 +15,12 @@ import {Form} from './form_interface'; * A base class for directives that contain multiple registered instances of `NgControl`. * Only used by the forms module. * - * 包含多个已注册 `NgControl` 实例的指令的基类。仅由表单模块使用。 - * * @publicApi */ export abstract class ControlContainer extends AbstractControlDirective { /** * @description * The name for the control - * - * 控件的名称 - * */ // TODO(issue/24571): remove '!'. name!: string|number|null; @@ -33,9 +28,6 @@ export abstract class ControlContainer extends AbstractControlDirective { /** * @description * The top-level form directive for the control. - * - * 控件的顶级表单指令。 - * */ get formDirective(): Form|null { return null; @@ -44,9 +36,6 @@ export abstract class ControlContainer extends AbstractControlDirective { /** * @description * The path to this group. - * - * 该组的路径。 - * */ get path(): string[]|null { return null; diff --git a/packages/forms/src/directives/control_value_accessor.ts b/packages/forms/src/directives/control_value_accessor.ts index f8456b5506..9c150ee2ea 100644 --- a/packages/forms/src/directives/control_value_accessor.ts +++ b/packages/forms/src/directives/control_value_accessor.ts @@ -13,13 +13,9 @@ import {InjectionToken} from '@angular/core'; * Defines an interface that acts as a bridge between the Angular forms API and a * native element in the DOM. * - * 定义一个接口,该接口充当 Angular 表单 API 和 DOM 中的原生元素之间的桥梁。 - * * Implement this interface to create a custom form control directive * that integrates with Angular forms. * - * 实现此接口以创建与 Angular 表单集成的自定义表单控件指令。 - * * @see DefaultValueAccessor * * @publicApi @@ -29,23 +25,14 @@ export interface ControlValueAccessor { * @description * Writes a new value to the element. * - * 将新值写入元素。 - * * This method is called by the forms API to write to the view when programmatic * changes from model to view are requested. * - * 当请求从模型到视图的编程更改时,表单 API 会调用此方法以写入视图。 - * * @usageNotes - * * ### Write a value to the element * - * ### 向元素写入值 - * * The following example writes a value to the native DOM element. * - * 以下示例将一个值写入原生 DOM 元素。 - * * ```ts * writeValue(value: any): void { * this._renderer.setProperty(this._elementRef.nativeElement, 'value', value); @@ -53,9 +40,6 @@ export interface ControlValueAccessor { * ``` * * @param obj The new value for the element - * - * 元素的新值 - * */ writeValue(obj: any): void; @@ -64,28 +48,17 @@ export interface ControlValueAccessor { * Registers a callback function that is called when the control's value * changes in the UI. * - * 注册一个回调函数,该控件的值在 UI 中更改时将调用该回调函数。 - * * This method is called by the forms API on initialization to update the form * model when values propagate from the view to the model. * - * 当值从视图传播到模型时,表单 API 会在初始化时调用此方法以更新表单模型。 - * * When implementing the `registerOnChange` method in your own value accessor, * save the given function so your class calls it at the appropriate time. * - * 在你自己的值访问器中实现 `registerOnChange` 方法时,请保存给定的函数,以便你的类在适当的时机调用它。 - * * @usageNotes - * * ### Store the change function * - * ### 存储变更函数 - * * The following example stores the provided function as an internal method. * - * 以下示例将所提供的函数存储为内部方法。 - * * ```ts * registerOnChange(fn: (_: any) => void): void { * this._onChange = fn; @@ -95,8 +68,6 @@ export interface ControlValueAccessor { * When the value changes in the UI, call the registered * function to allow the forms API to update itself: * - * 当用户界面中的值更改时,请调用已注册的函数以允许表单 API 自行更新: - * * ```ts * host: { * '(change)': '_onChange($event.target.value)' @@ -104,9 +75,6 @@ export interface ControlValueAccessor { * ``` * * @param fn The callback function to register - * - * 要注册的回调函数 - * */ registerOnChange(fn: any): void; @@ -115,24 +83,15 @@ export interface ControlValueAccessor { * Registers a callback function that is called by the forms API on initialization * to update the form model on blur. * - * 注册一个在初始化时由表单 API 调用的回调函数,以在失焦时更新表单模型。 - * * When implementing `registerOnTouched` in your own value accessor, save the given * function so your class calls it when the control should be considered * blurred or "touched". * - * 在你自己的值访问器中实现 `registerOnTouched` ,请保存给定函数,以便你的类在应将控件视为失焦或“已接触过”时调用它。 - * * @usageNotes - * * ### Store the callback function * - * ### 存储回调函数 - * * The following example stores the provided function as an internal method. * - * 以下示例将所提供的函数存储为内部方法。 - * * ```ts * registerOnTouched(fn: any): void { * this._onTouched = fn; @@ -142,8 +101,6 @@ export interface ControlValueAccessor { * On blur (or equivalent), your class should call the registered function to allow * the forms API to update itself: * - * 在 blur(或等效事件)时,你的类应调用已注册的函数以允许表单 API 自行更新: - * * ```ts * host: { * '(blur)': '_onTouched()' @@ -151,9 +108,6 @@ export interface ControlValueAccessor { * ``` * * @param fn The callback function to register - * - * 要注册的回调函数 - * */ registerOnTouched(fn: any): void; @@ -163,14 +117,9 @@ export interface ControlValueAccessor { * or from 'DISABLED'. Depending on the status, it enables or disables the * appropriate DOM element. * - * 当控件状态更改为 “DISABLED” 或从 “DISABLED” 更改时,表单 API 要调用的函数。根据其状态,它会启用或禁用适当的 DOM 元素。 - * * @usageNotes - * * The following is an example of writing the disabled property to a native DOM element: * - * 以下是将 disabled 属性写入原生 DOM 元素的示例: - * * ```ts * setDisabledState(isDisabled: boolean): void { * this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled); @@ -178,23 +127,25 @@ export interface ControlValueAccessor { * ``` * * @param isDisabled The disabled status to set on the element - * - * 要在元素上设置的禁用状态 - * */ setDisabledState?(isDisabled: boolean): void; } +/** + * Base class for all built-in ControlValueAccessor classes. We use this class to distinguish + * between built-in and custom CVAs, so that Forms logic can recognize built-in CVAs and treat + * custom ones with higher priority (when both built-in and custom CVAs are present). + * Note: this is an *internal-only* class and should not be extended or used directly in + * applications code. + */ +export class BuiltInControlValueAccessor {} + /** * Used to provide a `ControlValueAccessor` for form controls. * - * 用于为表单控件提供 `ControlValueAccessor` - * * See `DefaultValueAccessor` for how to implement one. * - * 有关如何实现的信息,请参见 `DefaultValueAccessor` - * * @publicApi */ export const NG_VALUE_ACCESSOR = - new InjectionToken>('NgValueAccessor'); + new InjectionToken>('NgValueAccessor'); \ No newline at end of file diff --git a/packages/forms/src/directives/default_value_accessor.ts b/packages/forms/src/directives/default_value_accessor.ts index 63474e0aa8..de1883b45d 100644 --- a/packages/forms/src/directives/default_value_accessor.ts +++ b/packages/forms/src/directives/default_value_accessor.ts @@ -30,33 +30,24 @@ function _isAndroid(): boolean { * @description * Provide this token to control if form directives buffer IME input until * the "compositionend" event occurs. - * - * 提供此令牌来控制表单指令是否要缓冲 IME 输入,直到发生“ compositionend” 事件为止。 - * * @publicApi */ export const COMPOSITION_BUFFER_MODE = new InjectionToken('CompositionEventMode'); /** - * @description - * - * {@searchKeywords ngDefaultControl} - * * The default `ControlValueAccessor` for writing a value and listening to changes on input * elements. The accessor is used by the `FormControlDirective`, `FormControlName`, and * `NgModel` directives. * + * {@searchKeywords ngDefaultControl} + * * @usageNotes * * ### Using the default value accessor * - * ### 使用默认值访问器 - * * The following example shows how to use an input element that activates the default value accessor * (in this case, a text field). * - * 以下示例演示了如何使用输入元素激活默认值访问器(在这种情况下为文本字段)。 - * * ```ts * const firstNameControl = new FormControl(); * ``` @@ -70,8 +61,6 @@ export const COMPOSITION_BUFFER_MODE = new InjectionToken('CompositionE * processing. In order to attach the default value accessor to a custom element, add the * `ngDefaultControl` attribute as shown below. * - * 默认情况下,此值访问器用于 `` 和 `
    选项说明
    OptionDescriptionValue TypeDefault Value
    - {$ renderOption(option.name, option.type, option.default, option.enum) $} + {$ renderOptionName(option.name) $} {% if option.deprecated %} {% if option.deprecated === true %}

    Deprecated

    {% else %} -

    已弃用:

    - {$ option.deprecated | marked $} + {$ ('**Deprecated:** ' + option.deprecated) | marked $} {% endif %} {% endif %} {$ option.description | marked $} - {% if option.default !== undefined %}

    默认值: {$ option.default $}

    {% endif %} - {% if option.aliases.length %}

    别名: {% for alias in option.aliases %}{$ renderOptionName(alias) $}{% if not loop.last %}, {% endif %}{% endfor %}

    {% endif %} + {% if option.aliases.length %}

    Aliases: {% for alias in option.aliases %}{$ renderOptionName(alias) $}{% if not loop.last %}, {% endif %}{% endfor %}

    {% endif %}
    {$ renderOptionValues(option.type, option.enum) $}{% if option.default !== undefined %}{$ option.default $}{% endif %}
    line - - line - - - abc - - - abc - -
    abc
    - - abc - -