mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 793326deef | |||
| 10d46b4247 | |||
| b744bf6a08 | |||
| 2e5bd88ebf | |||
| 9b8ea9b5ca | |||
| eb91035102 | |||
| 17e2a2a9d1 | |||
| b431b5c6d6 | |||
| bc371dac44 | |||
| 36361b0506 | |||
| 38f53a55c3 | |||
| 8de3d099d2 | |||
| 945a3ad359 | |||
| 88003e27db | |||
| a4cba79440 | |||
| 6ca7a79bfd | |||
| e95ee96562 | |||
| 536ded2280 | |||
| 6b8e60c06a | |||
| 5004b6ebf1 | |||
| 6c08635662 | |||
| f0add81caf | |||
| 21e7584c8c | |||
| 92c9661165 | |||
| a462f91d3e | |||
| aa8d3ed69b | |||
| 2ca404dbfd | |||
| f9edb776ca | |||
| c195c3f73c | |||
| 114011d2a0 | |||
| b5153814af | |||
| e8e8e5f171 | |||
| 08fb7bc4ca | |||
| 6d709273eb | |||
| 571d7debb5 | |||
| b1ba6429d4 | |||
| 6c104a28e0 | |||
| 5e09cd1f67 | |||
| 6a7b818d94 | |||
| 9ecaa05c7d | |||
| d36a9e6bf3 | |||
| 4b452ffd3d | |||
| 3f2d16c823 | |||
| 4dca1842b3 | |||
| 442b16c551 |
@@ -13,13 +13,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3.0.2
|
||||
- uses: angular/dev-infra/github-actions/commit-message-based-labels@8420d8593135df75b283e90beba0aa9b5b9838ce
|
||||
- uses: angular/dev-infra/github-actions/commit-message-based-labels@4b8b706a0cd6ae9bc542fe74417040375239aeeb
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
post_approval_changes:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3.0.2
|
||||
- uses: angular/dev-infra/github-actions/post-approval-changes@8420d8593135df75b283e90beba0aa9b5b9838ce
|
||||
- uses: angular/dev-infra/github-actions/post-approval-changes@4b8b706a0cd6ae9bc542fe74417040375239aeeb
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -14,6 +14,6 @@ jobs:
|
||||
if: github.repository == 'angular/angular'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: angular/dev-infra/github-actions/feature-request@8420d8593135df75b283e90beba0aa9b5b9838ce
|
||||
- uses: angular/dev-infra/github-actions/feature-request@4b8b706a0cd6ae9bc542fe74417040375239aeeb
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -14,6 +14,6 @@ jobs:
|
||||
if: github.repository == 'angular/angular'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: angular/dev-infra/github-actions/lock-closed@8420d8593135df75b283e90beba0aa9b5b9838ce
|
||||
- uses: angular/dev-infra/github-actions/lock-closed@4b8b706a0cd6ae9bc542fe74417040375239aeeb
|
||||
with:
|
||||
lock-bot-key: ${{ secrets.LOCK_BOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -45,6 +45,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: 'Upload to code-scanning'
|
||||
uses: github/codeql-action/upload-sarif@3f62b754e23e0dd60f91b744033e1dc1654c0ec6 # tag=v2.1.15
|
||||
uses: github/codeql-action/upload-sarif@3e7e3b32d0fb8283594bb0a76cc60a00918b0969 # tag=v2.1.16
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
+2
-1
@@ -1062,8 +1062,9 @@ groups:
|
||||
])
|
||||
reviewers:
|
||||
users:
|
||||
- alxhub
|
||||
- AndrewKushnir
|
||||
- gkalpak
|
||||
- jessicajaniuk
|
||||
- josephperrott
|
||||
|
||||
# =========================================================
|
||||
|
||||
+68
-131
@@ -1,3 +1,71 @@
|
||||
<a name="14.1.0"></a>
|
||||
# 14.1.0 (2022-07-20)
|
||||
## Deprecations
|
||||
### core
|
||||
- The `createNgModuleRef` is deprecated in favor of newly added `createNgModule` one.
|
||||
- The bit field signature of `inject()` has been deprecated, in favor of the
|
||||
new options object. Correspondingly, `InjectFlags` is deprecated as well.
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [55308f2df5](https://github.com/angular/angular/commit/55308f2df5b0f6e8e40e3c4085c463a5b83c5ed1) | feat | add `provideAnimations()` and `provideNoopAnimations()` functions ([#46793](https://github.com/angular/angular/pull/46793)) |
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [4a2e7335b1](https://github.com/angular/angular/commit/4a2e7335b180190800cf2c5db8ed1c8841dbf473) | feat | make the `CommonModule` pipes standalone ([#46401](https://github.com/angular/angular/pull/46401)) |
|
||||
| [a7597dd080](https://github.com/angular/angular/commit/a7597dd08026a4071758323d54ccbfb382e0c780) | feat | make the CommonModule directives standalone ([#46469](https://github.com/angular/angular/pull/46469)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [33ce3883a5](https://github.com/angular/angular/commit/33ce3883a5d962adf5ee7feffd3426feefe0c0b5) | feat | Add extended diagnostic to warn when missing let on ngForOf ([#46683](https://github.com/angular/angular/pull/46683)) |
|
||||
| [6f11a58040](https://github.com/angular/angular/commit/6f11a580406877e440c43df31fae3d5f120cafed) | feat | Add extended diagnostic to warn when text attributes are intended to be bindings ([#46161](https://github.com/angular/angular/pull/46161)) |
|
||||
| [9e836c232f](https://github.com/angular/angular/commit/9e836c232ff66043bc101b905dae4c8bde18bc58) | feat | warn when style suffixes are used with attribute bindings ([#46651](https://github.com/angular/angular/pull/46651)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [93c65e7b14](https://github.com/angular/angular/commit/93c65e7b1468bb0c696dec1bc3362422a2ca5170) | feat | add extended diagnostic for non-nullable optional chains ([#46686](https://github.com/angular/angular/pull/46686)) |
|
||||
| [131d029da1](https://github.com/angular/angular/commit/131d029da16b7d486cdc200f00160b9a11871fb2) | feat | detect missing control flow directive imports in standalone components ([#46146](https://github.com/angular/angular/pull/46146)) |
|
||||
| [6b8e60c06a](https://github.com/angular/angular/commit/6b8e60c06a4f9a5fdbce9e7971d403ccb2bc6e8e) | fix | improve the missingControlFlowDirective message ([#46846](https://github.com/angular/angular/pull/46846)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [e8e8e5f171](https://github.com/angular/angular/commit/e8e8e5f1718f605c62a671b0ff15fa4dff7d8229) | feat | add `createComponent` function |
|
||||
| [b5153814af](https://github.com/angular/angular/commit/b5153814af3e6b1ec8550ddf39584efd7cca36be) | feat | add `reflectComponentType` function |
|
||||
| [96c6139c9a](https://github.com/angular/angular/commit/96c6139c9ab35aa6ab2330a5a79a5906d5c2e8be) | feat | add ability to set inputs on ComponentRef ([#46641](https://github.com/angular/angular/pull/46641)) |
|
||||
| [a6d5fe202c](https://github.com/angular/angular/commit/a6d5fe202cafb419f3beb8d09711132124b6aa9a) | feat | alias `createNgModuleRef` as `createNgModule` ([#46789](https://github.com/angular/angular/pull/46789)) |
|
||||
| [71e606d3c3](https://github.com/angular/angular/commit/71e606d3c3cfdcf93a6e40f84fd69aa75c5cca42) | feat | expose EnvironmentInjector on ApplicationRef ([#46665](https://github.com/angular/angular/pull/46665)) |
|
||||
| [19e6d9ccd3](https://github.com/angular/angular/commit/19e6d9ccd36c7776fcee7de541bba413fd6dcabf) | feat | import AsyncStackTaggingZone if available ([#46693](https://github.com/angular/angular/pull/46693)) |
|
||||
| [a7a14df5f8](https://github.com/angular/angular/commit/a7a14df5f8ff2d1e6a4dba3ac53f4479a6646810) | feat | introduce `EnvironmentInjector.runInContext` API ([#46653](https://github.com/angular/angular/pull/46653)) |
|
||||
| [fa52b6e906](https://github.com/angular/angular/commit/fa52b6e906e549e1c6b4a4f4428596d093fb0549) | feat | options object to supersede bit flags for `inject()` ([#46649](https://github.com/angular/angular/pull/46649)) |
|
||||
| [af20112222](https://github.com/angular/angular/commit/af20112222fcfa961ff9a2dee4050dd59aa4156e) | feat | support the descendants option for ContentChild queries ([#46638](https://github.com/angular/angular/pull/46638)) |
|
||||
| [945a3ad359](https://github.com/angular/angular/commit/945a3ad359cd2fe3456f635b0fe36effbfe8d5c2) | fix | Fix `runInContext` for `NgModuleRef` injector ([#46877](https://github.com/angular/angular/pull/46877)) |
|
||||
| [bb7c80477b](https://github.com/angular/angular/commit/bb7c80477b1258f48971b67b2eade6a4b41337f2) | fix | make parent injector argument required in `createEnvironmentInjector` ([#46397](https://github.com/angular/angular/pull/46397)) |
|
||||
### http
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [82acbf919b](https://github.com/angular/angular/commit/82acbf919bae2ddb6a46f41239aa2a101901f089) | feat | improve error message for nullish header ([#46059](https://github.com/angular/angular/pull/46059)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [53ca936366](https://github.com/angular/angular/commit/53ca936366fb908278571bae5fcc7fa08b19a5a0) | feat | Add ability to create `UrlTree` from any `ActivatedRouteSnapshot` ([#45877](https://github.com/angular/angular/pull/45877)) |
|
||||
| [de058bba99](https://github.com/angular/angular/commit/de058bba9979f68de2320118b537e6be4dbb888d) | feat | Add CanMatch guard to control whether a Route should match ([#46021](https://github.com/angular/angular/pull/46021)) |
|
||||
| [6c1357dd7d](https://github.com/angular/angular/commit/6c1357dd7d5f466575398ebc32c581068d9bf2df) | feat | Add stable cancelation code to `NavigationCancel` event ([#46675](https://github.com/angular/angular/pull/46675)) |
|
||||
| [a4ce273e50](https://github.com/angular/angular/commit/a4ce273e50551e6c6bc8f3a86591b04760de7ba4) | feat | Add the target `RouterStateSnapshot` to `NavigationError` ([#46731](https://github.com/angular/angular/pull/46731)) |
|
||||
| [abe3759e24](https://github.com/angular/angular/commit/abe3759e2417c0396d75e226edcef2fae99e363b) | fix | allow to return `UrlTree` from `CanMatchFn` ([#46455](https://github.com/angular/angular/pull/46455)) |
|
||||
| [e8c7dd10e9](https://github.com/angular/angular/commit/e8c7dd10e93d338456131e11d600bf758ee48634) | fix | Ensure `APP_INITIALIZER` of `enabledBlocking` option completes ([#46026](https://github.com/angular/angular/pull/46026)) |
|
||||
| [ce20ed067f](https://github.com/angular/angular/commit/ce20ed067f12714ee6488ffe07f1a1c18bb60a15) | fix | Ensure Route injector is created before running CanMatch guards ([#46394](https://github.com/angular/angular/pull/46394)) |
|
||||
| [6a7b818d94](https://github.com/angular/angular/commit/6a7b818d94fdd13d81a01285e3e4be953a29cc06) | fix | Ensure target `RouterStateSnapshot` is defined in `NavigationError` ([#46842](https://github.com/angular/angular/pull/46842)) |
|
||||
| [f94c6f433d](https://github.com/angular/angular/commit/f94c6f433dba3924b79f137cfcc49d2dfd4d679c) | fix | Expose CanMatchFn as public API ([#46394](https://github.com/angular/angular/pull/46394)) |
|
||||
| [e8ae0fe3e9](https://github.com/angular/angular/commit/e8ae0fe3e91e2e805146fad0d2f0976b1f886112) | fix | Fix cancellation code for canLoad rejections ([#46752](https://github.com/angular/angular/pull/46752)) |
|
||||
### upgrade
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [e9cb0454dc](https://github.com/angular/angular/commit/e9cb0454dce2113cfd5ba11deeca6cdcf47d47f0) | feat | more closely align `UpgradeModule#bootstrap()` with `angular.bootstrap()` ([#46214](https://github.com/angular/angular/pull/46214)) |
|
||||
## Special Thanks
|
||||
AleksanderBodurri, Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Cédric Exbrayat, Dmitrij Kuba, Dylan Hunn, George Kalpakas, Jessica Janiuk, JiaLiPassion, Joey Perrott, John Vandenberg, JoostK, Keith Li, Or'el Ben-Ya'ir, Paul Gschwendtner, Pawel Kozlowski, SyedAhm3r, arturovt, mariu, markostanimirovic and mgechev
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.0.6"></a>
|
||||
# 14.0.6 (2022-07-13)
|
||||
### compiler-cli
|
||||
@@ -18,33 +86,6 @@ Alan Agius, Andrew Scott, Bob Watson, Dylan Hunn, George Kalpakas, Ivaylo Kirov,
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.1.0-next.4"></a>
|
||||
# 14.1.0-next.4 (2022-07-06)
|
||||
## Deprecations
|
||||
### core
|
||||
- The bit field signature of `inject()` has been deprecated, in favor of the
|
||||
new options object. Correspondingly, `InjectFlags` is deprecated as well.
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [6f11a58040](https://github.com/angular/angular/commit/6f11a580406877e440c43df31fae3d5f120cafed) | feat | Add extended diagnostic to warn when text attributes are intended to be bindings ([#46161](https://github.com/angular/angular/pull/46161)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [96c6139c9a](https://github.com/angular/angular/commit/96c6139c9ab35aa6ab2330a5a79a5906d5c2e8be) | feat | add ability to set inputs on ComponentRef ([#46641](https://github.com/angular/angular/pull/46641)) |
|
||||
| [a7a14df5f8](https://github.com/angular/angular/commit/a7a14df5f8ff2d1e6a4dba3ac53f4479a6646810) | feat | introduce `EnvironmentInjector.runInContext` API ([#46653](https://github.com/angular/angular/pull/46653)) |
|
||||
| [fa52b6e906](https://github.com/angular/angular/commit/fa52b6e906e549e1c6b4a4f4428596d093fb0549) | feat | options object to supersede bit flags for `inject()` ([#46649](https://github.com/angular/angular/pull/46649)) |
|
||||
| [af20112222](https://github.com/angular/angular/commit/af20112222fcfa961ff9a2dee4050dd59aa4156e) | feat | support the descendants option for ContentChild queries ([#46638](https://github.com/angular/angular/pull/46638)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [6c1357dd7d](https://github.com/angular/angular/commit/6c1357dd7d5f466575398ebc32c581068d9bf2df) | feat | Add stable cancelation code to `NavigationCancel` event ([#46675](https://github.com/angular/angular/pull/46675)) |
|
||||
| [e8c7dd10e9](https://github.com/angular/angular/commit/e8c7dd10e93d338456131e11d600bf758ee48634) | fix | Ensure `APP_INITIALIZER` of `enabledBlocking` option completes ([#46026](https://github.com/angular/angular/pull/46026)) |
|
||||
## Special Thanks
|
||||
Alan Agius, Alex Rickabaugh, Andrew Scott, Aristeidis Bampakos, Bob Watson, George Kalpakas, Paul Gschwendtner and Pawel Kozlowski
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.0.5"></a>
|
||||
# 14.0.5 (2022-07-06)
|
||||
### router
|
||||
@@ -56,39 +97,6 @@ Alan Agius, Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Aristeidis Bampakos,
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.1.0-next.3"></a>
|
||||
# 14.1.0-next.3 (2022-06-29)
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [4eb1ca11f0](https://github.com/angular/angular/commit/4eb1ca11f0fbc7683115fc382420b3e75f248b4c) | fix | cleanup DOM elements when the root view is removed ([#45143](https://github.com/angular/angular/pull/45143)) |
|
||||
| [b417370383](https://github.com/angular/angular/commit/b41737038377af480763226a904b67e7d8e283ae) | fix | enable shadowElements to leave when their parent does ([#46459](https://github.com/angular/angular/pull/46459)) |
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [4a2e7335b1](https://github.com/angular/angular/commit/4a2e7335b180190800cf2c5db8ed1c8841dbf473) | feat | make the `CommonModule` pipes standalone ([#46401](https://github.com/angular/angular/pull/46401)) |
|
||||
| [a7597dd080](https://github.com/angular/angular/commit/a7597dd08026a4071758323d54ccbfb382e0c780) | feat | make the CommonModule directives standalone ([#46469](https://github.com/angular/angular/pull/46469)) |
|
||||
| [1c39aace0e](https://github.com/angular/angular/commit/1c39aace0e74b02338729e5fb61e322ab06a9e14) | fix | handle CSS custom properties in NgStyle ([#46451](https://github.com/angular/angular/pull/46451)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [8b6f4215a3](https://github.com/angular/angular/commit/8b6f4215a3c13a44d989d6068f6fb4625f3bab67) | fix | trigger `ApplicationRef.destroy` when Platform is destroyed ([#46497](https://github.com/angular/angular/pull/46497)) |
|
||||
| [606d94299a](https://github.com/angular/angular/commit/606d94299ac929243e8fa10fe7e00dee6d40064b) | fix | Update ngfor error code to be negative ([#46555](https://github.com/angular/angular/pull/46555)) |
|
||||
| [f86e0948f8](https://github.com/angular/angular/commit/f86e0948f80d398a8c4da9032372e083acc5d7a2) | fix | Updates error to use RuntimeError code ([#46526](https://github.com/angular/angular/pull/46526)) |
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [0a5c8c0bc4](https://github.com/angular/angular/commit/0a5c8c0bc45ec58bcd297c7b001b88aec5c5e14d) | fix | Convert existing reactive errors to use RuntimeErrorCode. ([#46560](https://github.com/angular/angular/pull/46560)) |
|
||||
| [f6a096e3d4](https://github.com/angular/angular/commit/f6a096e3d4db9dfc31ab9228f37e119b87e5f8cf) | fix | Update a Forms validator error to use RuntimeError ([#46537](https://github.com/angular/angular/pull/46537)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [ee39cf62bc](https://github.com/angular/angular/commit/ee39cf62bc3f3d60d1dfe7064fe34565e5429b84) | fix | Ensure that new `RouterOutlet` instances work after old ones are destroyed ([#46554](https://github.com/angular/angular/pull/46554)) |
|
||||
## Special Thanks
|
||||
Alan Agius, Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Bezael, Chad Ramos, Chellappan, Cédric Exbrayat, Dylan Hunn, George Kalpakas, Jeremy Meiss, Jessica Janiuk, Joey Perrott, KMathy, Kristiyan Kostadinov, Paul Gschwendtner, Pawel Kozlowski, Ramesh Thiruchelvam, Vaibhav Kumar, arturovt, dario-piotrowicz and renovate[bot]
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.0.4"></a>
|
||||
# 14.0.4 (2022-06-29)
|
||||
### animations
|
||||
@@ -120,37 +128,6 @@ Alan Agius, Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Bezael, Chad Ramos, C
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.1.0-next.2"></a>
|
||||
# 14.1.0-next.2 (2022-06-22)
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [342effeb7d](https://github.com/angular/angular/commit/342effeb7d4d078ea51a7c345497794a7834b3c0) | fix | reset the start and done fns on player reset ([#46364](https://github.com/angular/angular/pull/46364)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [ddd6d66203](https://github.com/angular/angular/commit/ddd6d662038336f9a8e7d68a8b5cda2c3ca1c1d7) | fix | deduplicate imports of standalone components in JIT compiler ([#46439](https://github.com/angular/angular/pull/46439)) |
|
||||
| [3d8787c961](https://github.com/angular/angular/commit/3d8787c961176e15a7583d415f778054378ea06e) | fix | handle NgModules with standalone pipes in TestBed correctly ([#46407](https://github.com/angular/angular/pull/46407)) |
|
||||
| [bb7c80477b](https://github.com/angular/angular/commit/bb7c80477b1258f48971b67b2eade6a4b41337f2) | fix | make parent injector argument required in `createEnvironmentInjector` ([#46397](https://github.com/angular/angular/pull/46397)) |
|
||||
### http
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [82acbf919b](https://github.com/angular/angular/commit/82acbf919bae2ddb6a46f41239aa2a101901f089) | feat | improve error message for nullish header ([#46059](https://github.com/angular/angular/pull/46059)) |
|
||||
### platform-server
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [08d3db232c](https://github.com/angular/angular/commit/08d3db232cb758701c41277996f1b41773f98e0d) | fix | invalid style attribute being generated for null values ([#46433](https://github.com/angular/angular/pull/46433)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [abe3759e24](https://github.com/angular/angular/commit/abe3759e2417c0396d75e226edcef2fae99e363b) | fix | allow to return `UrlTree` from `CanMatchFn` ([#46455](https://github.com/angular/angular/pull/46455)) |
|
||||
| [ce20ed067f](https://github.com/angular/angular/commit/ce20ed067f12714ee6488ffe07f1a1c18bb60a15) | fix | Ensure Route injector is created before running CanMatch guards ([#46394](https://github.com/angular/angular/pull/46394)) |
|
||||
| [f94c6f433d](https://github.com/angular/angular/commit/f94c6f433dba3924b79f137cfcc49d2dfd4d679c) | fix | Expose CanMatchFn as public API ([#46394](https://github.com/angular/angular/pull/46394)) |
|
||||
## Special Thanks
|
||||
4javier, Aakash, Alan Agius, Andrew Kushnir, Andrew Scott, Aristeidis Bampakos, Dany Paredes, Derek Cormier, Dmitrij Kuba, JoostK, Kristiyan Kostadinov, Paul Gschwendtner, Ramesh Thiruchelvam, arturovt, behrooz bozorg chami, dario-piotrowicz, markostanimirovic, renovate[bot] and web-dave
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.0.3"></a>
|
||||
# 14.0.3 (2022-06-22)
|
||||
### animations
|
||||
@@ -171,31 +148,6 @@ Alan Agius, Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Bezael, Chad Ramos, C
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.1.0-next.1"></a>
|
||||
# 14.1.0-next.1 (2022-06-15)
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [8b9c32d4dc](https://github.com/angular/angular/commit/8b9c32d4dccca5417adce1ca6067adf0805d6d08) | fix | allow null in ngComponentOutlet ([#46280](https://github.com/angular/angular/pull/46280)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [131d029da1](https://github.com/angular/angular/commit/131d029da16b7d486cdc200f00160b9a11871fb2) | feat | detect missing control flow directive imports in standalone components ([#46146](https://github.com/angular/angular/pull/46146)) |
|
||||
| [04acc6b14d](https://github.com/angular/angular/commit/04acc6b14d502597a98537019822f1a1217c9a61) | fix | don't emit empty providers array ([#46301](https://github.com/angular/angular/pull/46301)) |
|
||||
| [f35f4751b5](https://github.com/angular/angular/commit/f35f4751b54dcb9e65f2d72aeea87651314159e8) | fix | use inline type-check blocks for components outside `rootDir` ([#46096](https://github.com/angular/angular/pull/46096)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [b1a3dec58d](https://github.com/angular/angular/commit/b1a3dec58d0dc21d76f757d4085c7581d89dc7dd) | fix | Resolve forwardRef declarations for jit ([#46334](https://github.com/angular/angular/pull/46334)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [de058bba99](https://github.com/angular/angular/commit/de058bba9979f68de2320118b537e6be4dbb888d) | feat | Add CanMatch guard to control whether a Route should match ([#46021](https://github.com/angular/angular/pull/46021)) |
|
||||
## Special Thanks
|
||||
Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Badawi7, Daniel Schmitz, Derek Cormier, Jessica Janiuk, JiaLiPassion, JoostK, Kevin Davila, Kristiyan Kostadinov, Paul Draper, Paul Gschwendtner, Tom Eustace, Totati, Younes Jaaidi, alefra, dario-piotrowicz, markostanimirovic, mgechev, piyush132000, sten-bone and vivekkoya
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.0.2"></a>
|
||||
# 14.0.2 (2022-06-15)
|
||||
### common
|
||||
@@ -216,21 +168,6 @@ Alex Rickabaugh, Andrew Scott, Badawi7, Daniel Schmitz, Derek Cormier, JoostK, K
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.1.0-next.0"></a>
|
||||
# 14.1.0-next.0 (2022-06-08)
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [53ca936366](https://github.com/angular/angular/commit/53ca936366fb908278571bae5fcc7fa08b19a5a0) | feat | Add ability to create `UrlTree` from any `ActivatedRouteSnapshot` ([#45877](https://github.com/angular/angular/pull/45877)) |
|
||||
### upgrade
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [e9cb0454dc](https://github.com/angular/angular/commit/e9cb0454dce2113cfd5ba11deeca6cdcf47d47f0) | feat | more closely align `UpgradeModule#bootstrap()` with `angular.bootstrap()` ([#46214](https://github.com/angular/angular/pull/46214)) |
|
||||
## Special Thanks
|
||||
AleksanderBodurri, Alex Rickabaugh, Andrew Kushnir, Andrew Scott, George Kalpakas, Jessica Janiuk, Joey Perrott, Keith Li, SyedAhm3r, mariu and mgechev
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.0.1"></a>
|
||||
# 14.0.1 (2022-06-08)
|
||||
### bazel
|
||||
|
||||
@@ -7,6 +7,5 @@ import { Component } from '@angular/core';
|
||||
})
|
||||
export class CartComponent {
|
||||
|
||||
constructor() { }
|
||||
|
||||
}
|
||||
|
||||
+2
-11
@@ -1,11 +1,6 @@
|
||||
// #docplaster
|
||||
/*
|
||||
// #docregion as-generated
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
// #enddocregion as-generated
|
||||
*/
|
||||
// #docregion imports
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Product } from '../products';
|
||||
// #enddocregion imports
|
||||
// #docregion as-generated
|
||||
@@ -16,14 +11,10 @@ import { Product } from '../products';
|
||||
styleUrls: ['./product-alerts.component.css']
|
||||
})
|
||||
// #docregion input-decorator
|
||||
export class ProductAlertsComponent implements OnInit {
|
||||
export class ProductAlertsComponent {
|
||||
|
||||
// #enddocregion as-generated
|
||||
@Input() product!: Product;
|
||||
// #docregion as-generated
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# Service worker configuration
|
||||
|
||||
This topic describes the properties of the service worker configuration file.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
A basic understanding of the following:
|
||||
|
||||
* [Service worker overview](https://developer.chrome.com/docs/workbox/service-worker-overview/)
|
||||
* [Service Worker in Production](guide/service-worker-devops)
|
||||
|
||||
The `ngsw-config.json` configuration file specifies which files and data URLs the Angular service worker should cache and how it should update the cached files and data.
|
||||
@@ -64,9 +67,11 @@ Example patterns:
|
||||
| `/*.html` | Specifies only HTML files in the root |
|
||||
| `!/**/*.map` | Exclude all sourcemaps |
|
||||
|
||||
## Service worker configuration properties
|
||||
|
||||
The following sections describe each property of the configuration file.
|
||||
|
||||
## `appData`
|
||||
### `appData`
|
||||
|
||||
This section enables you to pass any data you want that describes this particular version of the application.
|
||||
The `SwUpdate` service includes that data in the update notifications.
|
||||
@@ -74,12 +79,12 @@ Many applications use this section to provide additional information for the dis
|
||||
|
||||
<a id="index-file"></a>
|
||||
|
||||
## `index`
|
||||
### `index`
|
||||
|
||||
Specifies the file that serves as the index page to satisfy navigation requests.
|
||||
Usually this is `/index.html`.
|
||||
|
||||
## `assetGroups`
|
||||
### `assetGroups`
|
||||
|
||||
*Assets* are resources that are part of the application version that update along with the application.
|
||||
They can include resources loaded from the page's origin as well as third-party resources loaded from CDNs and other external URLs.
|
||||
@@ -134,12 +139,14 @@ interface AssetGroup {
|
||||
|
||||
</code-example>
|
||||
|
||||
### `name`
|
||||
Each `AssetGroup` is defined by the following asset group properties.
|
||||
|
||||
#### `name`
|
||||
|
||||
A `name` is mandatory.
|
||||
It identifies this particular group of assets between versions of the configuration.
|
||||
|
||||
### `installMode`
|
||||
#### `installMode`
|
||||
|
||||
The `installMode` determines how these resources are initially cached.
|
||||
The `installMode` can be either of two values:
|
||||
@@ -151,7 +158,7 @@ The `installMode` can be either of two values:
|
||||
|
||||
Defaults to `prefetch`.
|
||||
|
||||
### `updateMode`
|
||||
#### `updateMode`
|
||||
|
||||
For resources already in the cache, the `updateMode` determines the caching behavior when a new version of the application is discovered.
|
||||
Any resources in the group that have changed since the previous version are updated in accordance with `updateMode`.
|
||||
@@ -163,7 +170,7 @@ Any resources in the group that have changed since the previous version are upda
|
||||
|
||||
Defaults to the value `installMode` is set to.
|
||||
|
||||
### `resources`
|
||||
#### `resources`
|
||||
|
||||
This section describes the resources to cache, broken up into the following groups:
|
||||
|
||||
@@ -172,7 +179,7 @@ This section describes the resources to cache, broken up into the following grou
|
||||
| `files` | Lists patterns that match files in the distribution directory. These can be single files or glob-like patterns that match a number of files. |
|
||||
| `urls` | Includes both URLs and URL patterns that are matched at runtime. These resources are not fetched directly and do not have content hashes, but they are cached according to their HTTP headers. This is most useful for CDNs such as the Google Fonts service. <br /> *\(Negative glob patterns are not supported and `?` will be matched literally; that is, it will not match any character other than `?`.\)* |
|
||||
|
||||
### `cacheQueryOptions`
|
||||
#### `cacheQueryOptions`
|
||||
|
||||
These options are used to modify the matching behavior of requests.
|
||||
They are passed to the browsers `Cache#match` function.
|
||||
@@ -183,7 +190,7 @@ Currently, only the following options are supported:
|
||||
|:--- |:--- |
|
||||
| `ignoreSearch` | Ignore query parameters. Defaults to `false`. |
|
||||
|
||||
## `dataGroups`
|
||||
### `dataGroups`
|
||||
|
||||
Unlike asset resources, data requests are not versioned along with the application.
|
||||
They're cached according to manually-configured policies that are more useful for situations such as API requests and other data dependencies.
|
||||
@@ -236,11 +243,13 @@ export interface DataGroup {
|
||||
|
||||
</code-example>
|
||||
|
||||
### `name`
|
||||
Each `DataGroup` is defined by the following data group properties.
|
||||
|
||||
#### `name`
|
||||
|
||||
Similar to `assetGroups`, every data group has a `name` which uniquely identifies it.
|
||||
|
||||
### `urls`
|
||||
#### `urls`
|
||||
|
||||
A list of URL patterns.
|
||||
URLs that match these patterns are cached according to this data group's policy.
|
||||
@@ -249,7 +258,7 @@ Only non-mutating requests \(GET and HEAD\) are cached.
|
||||
* Negative glob patterns are not supported
|
||||
* `?` is matched literally; that is, it matches *only* the character `?`
|
||||
|
||||
### `version`
|
||||
#### `version`
|
||||
|
||||
Occasionally APIs change formats in a way that is not backward-compatible.
|
||||
A new version of the application might not be compatible with the old API format and thus might not be compatible with existing cached resources from that API.
|
||||
@@ -258,18 +267,22 @@ A new version of the application might not be compatible with the old API format
|
||||
|
||||
`version` is an integer field and defaults to `1`.
|
||||
|
||||
### `cacheConfig`
|
||||
#### `cacheConfig`
|
||||
|
||||
This section defines the policy by which matching requests are cached.
|
||||
The following properties define the policy by which matching requests are cached.
|
||||
|
||||
#### `maxSize`
|
||||
##### `maxSize`
|
||||
|
||||
\(required\) The maximum number of entries, or responses, in the cache.
|
||||
**Required**
|
||||
|
||||
The maximum number of entries, or responses, in the cache.
|
||||
Open-ended caches can grow in unbounded ways and eventually exceed storage quotas, calling for eviction.
|
||||
|
||||
#### `maxAge`
|
||||
##### `maxAge`
|
||||
|
||||
(required) The `maxAge` parameter indicates how long responses are allowed to remain in the cache before being considered invalid and evicted.
|
||||
**Required**
|
||||
|
||||
The `maxAge` parameter indicates how long responses are allowed to remain in the cache before being considered invalid and evicted.
|
||||
`maxAge` is a duration string, using the following unit suffixes:
|
||||
|
||||
| Suffixes | Details |
|
||||
@@ -282,7 +295,7 @@ Open-ended caches can grow in unbounded ways and eventually exceed storage quota
|
||||
|
||||
For example, the string `3d12h` caches content for up to three and a half days.
|
||||
|
||||
#### `timeout`
|
||||
##### `timeout`
|
||||
|
||||
This duration string specifies the network timeout.
|
||||
The network timeout is how long the Angular service worker waits for the network to respond before using a cached response, if configured to do so.
|
||||
@@ -298,7 +311,7 @@ The network timeout is how long the Angular service worker waits for the network
|
||||
|
||||
For example, the string `5s30u` translates to five seconds and 30 milliseconds of network timeout.
|
||||
|
||||
#### `strategy`
|
||||
##### `strategy`
|
||||
|
||||
The Angular service worker can use either of two caching strategies for data resources.
|
||||
|
||||
@@ -309,19 +322,19 @@ The Angular service worker can use either of two caching strategies for data res
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
You can also emulate a third strategy, [staleWhileRevalidate](https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#stale-while-revalidate), which returns cached data \(if available\), but also fetches fresh data from the network in the background for next time.
|
||||
You can also emulate a third strategy, [staleWhileRevalidate](https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#stale-while-revalidate), which returns cached data if it is available, but also fetches fresh data from the network in the background for next time.
|
||||
To use this strategy set `strategy` to `freshness` and `timeout` to `0u` in `cacheConfig`.
|
||||
|
||||
This essentially does the following:
|
||||
|
||||
1. Try to fetch from the network first.
|
||||
1. If the network request does not complete after 0ms \(that is, immediately\), fall back to the cache \(ignoring cache age\).
|
||||
1. Once the network request completes, update the cache for future requests.
|
||||
1. If the resource does not exist in the cache, wait for the network request anyway.
|
||||
2. If the network request does not complete immediately, that is after a timeout of 0 ms, ignore the cache age and fall back to the cached value.
|
||||
3. Once the network request completes, update the cache for future requests.
|
||||
4. If the resource does not exist in the cache, wait for the network request anyway.
|
||||
|
||||
</div>
|
||||
|
||||
#### `cacheOpaqueResponses`
|
||||
##### `cacheOpaqueResponses`
|
||||
|
||||
Whether the Angular service worker should cache opaque responses or not.
|
||||
|
||||
@@ -329,8 +342,8 @@ If not specified, the default value depends on the data group's configured strat
|
||||
|
||||
| Strategies | Details |
|
||||
|:--- |:--- |
|
||||
| Groups with the `freshness` strategy | The default value is `true` \(cache opaque responses\). These groups will request the data anew every time, only falling back to the cached response when offline or on a slow network. Therefore, it doesn't matter if the service worker caches an error response. |
|
||||
| Groups with the `performance` strategy | The default value is `false` \(do not cache opaque responses\). These groups would continue to return a cached response until `maxAge` expires, even if the error was due to a temporary network or server issue. Therefore, it would be problematic for the service worker to cache an error response. |
|
||||
| Groups with the `freshness` strategy | The default value is `true` and the service worker caches opaque responses. These groups will request the data every time and only fall back to the cached response when offline or on a slow network. Therefore, it doesn't matter if the service worker caches an error response. |
|
||||
| Groups with the `performance` strategy | The default value is `false` and the service worker doesn't cache opaque responses. These groups would continue to return a cached response until `maxAge` expires, even if the error was due to a temporary network or server issue. Therefore, it would be problematic for the service worker to cache an error response. |
|
||||
|
||||
<div class="callout is-important">
|
||||
|
||||
@@ -344,27 +357,24 @@ If you are not able to implement CORS —for example, if you don't control t
|
||||
|
||||
</div>
|
||||
|
||||
### `cacheQueryOptions`
|
||||
#### `cacheQueryOptions`
|
||||
|
||||
See [assetGroups](#assetgroups) for details.
|
||||
|
||||
## `navigationUrls`
|
||||
### `navigationUrls`
|
||||
|
||||
This optional section enables you to specify a custom list of URLs that will be redirected to the index file.
|
||||
|
||||
### Handling navigation requests
|
||||
#### Handling navigation requests
|
||||
|
||||
The ServiceWorker redirects navigation requests that don't match any `asset` or `data` group to the specified [index file](#index-file).
|
||||
A request is considered to be a navigation request if:
|
||||
|
||||
* Its [mode](https://developer.mozilla.org/docs/Web/API/Request/mode) is `navigation`
|
||||
* It accepts a `text/html` response \(as determined by the value of the `Accept` header\)
|
||||
* Its URL matches certain criteria \(see the following\)
|
||||
|
||||
By default, these criteria are:
|
||||
|
||||
* The URL must not contain a file extension \(that is, a `.`\) in the last path segment
|
||||
* The URL must not contain `__`
|
||||
* It accepts a `text/html` response as determined by the value of the `Accept` header
|
||||
* Its URL matches the following criteria:
|
||||
* The URL must not contain a file extension \(that is, a `.`\) in the last path segment
|
||||
* The URL must not contain `__`
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@@ -372,10 +382,10 @@ To configure whether navigation requests are sent through to the network or not,
|
||||
|
||||
</div>
|
||||
|
||||
### Matching navigation request URLs
|
||||
#### Matching navigation request URLs
|
||||
|
||||
While these default criteria are fine in most cases, it is sometimes desirable to configure different rules.
|
||||
For example, you might want to ignore specific routes \(that are not part of the Angular app\) and pass them through to the server.
|
||||
For example, you might want to ignore specific routes, such as those that are not part of the Angular app, and pass them through to the server.
|
||||
|
||||
This field contains an array of URLs and [glob-like](#glob-patterns) URL patterns that are matched at runtime.
|
||||
It can contain both negative patterns \(that is, patterns starting with `!`\) and non-negative patterns and URLs.
|
||||
@@ -398,7 +408,7 @@ If the field is omitted, it defaults to:
|
||||
|
||||
<a id="navigation-request-strategy"></a>
|
||||
|
||||
## `navigationRequestStrategy`
|
||||
### `navigationRequestStrategy`
|
||||
|
||||
This optional property enables you to configure how the service worker handles navigation requests:
|
||||
|
||||
@@ -413,7 +423,7 @@ This optional property enables you to configure how the service worker handles n
|
||||
| Possible values | Details |
|
||||
|:--- |:--- |
|
||||
| `'performance'` | The default setting. Serves the specified [index file](#index-file), which is typically cached. |
|
||||
| `'freshness'` | Passes the requests through to the network and falls back to the `performance` behavior when offline. This value is useful when the server redirects the navigation requests elsewhere using an HTTP redirect \(3xx status code\). Reasons for using this value include: <ul> <li> Redirecting to an authentication website when authentication is not handled by the application </li> <li> Redirecting specific URLs to avoid breaking existing links/bookmarks after a website redesign </li> <li> Redirecting to a different website, such as a server-status page, while a page is temporarily down </li> </ul> |
|
||||
| `'freshness'` | Passes the requests through to the network and falls back to the `performance` behavior when offline. This value is useful when the server redirects the navigation requests elsewhere using a `3xx` HTTP redirect status code. Reasons for using this value include: <ul> <li> Redirecting to an authentication website when authentication is not handled by the application </li> <li> Redirecting specific URLs to avoid breaking existing links/bookmarks after a website redesign </li> <li> Redirecting to a different website, such as a server-status page, while a page is temporarily down </li> </ul> |
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
|
||||
@@ -9,21 +9,21 @@ A basic understanding of the following:
|
||||
|
||||
* [Service Worker Communication](guide/service-worker-communications)
|
||||
|
||||
## Service worker and caching of app resources
|
||||
## Service worker and caching of application resources
|
||||
|
||||
Conceptually, imagine the Angular service worker as a forward cache or a CDN edge that is installed in the end user's web browser.
|
||||
The service worker's job is to satisfy requests made by the Angular application for resources or data from a local cache, without needing to wait for the network.
|
||||
Imagine the Angular service worker as a forward cache or a Content Delivery Network (CDN) edge that is installed in the end user's web browser.
|
||||
The service worker responds to requests made by the Angular application for resources or data from a local cache, without needing to wait for the network.
|
||||
Like any cache, it has rules for how content is expired and updated.
|
||||
|
||||
<a id="versions"></a>
|
||||
|
||||
### App versions
|
||||
### Application versions
|
||||
|
||||
In the context of an Angular service worker, a "version" is a collection of resources that represent a specific build of the Angular application.
|
||||
Whenever a new build of the application is deployed, the service worker treats that build as a new version of the application.
|
||||
This is true even if only a single file is updated.
|
||||
At any given time, the service worker might have multiple versions of the application in its cache and it might be serving them simultaneously.
|
||||
For more information, see the [App tabs](guide/service-worker-devops#tabs) section below.
|
||||
For more information, see the [Application tabs](guide/service-worker-devops#tabs) section.
|
||||
|
||||
To preserve application integrity, the Angular service worker groups all files into a version together.
|
||||
The files grouped into a version usually include HTML, JS, and CSS files.
|
||||
@@ -35,15 +35,15 @@ In this scenario, it is not valid to serve the old `index.html`, which calls `st
|
||||
|
||||
This file integrity is especially important when lazy loading modules.
|
||||
A JS bundle might reference many lazy chunks, and the filenames of the lazy chunks are unique to the particular build of the application.
|
||||
If a running application at version `X` attempts to load a lazy chunk, but the server has already updated to version `X + 1`, the lazy loading operation will fail.
|
||||
If a running application at version `X` attempts to load a lazy chunk, but the server has already updated to version `X + 1`, the lazy loading operation fails.
|
||||
|
||||
The version identifier of the application is determined by the contents of all resources, and it changes if any of them change.
|
||||
In practice, the version is determined by the contents of the `ngsw.json` file, which includes hashes for all known content.
|
||||
If any of the cached files change, the file's hash will change in `ngsw.json`, causing the Angular service worker to treat the active set of files as a new version.
|
||||
If any of the cached files change, the file's hash changes in `ngsw.json`. This change causes the Angular service worker to treat the active set of files as a new version.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
`ngsw.json` is the manifest file that is generated at build time based on `ngsw-config.json`.
|
||||
The build process creates the manifest file, `ngsw.json`, using information from `ngsw-config.json`.
|
||||
|
||||
</div>
|
||||
|
||||
@@ -52,25 +52,27 @@ With the versioning behavior of the Angular service worker, an application serve
|
||||
#### Update checks
|
||||
|
||||
Every time the user opens or refreshes the application, the Angular service worker checks for updates to the application by looking for updates to the `ngsw.json` manifest.
|
||||
If an update is found, it is downloaded and cached automatically, and will be served the next time the application is loaded.
|
||||
If an update is found, it is downloaded and cached automatically, and is served the next time the application is loaded.
|
||||
|
||||
### Resource integrity
|
||||
|
||||
One of the potential side effects of long caching is inadvertently caching an invalid resource.
|
||||
In a normal HTTP cache, a hard refresh or cache expiration limits the negative effects of caching an invalid file.
|
||||
A service worker ignores such constraints and effectively long caches the entire application.
|
||||
Consequently, it is essential that the service worker gets the correct content.
|
||||
One of the potential side effects of long caching is inadvertently caching a resource that's not valid.
|
||||
In a normal HTTP cache, a hard refresh or the cache expiring limits the negative effects of caching a file that's not valid.
|
||||
A service worker ignores such constraints and effectively long-caches the entire application.
|
||||
It's important that the service worker gets the correct content, so it keeps hashes of the resources to maintain their integrity.
|
||||
|
||||
#### Hashed content
|
||||
|
||||
To ensure resource integrity, the Angular service worker validates the hashes of all resources for which it has a hash.
|
||||
Typically for an application created with the [Angular CLI](cli), this is everything in the `dist` directory covered by the user's `src/ngsw-config.json` configuration.
|
||||
For an application created with the [Angular CLI](cli), this is everything in the `dist` directory covered by the user's `src/ngsw-config.json` configuration.
|
||||
|
||||
If a particular file fails validation, the Angular service worker attempts to re-fetch the content using a "cache-busting" URL parameter to eliminate the effects of browser or intermediate caching.
|
||||
If that content also fails validation, the service worker considers the entire version of the application to be invalid and it stops serving the application.
|
||||
If necessary, the service worker enters a safe mode where requests fall back on the network, opting not to use its cache if the risk of serving invalid, broken, or outdated content is high.
|
||||
If a particular file fails validation, the Angular service worker attempts to re-fetch the content using a "cache-busting" URL parameter to prevent browser or intermediate caching.
|
||||
If that content also fails validation, the service worker considers the entire version of the application to not be valid and stops serving the application.
|
||||
If necessary, the service worker enters a safe mode where requests fall back on the network. The service worker doesn't use its cache if there's a high risk of serving content that is broken, outdated, or not valid.
|
||||
|
||||
Hash mismatches can occur for a variety of reasons:
|
||||
|
||||
* Caching layers in between the origin server and the end user could serve stale content
|
||||
* Caching layers between the origin server and the end user could serve stale content
|
||||
* A non-atomic deployment could result in the Angular service worker having visibility of partially updated content
|
||||
* Errors during the build process could result in updated resources without `ngsw.json` being updated.
|
||||
The reverse could also happen resulting in an updated `ngsw.json` without updated resources.
|
||||
@@ -80,36 +82,36 @@ Hash mismatches can occur for a variety of reasons:
|
||||
The only resources that have hashes in the `ngsw.json` manifest are resources that were present in the `dist` directory at the time the manifest was built.
|
||||
Other resources, especially those loaded from CDNs, have content that is unknown at build time or are updated more frequently than the application is deployed.
|
||||
|
||||
If the Angular service worker does not have a hash to validate a given resource, it still caches its contents but it honors the HTTP caching headers by using a policy of "stale while revalidate".
|
||||
That is, when HTTP caching headers for a cached resource indicate that the resource has expired, the Angular service worker continues to serve the content and it attempts to refresh the resource in the background.
|
||||
If the Angular service worker does not have a hash to verify a resource is valid, it still caches its contents. At the same time, it honors the HTTP caching headers by using a policy of *stale while revalidate*.
|
||||
The Angular service worker continues to serve a resource even after its HTTP caching headers indicate
|
||||
that it is no longer valid. At the same time, it attempts to refresh the expired resource in the background.
|
||||
This way, broken unhashed resources do not remain in the cache beyond their configured lifetimes.
|
||||
|
||||
<a id="tabs"></a>
|
||||
|
||||
### App tabs
|
||||
### Application tabs
|
||||
|
||||
It can be problematic for an application if the version of resources it's receiving changes suddenly or without warning.
|
||||
See the [App versions](guide/service-worker-devops#versions) section above for a description of such issues.
|
||||
See the [Application versions](guide/service-worker-devops#versions) section for a description of such issues.
|
||||
|
||||
The Angular service worker provides a guarantee: a running application will continue to run the same version of the application.
|
||||
If another instance of the application is opened in a new web browser tab, then the most current version of the app is served.
|
||||
The Angular service worker provides a guarantee: a running application continues to run the same version of the application.
|
||||
If another instance of the application is opened in a new web browser tab, then the most current version of the application is served.
|
||||
As a result, that new tab can be running a different version of the application than the original tab.
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
**IMPORTANT**: <br />
|
||||
This guarantee is **stronger** than that provided by the normal web deployment model.
|
||||
Without a service worker, there is no guarantee that code lazily loaded later in a running application is from the same version as the initial code for the application.
|
||||
Without a service worker, there is no guarantee that lazily loaded code is from the same version as the application's initial code.
|
||||
|
||||
</div>
|
||||
|
||||
There are a few limited reasons why the Angular service worker might change the version of a running application.
|
||||
Some of them are error conditions:
|
||||
The Angular service worker might change the version of a running application under error conditions such as:
|
||||
|
||||
* The current version becomes invalid due to a failed hash
|
||||
* An unrelated error causes the service worker to enter safe mode; that is, temporary deactivation
|
||||
* The current version becomes non-valid due to a failed hash
|
||||
* An unrelated error causes the service worker to enter safe mode and deactivates it temporarily
|
||||
|
||||
The Angular service worker is aware of which versions are in use at any given moment and it cleans up versions when no tab is using them.
|
||||
The Angular service worker cleans up application versions when no tab is using them.
|
||||
|
||||
Other reasons the Angular service worker might change the version of a running application are normal events:
|
||||
|
||||
@@ -119,27 +121,35 @@ Other reasons the Angular service worker might change the version of a running a
|
||||
### Service worker updates
|
||||
|
||||
The Angular service worker is a small script that runs in web browsers.
|
||||
From time to time, the service worker will be updated with bug fixes and feature improvements.
|
||||
From time to time, the service worker is updated with bug fixes and feature improvements.
|
||||
|
||||
The Angular service worker is downloaded when the application is first opened and when the application is accessed after a period of inactivity.
|
||||
If the service worker has changed, the service worker will be updated in the background.
|
||||
If the service worker changes, it's updated in the background.
|
||||
|
||||
Most updates to the Angular service worker are transparent to the app —the old caches are still valid and content is still served
|
||||
normally.
|
||||
However, occasionally a bugfix or feature in the Angular service worker requires the invalidation of old caches.
|
||||
In this case, the application will be refreshed transparently from the network.
|
||||
Most updates to the Angular service worker are transparent to the application. The old caches are still valid and content is still served normally.
|
||||
Occasionally, a bug fix or feature in the Angular service worker might require the invalidation of old caches.
|
||||
In this case, the service worker transparently refreshes the application from the network.
|
||||
|
||||
### Bypassing the service worker
|
||||
|
||||
In some cases, you might want to bypass the service worker entirely and let the browser handle the request instead.
|
||||
An example is when you rely on a feature that is currently not supported in service workers \(for example, [reporting progress on uploaded files](https://github.com/w3c/ServiceWorker/issues/1141)\).
|
||||
In some cases, you might want to bypass the service worker entirely and let the browser handle the request.
|
||||
An example is when you rely on a feature that is currently not supported in service workers, such as [reporting progress on uploaded files](https://github.com/w3c/ServiceWorker/issues/1141).
|
||||
|
||||
To bypass the service worker, set `ngsw-bypass` as a request header, or as a query parameter.
|
||||
\(The value of the header or query parameter is ignored and can be empty or omitted.\)
|
||||
The value of the header or query parameter is ignored and can be empty or omitted.
|
||||
|
||||
### Service worker requests when the server can't be reached
|
||||
|
||||
The service worker processes all requests unless the [service worker is explicitly bypassed](#bypassing-the-service-worker).
|
||||
The service worker either returns a cached response or sends the request to the server, depending on the state and configuration of the cache.
|
||||
The service worker only caches responses to non-mutating requests, such as `GET` and `HEAD`.
|
||||
|
||||
If the service worker receives an error from the server or it doesn't receive a response, it returns an error status that indicates the result of the call.
|
||||
For example, if the service worker doesn't receive a response, it creates a [504 Gateway Timeout](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504) status to return. The `504` status in this example could be returned because the server is offline or the client is disconnected.
|
||||
|
||||
## Debugging the Angular service worker
|
||||
|
||||
Occasionally, it might be necessary to examine the Angular service worker in a running state to investigate issues or to ensure that it is operating as designed.
|
||||
Occasionally, it might be necessary to examine the Angular service worker in a running state to investigate issues or whether it's operating as designed.
|
||||
Browsers provide built-in tools for debugging service workers and the Angular service worker itself includes useful debugging features.
|
||||
|
||||
### Locating and analyzing debugging information
|
||||
@@ -187,7 +197,7 @@ There are two possible degraded states:
|
||||
|
||||
| Degraded states | Details |
|
||||
|:--- |:--- |
|
||||
| `EXISTING_CLIENTS_ONLY` | The service worker does not have a clean copy of the latest known version of the application. Older cached versions are safe to use, so existing tabs continue to run from cache, but new loads of the application will be served from the network. The service worker will try to recover from this state when a new version of the application is detected and installed \(that is, when a new `ngsw.json` is available\). |
|
||||
| `EXISTING_CLIENTS_ONLY` | The service worker does not have a clean copy of the latest known version of the application. Older cached versions are safe to use, so existing tabs continue to run from cache, but new loads of the application will be served from the network. The service worker will try to recover from this state when a new version of the application is detected and installed. This happens when a new `ngsw.json` is available. |
|
||||
| `SAFE_MODE` | The service worker cannot guarantee the safety of using cached data. Either an unexpected error occurred or all cached versions are invalid. All traffic will be served from the network, running as little service worker code as possible. |
|
||||
|
||||
In both cases, the parenthetical annotation provides the
|
||||
@@ -269,9 +279,9 @@ Debug log:
|
||||
|
||||
</code-example>
|
||||
|
||||
Errors that occur within the service worker will be logged here.
|
||||
Errors that occur within the service worker are logged here.
|
||||
|
||||
### Developer Tools
|
||||
### Developer tools
|
||||
|
||||
Browsers such as Chrome provide developer tools for interacting with service workers.
|
||||
Such tools can be powerful when used properly, but there are a few things to keep in mind.
|
||||
@@ -282,23 +292,28 @@ Such tools can be powerful when used properly, but there are a few things to kee
|
||||
* If you look in the Cache Storage viewer, the cache is frequently out of date.
|
||||
Right click the Cache Storage title and refresh the caches.
|
||||
|
||||
* Stopping and starting the service worker in the Service Worker pane triggers a check for updates
|
||||
* Stopping and starting the service worker in the Service Worker pane checks for updates
|
||||
|
||||
## Service Worker Safety
|
||||
## Service worker safety
|
||||
|
||||
Like any complex system, bugs or broken configurations can cause the Angular service worker to act in unforeseen ways.
|
||||
While its design attempts to minimize the impact of such problems, the Angular service worker contains several failsafe mechanisms in case an administrator ever needs to deactivate the service worker quickly.
|
||||
Bugs or broken configurations could cause the Angular service worker to act in unexpected ways.
|
||||
If this happens, the Angular service worker contains several failsafe mechanisms in case an administrator needs to deactivate the service worker quickly.
|
||||
|
||||
### Fail-safe
|
||||
|
||||
To deactivate the service worker, remove or rename the `ngsw.json` file.
|
||||
When the service worker's request for `ngsw.json` returns a `404`, then the service worker removes all of its caches and de-registers itself, essentially self-destructing.
|
||||
To deactivate the service worker, rename the `ngsw.json` file or delete it.
|
||||
When the service worker's request for `ngsw.json` returns a `404`, then the service worker removes all its caches and de-registers itself, essentially self-destructing.
|
||||
|
||||
### Safety Worker
|
||||
### Safety worker
|
||||
|
||||
Also included in the `@angular/service-worker` NPM package is a small script `safety-worker.js`, which when loaded will unregister itself from the browser and remove the service worker caches.
|
||||
<!-- vale Angular.Google_Acronyms = NO -->
|
||||
|
||||
A small script, `safety-worker.js`, is also included in the `@angular/service-worker` NPM package.
|
||||
When loaded, it un-registers itself from the browser and removes the service worker caches.
|
||||
This script can be used as a last resort to get rid of unwanted service workers already installed on client pages.
|
||||
|
||||
<!-- vale Angular.Google_Acronyms = YES -->
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
**IMPORTANT**: <br />
|
||||
@@ -306,11 +321,11 @@ You cannot register this worker directly, as old clients with cached state might
|
||||
|
||||
</div>
|
||||
|
||||
Instead, you must serve the contents of `safety-worker.js` at the URL of the Service Worker script you are trying to unregister, and must continue to do so until you are certain all users have successfully unregistered the old worker.
|
||||
Instead, you must serve the contents of `safety-worker.js` at the URL of the Service Worker script you are trying to unregister. You must continue to do so until you are certain all users have successfully unregistered the old worker.
|
||||
For most sites, this means that you should serve the safety worker at the old Service Worker URL forever.
|
||||
This script can be used both to deactivate `@angular/service-worker` \(and remove the corresponding caches\) as well as any other Service Workers which might have been served in the past on your site.
|
||||
This script can be used to deactivate `@angular/service-worker` and remove the corresponding caches. It also removes any other Service Workers which might have been served in the past on your site.
|
||||
|
||||
### Changing your app's location
|
||||
### Changing your application's location
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
@@ -321,11 +336,11 @@ You might have already encountered the error `The script resource is behind a re
|
||||
</div>
|
||||
|
||||
This can be a problem if you have to change your application's location.
|
||||
If you setup a redirect from the old location \(for example `example.com`\) to the new location \(for example `www.example.com`\) the worker will stop working.
|
||||
If you setup a redirect from the old location, such as `example.com`, to the new location, `www.example.com` in this example, the worker stops working.
|
||||
Also, the redirect won't even trigger for users who are loading the site entirely from Service Worker.
|
||||
The old worker \(registered at `example.com`\) tries to update and sends requests to the old location `example.com` which get redirected to the new location `www.example.com` and create the error `The script resource is behind a redirect, which is disallowed`.
|
||||
The old worker, which was registered at `example.com`, tries to update and sends a request to the old location `example.com`. This request is redirected to the new location `www.example.com` and creates the error: `The script resource is behind a redirect, which is disallowed`.
|
||||
|
||||
To remedy this, you might need to deactivate the old worker using one of the above techniques \([Fail-safe](#fail-safe) or [Safety Worker](#safety-worker)\).
|
||||
To remedy this, you might need to deactivate the old worker using one of the preceding techniques: [Fail-safe](#fail-safe) or [Safety Worker](#safety-worker).
|
||||
|
||||
## More on Angular service workers
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@ Debug specs in the browser in the same way that you debug an application.
|
||||
1. Reveal the Karma browser window.
|
||||
See [Set up testing](guide/testing#set-up-testing) if you need help with this step.
|
||||
|
||||
1. Click the **DEBUG** button; it opens a new browser tab and re-runs the tests.
|
||||
1. Open the browser's "Developer Tools" \(`Ctrl-Shift-I` on Windows; `Command-Option-I` in macOS\).
|
||||
1. Pick the "sources" section.
|
||||
1. Open the `1st.spec.ts` test file \(Control/Command-P, then start typing the name of the file\).
|
||||
1. Set a breakpoint in the test.
|
||||
1. Refresh the browser, and it stops at the breakpoint.
|
||||
1. Click the **DEBUG** button to open a new browser tab and re-run the tests.
|
||||
2. Open the browser's **Developer Tools**. On Windows, press `Ctrl-Shift-I`. On macOS, press `Command-Option-I`.
|
||||
3. Pick the **Sources** section.
|
||||
4. Press `Control/Command-P`, and then start typing the name of your test file to open it.
|
||||
5. Set a breakpoint in the test.
|
||||
6. Refresh the browser, and notice how it stops at the breakpoint.
|
||||
|
||||
<div class="lightbox">
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ To adequately test a component, you should test that they work together as inten
|
||||
Such tests require creating the component's host element in the browser DOM, as Angular does, and investigating the component class's interaction with the DOM as described by its template.
|
||||
|
||||
The Angular `TestBed` facilitates this kind of testing as you'll see in the following sections.
|
||||
But in many cases, *testing the component class alone*, without DOM involvement, can validate much of the component's behavior in an straightforward, more obvious way.
|
||||
But in many cases, *testing the component class alone*, without DOM involvement, can validate much of the component's behavior in a straightforward, more obvious way.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
{"type": 301, "source": "/getting-started", "destination": "/start"},
|
||||
{"type": 301, "source": "/getting-started/:rest*", "destination": "/start/:rest*"},
|
||||
{"type": 301, "source": "/guide/displaying-data", "destination": "/start#template-syntax"},
|
||||
{"type": 301, "source": "/guide/ivy", "destination": "https://v12.angular.io/guide/ivy"},
|
||||
{"type": 301, "source": "/guide/updating-to-version-10", "destination": "https://v10.angular.io/guide/updating-to-version-10"},
|
||||
{"type": 301, "source": "/guide/updating-to-version-11", "destination": "https://v11.angular.io/guide/updating-to-version-11"},
|
||||
{"type": 301, "source": "/guide/updating-to-version-12", "destination": "/guide/update-to-latest-version"},
|
||||
|
||||
+3
-3
@@ -105,7 +105,7 @@
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/architect-cli": "0.1401.0-next.4",
|
||||
"@angular-devkit/architect-cli": "0.1401.0-rc.3",
|
||||
"@angular-devkit/build-angular": "^14.1.0-next",
|
||||
"@angular-eslint/builder": "^14.0.0",
|
||||
"@angular-eslint/eslint-plugin": "^14.0.0",
|
||||
@@ -119,8 +119,8 @@
|
||||
"@types/node": "^12.7.9",
|
||||
"@types/trusted-types": "^2.0.2",
|
||||
"@types/xregexp": "^4.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.30.6",
|
||||
"@typescript-eslint/parser": "5.30.6",
|
||||
"@typescript-eslint/eslint-plugin": "5.30.7",
|
||||
"@typescript-eslint/parser": "5.30.7",
|
||||
"@yarnpkg/lockfile": "^1.1.0",
|
||||
"archiver": "^5.3.0",
|
||||
"assert": "^2.0.0",
|
||||
|
||||
@@ -205,6 +205,7 @@
|
||||
/guide/displaying-data --> /start#template-syntax
|
||||
/guide/i18n --> /guide/i18n-overview
|
||||
/guide --> /docs
|
||||
/guide/ivy --> https://v12.angular.io/guide/ivy
|
||||
/guide/learning-angular --> /start
|
||||
/guide/learning-angular.html --> /start
|
||||
/guide/metadata --> /guide/aot-compiler
|
||||
|
||||
+72
-53
@@ -10,13 +10,13 @@
|
||||
"@jridgewell/gen-mapping" "^0.1.0"
|
||||
"@jridgewell/trace-mapping" "^0.3.9"
|
||||
|
||||
"@angular-devkit/architect-cli@0.1401.0-next.4":
|
||||
version "0.1401.0-next.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/architect-cli/-/architect-cli-0.1401.0-next.4.tgz#063ea04a6f33d111e9655f4abf42b2079f08f113"
|
||||
integrity sha512-X5kCUMbOQ5JxDv9RuyLBf6hhJpNReYjJRBg1Em4Gn3bYDSmZ8rEb5JO7NaE/21gF3b42uonC85NggGOybAN0Ow==
|
||||
"@angular-devkit/architect-cli@0.1401.0-rc.3":
|
||||
version "0.1401.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/architect-cli/-/architect-cli-0.1401.0-rc.3.tgz#6b8a53ff806aa893b54ddc7f06d92ec4bcfa5625"
|
||||
integrity sha512-ucxGv2TPx1ZxQyJg22wJbksqYmByX73oQE2wg536vguSBVm8ftJ0g/TmRF4m9eyE3AUneGK+6WC8mZwBuZs21Q==
|
||||
dependencies:
|
||||
"@angular-devkit/architect" "0.1401.0-next.4"
|
||||
"@angular-devkit/core" "14.1.0-next.4"
|
||||
"@angular-devkit/architect" "0.1401.0-rc.3"
|
||||
"@angular-devkit/core" "14.1.0-rc.3"
|
||||
ansi-colors "4.1.3"
|
||||
progress "2.0.3"
|
||||
rxjs "6.6.7"
|
||||
@@ -39,6 +39,14 @@
|
||||
"@angular-devkit/core" "14.1.0-next.4"
|
||||
rxjs "6.6.7"
|
||||
|
||||
"@angular-devkit/architect@0.1401.0-rc.3":
|
||||
version "0.1401.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1401.0-rc.3.tgz#55444857b8c041bfcfa57825047933588e917374"
|
||||
integrity sha512-ODAKO/cQNCpcTL+TIDxU6lPJB6kYT7raMg68JGc0bCiV82BEBZPE94xAIs/NA3B18WWEB6AxX6QgSVp1r963bg==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "14.1.0-rc.3"
|
||||
rxjs "6.6.7"
|
||||
|
||||
"@angular-devkit/build-angular@^14.1.0-next":
|
||||
version "14.1.0-next.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-14.1.0-next.4.tgz#2c8ac035d757fc44af73943814bc05c7898a31be"
|
||||
@@ -139,6 +147,17 @@
|
||||
rxjs "6.6.7"
|
||||
source-map "0.7.4"
|
||||
|
||||
"@angular-devkit/core@14.1.0-rc.3":
|
||||
version "14.1.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-14.1.0-rc.3.tgz#b9d69c5216e7b7ac3f035a93b341d3480ff71e99"
|
||||
integrity sha512-KTrKoRJ12kuO1MVRJamaNc2v8avq0/roZ0dVlazXASs+BWlG/6x8fpBqF5ZodTF8M+rfYLkOSpJFlkWOBWkkyA==
|
||||
dependencies:
|
||||
ajv "8.11.0"
|
||||
ajv-formats "2.1.1"
|
||||
jsonc-parser "3.0.0"
|
||||
rxjs "6.6.7"
|
||||
source-map "0.7.4"
|
||||
|
||||
"@angular-devkit/schematics@14.0.5":
|
||||
version "14.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-14.0.5.tgz#01777d2ad473d35bdfdbbb751521c43421ad9772"
|
||||
@@ -2156,14 +2175,14 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@5.30.6":
|
||||
version "5.30.6"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.6.tgz#9c6017b6c1d04894141b4a87816388967f64c359"
|
||||
integrity sha512-J4zYMIhgrx4MgnZrSDD7sEnQp7FmhKNOaqaOpaoQ/SfdMfRB/0yvK74hTnvH+VQxndZynqs5/Hn4t+2/j9bADg==
|
||||
"@typescript-eslint/eslint-plugin@5.30.7":
|
||||
version "5.30.7"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.7.tgz#1621dabc1ae4084310e19e9efc80dfdbb97e7493"
|
||||
integrity sha512-l4L6Do+tfeM2OK0GJsU7TUcM/1oN/N25xHm3Jb4z3OiDU4Lj8dIuxX9LpVMS9riSXQs42D1ieX7b85/r16H9Fw==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "5.30.6"
|
||||
"@typescript-eslint/type-utils" "5.30.6"
|
||||
"@typescript-eslint/utils" "5.30.6"
|
||||
"@typescript-eslint/scope-manager" "5.30.7"
|
||||
"@typescript-eslint/type-utils" "5.30.7"
|
||||
"@typescript-eslint/utils" "5.30.7"
|
||||
debug "^4.3.4"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
ignore "^5.2.0"
|
||||
@@ -2171,14 +2190,14 @@
|
||||
semver "^7.3.7"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/parser@5.30.6":
|
||||
version "5.30.6"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.30.6.tgz#add440db038fa9d777e4ebdaf66da9e7fb7abe92"
|
||||
integrity sha512-gfF9lZjT0p2ZSdxO70Xbw8w9sPPJGfAdjK7WikEjB3fcUI/yr9maUVEdqigBjKincUYNKOmf7QBMiTf719kbrA==
|
||||
"@typescript-eslint/parser@5.30.7":
|
||||
version "5.30.7"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.30.7.tgz#99d09729392aec9e64b1de45cd63cb81a4ddd980"
|
||||
integrity sha512-Rg5xwznHWWSy7v2o0cdho6n+xLhK2gntImp0rJroVVFkcYFYQ8C8UJTSuTw/3CnExBmPjycjmUJkxVmjXsld6A==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "5.30.6"
|
||||
"@typescript-eslint/types" "5.30.6"
|
||||
"@typescript-eslint/typescript-estree" "5.30.6"
|
||||
"@typescript-eslint/scope-manager" "5.30.7"
|
||||
"@typescript-eslint/types" "5.30.7"
|
||||
"@typescript-eslint/typescript-estree" "5.30.7"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/scope-manager@5.29.0":
|
||||
@@ -2189,20 +2208,20 @@
|
||||
"@typescript-eslint/types" "5.29.0"
|
||||
"@typescript-eslint/visitor-keys" "5.29.0"
|
||||
|
||||
"@typescript-eslint/scope-manager@5.30.6":
|
||||
version "5.30.6"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.30.6.tgz#ce1b49ff5ce47f55518d63dbe8fc9181ddbd1a33"
|
||||
integrity sha512-Hkq5PhLgtVoW1obkqYH0i4iELctEKixkhWLPTYs55doGUKCASvkjOXOd/pisVeLdO24ZX9D6yymJ/twqpJiG3g==
|
||||
"@typescript-eslint/scope-manager@5.30.7":
|
||||
version "5.30.7"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.30.7.tgz#8269a931ef1e5ae68b5eb80743cc515c4ffe3dd7"
|
||||
integrity sha512-7BM1bwvdF1UUvt+b9smhqdc/eniOnCKxQT/kj3oXtj3LqnTWCAM0qHRHfyzCzhEfWX0zrW7KqXXeE4DlchZBKw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.30.6"
|
||||
"@typescript-eslint/visitor-keys" "5.30.6"
|
||||
"@typescript-eslint/types" "5.30.7"
|
||||
"@typescript-eslint/visitor-keys" "5.30.7"
|
||||
|
||||
"@typescript-eslint/type-utils@5.30.6":
|
||||
version "5.30.6"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.30.6.tgz#a64aa9acbe609ab77f09f53434a6af2b9685f3af"
|
||||
integrity sha512-GFVVzs2j0QPpM+NTDMXtNmJKlF842lkZKDSanIxf+ArJsGeZUIaeT4jGg+gAgHt7AcQSFwW7htzF/rbAh2jaVA==
|
||||
"@typescript-eslint/type-utils@5.30.7":
|
||||
version "5.30.7"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.30.7.tgz#5693dc3db6f313f302764282d614cfdbc8a9fcfd"
|
||||
integrity sha512-nD5qAE2aJX/YLyKMvOU5jvJyku4QN5XBVsoTynFrjQZaDgDV6i7QHFiYCx10wvn7hFvfuqIRNBtsgaLe0DbWhw==
|
||||
dependencies:
|
||||
"@typescript-eslint/utils" "5.30.6"
|
||||
"@typescript-eslint/utils" "5.30.7"
|
||||
debug "^4.3.4"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
@@ -2211,10 +2230,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.29.0.tgz#7861d3d288c031703b2d97bc113696b4d8c19aab"
|
||||
integrity sha512-X99VbqvAXOMdVyfFmksMy3u8p8yoRGITgU1joBJPzeYa0rhdf5ok9S56/itRoUSh99fiDoMtarSIJXo7H/SnOg==
|
||||
|
||||
"@typescript-eslint/types@5.30.6":
|
||||
version "5.30.6"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.30.6.tgz#86369d0a7af8c67024115ac1da3e8fb2d38907e1"
|
||||
integrity sha512-HdnP8HioL1F7CwVmT4RaaMX57RrfqsOMclZc08wGMiDYJBsLGBM7JwXM4cZJmbWLzIR/pXg1kkrBBVpxTOwfUg==
|
||||
"@typescript-eslint/types@5.30.7":
|
||||
version "5.30.7"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.30.7.tgz#18331487cc92d0f1fb1a6f580c8ec832528079d0"
|
||||
integrity sha512-ocVkETUs82+U+HowkovV6uxf1AnVRKCmDRNUBUUo46/5SQv1owC/EBFkiu4MOHeZqhKz2ktZ3kvJJ1uFqQ8QPg==
|
||||
|
||||
"@typescript-eslint/typescript-estree@5.29.0":
|
||||
version "5.29.0"
|
||||
@@ -2229,13 +2248,13 @@
|
||||
semver "^7.3.7"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@5.30.6":
|
||||
version "5.30.6"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.6.tgz#a84a0d6a486f9b54042da1de3d671a2c9f14484e"
|
||||
integrity sha512-Z7TgPoeYUm06smfEfYF0RBkpF8csMyVnqQbLYiGgmUSTaSXTP57bt8f0UFXstbGxKIreTwQCujtaH0LY9w9B+A==
|
||||
"@typescript-eslint/typescript-estree@5.30.7":
|
||||
version "5.30.7"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.7.tgz#05da9f1b281985bfedcf62349847f8d168eecc07"
|
||||
integrity sha512-tNslqXI1ZdmXXrHER83TJ8OTYl4epUzJC0aj2i4DMDT4iU+UqLT3EJeGQvJ17BMbm31x5scSwo3hPM0nqQ1AEA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.30.6"
|
||||
"@typescript-eslint/visitor-keys" "5.30.6"
|
||||
"@typescript-eslint/types" "5.30.7"
|
||||
"@typescript-eslint/visitor-keys" "5.30.7"
|
||||
debug "^4.3.4"
|
||||
globby "^11.1.0"
|
||||
is-glob "^4.0.3"
|
||||
@@ -2254,15 +2273,15 @@
|
||||
eslint-scope "^5.1.1"
|
||||
eslint-utils "^3.0.0"
|
||||
|
||||
"@typescript-eslint/utils@5.30.6":
|
||||
version "5.30.6"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.30.6.tgz#1de2da14f678e7d187daa6f2e4cdb558ed0609dc"
|
||||
integrity sha512-xFBLc/esUbLOJLk9jKv0E9gD/OH966M40aY9jJ8GiqpSkP2xOV908cokJqqhVd85WoIvHVHYXxSFE4cCSDzVvA==
|
||||
"@typescript-eslint/utils@5.30.7":
|
||||
version "5.30.7"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.30.7.tgz#7135be070349e9f7caa262b0ca59dc96123351bb"
|
||||
integrity sha512-Z3pHdbFw+ftZiGUnm1GZhkJgVqsDL5CYW2yj+TB2mfXDFOMqtbzQi2dNJIyPqPbx9mv2kUxS1gU+r2gKlKi1rQ==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.9"
|
||||
"@typescript-eslint/scope-manager" "5.30.6"
|
||||
"@typescript-eslint/types" "5.30.6"
|
||||
"@typescript-eslint/typescript-estree" "5.30.6"
|
||||
"@typescript-eslint/scope-manager" "5.30.7"
|
||||
"@typescript-eslint/types" "5.30.7"
|
||||
"@typescript-eslint/typescript-estree" "5.30.7"
|
||||
eslint-scope "^5.1.1"
|
||||
eslint-utils "^3.0.0"
|
||||
|
||||
@@ -2274,12 +2293,12 @@
|
||||
"@typescript-eslint/types" "5.29.0"
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@5.30.6":
|
||||
version "5.30.6"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.6.tgz#94dd10bb481c8083378d24de1742a14b38a2678c"
|
||||
integrity sha512-41OiCjdL2mCaSDi2SvYbzFLlqqlm5v1ZW9Ym55wXKL/Rx6OOB1IbuFGo71Fj6Xy90gJDFTlgOS+vbmtGHPTQQA==
|
||||
"@typescript-eslint/visitor-keys@5.30.7":
|
||||
version "5.30.7"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.7.tgz#c093abae75b4fd822bfbad9fc337f38a7a14909a"
|
||||
integrity sha512-KrRXf8nnjvcpxDFOKej4xkD7657+PClJs5cJVSG7NNoCNnjEdc46juNAQt7AyuWctuCgs6mVRc1xGctEqrjxWw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.30.6"
|
||||
"@typescript-eslint/types" "5.30.7"
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
"@webassemblyjs/ast@1.11.1":
|
||||
|
||||
@@ -47,6 +47,8 @@ export const enum RuntimeErrorCode {
|
||||
// (undocumented)
|
||||
INVALID_INJECTION_TOKEN = 204,
|
||||
// (undocumented)
|
||||
INVALID_MULTI_PROVIDER = 209,
|
||||
// (undocumented)
|
||||
MISSING_GENERATED_DEF = 906,
|
||||
// (undocumented)
|
||||
MISSING_INJECTION_CONTEXT = -203,
|
||||
|
||||
@@ -233,6 +233,22 @@ export abstract class ComponentFactoryResolver {
|
||||
abstract resolveComponentFactory<T>(component: Type<T>): ComponentFactory<T>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface ComponentMirror<C> {
|
||||
get inputs(): ReadonlyArray<{
|
||||
readonly propName: string;
|
||||
readonly templateName: string;
|
||||
}>;
|
||||
get isStandalone(): boolean;
|
||||
get ngContentSelectors(): ReadonlyArray<string>;
|
||||
get outputs(): ReadonlyArray<{
|
||||
readonly propName: string;
|
||||
readonly templateName: string;
|
||||
}>;
|
||||
get selector(): string;
|
||||
get type(): Type<C>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export abstract class ComponentRef<C> {
|
||||
abstract get changeDetectorRef(): ChangeDetectorRef;
|
||||
@@ -299,6 +315,14 @@ export interface ContentChildrenDecorator {
|
||||
}): Query;
|
||||
}
|
||||
|
||||
// @public
|
||||
export function createComponent<C>(component: Type<C>, options: {
|
||||
environmentInjector: EnvironmentInjector;
|
||||
hostElement?: Element;
|
||||
elementInjector?: Injector;
|
||||
projectableNodes?: Node[][];
|
||||
}): ComponentRef<C>;
|
||||
|
||||
// @public
|
||||
export function createEnvironmentInjector(providers: Array<Provider | ImportedNgModuleProviders>, parent: EnvironmentInjector, debugName?: string | null): EnvironmentInjector;
|
||||
|
||||
@@ -1106,6 +1130,9 @@ export class QueryList<T> implements Iterable<T> {
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export function reflectComponentType<C>(component: Type<C>): ComponentMirror<C> | null;
|
||||
|
||||
// @public @deprecated
|
||||
export abstract class ReflectiveInjector implements Injector {
|
||||
abstract createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector;
|
||||
|
||||
@@ -874,6 +874,10 @@ export abstract class TitleStrategy {
|
||||
buildTitle(snapshot: RouterStateSnapshot): string | undefined;
|
||||
getResolvedTitleForRoute(snapshot: ActivatedRouteSnapshot): any;
|
||||
abstract updateTitle(snapshot: RouterStateSnapshot): void;
|
||||
// (undocumented)
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<TitleStrategy, never>;
|
||||
// (undocumented)
|
||||
static ɵprov: i0.ɵɵInjectableDeclaration<TitleStrategy>;
|
||||
}
|
||||
|
||||
// @public
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
import { ChildrenOutletContexts } from '@angular/router';
|
||||
import { Compiler } from '@angular/core';
|
||||
import { DefaultTitleStrategy } from '@angular/router';
|
||||
import { ExtraOptions } from '@angular/router';
|
||||
import * as i0 from '@angular/core';
|
||||
import * as i1 from '@angular/router';
|
||||
@@ -37,7 +36,7 @@ export class RouterTestingModule {
|
||||
export function setupTestingRouter(urlSerializer: UrlSerializer, contexts: ChildrenOutletContexts, location: Location_2, compiler: Compiler, injector: Injector, routes: Route[][], opts?: ExtraOptions | UrlHandlingStrategy | null, urlHandlingStrategy?: UrlHandlingStrategy, routeReuseStrategy?: RouteReuseStrategy, titleStrategy?: TitleStrategy): Router;
|
||||
|
||||
// @public
|
||||
export function setupTestingRouterInternal(urlSerializer: UrlSerializer, contexts: ChildrenOutletContexts, location: Location_2, compiler: Compiler, injector: Injector, routes: Route[][], opts?: ExtraOptions | UrlHandlingStrategy, urlHandlingStrategy?: UrlHandlingStrategy, routeReuseStrategy?: RouteReuseStrategy, defaultTitleStrategy?: DefaultTitleStrategy, titleStrategy?: TitleStrategy): Router;
|
||||
export function setupTestingRouterInternal(urlSerializer: UrlSerializer, contexts: ChildrenOutletContexts, location: Location_2, compiler: Compiler, injector: Injector, routes: Route[][], titleStrategy: TitleStrategy, opts?: ExtraOptions | UrlHandlingStrategy, urlHandlingStrategy?: UrlHandlingStrategy, routeReuseStrategy?: RouteReuseStrategy): Router;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
load("//tools:defaults.bzl", "protractor_web_test_suite", "ts_library")
|
||||
|
||||
def example_test(name, srcs, server, data = [], deps = [], **kwargs):
|
||||
def example_test(name, srcs, server, data = [], deps = [], use_legacy_webdriver_types = True, **kwargs):
|
||||
ts_deps = [
|
||||
"@npm//@angular/dev-infra-private/bazel/benchmark/driver-utilities",
|
||||
"//packages/private/testing",
|
||||
"@npm//@types/selenium-webdriver",
|
||||
"@npm//protractor",
|
||||
] + deps
|
||||
|
||||
# Reliance on the Control Flow in Selenium Webdriver is not recommended long-term,
|
||||
# especially with the deprecation of Protractor. New tests should not use the legacy
|
||||
# webdriver types but rather use the actual `@types/jasmine` types.
|
||||
if use_legacy_webdriver_types:
|
||||
ts_deps.append("@npm//@types/jasminewd2")
|
||||
|
||||
ts_library(
|
||||
name = "%s_lib" % name,
|
||||
testonly = True,
|
||||
srcs = srcs,
|
||||
tsconfig = "//modules/playground:tsconfig-e2e.json",
|
||||
deps = [
|
||||
"@npm//@angular/dev-infra-private/bazel/benchmark/driver-utilities",
|
||||
"//packages/private/testing",
|
||||
"@npm//@types/jasminewd2",
|
||||
"@npm//@types/selenium-webdriver",
|
||||
"@npm//protractor",
|
||||
] + deps,
|
||||
deps = ts_deps,
|
||||
)
|
||||
|
||||
protractor_web_test_suite(
|
||||
|
||||
@@ -8,6 +8,7 @@ example_test(
|
||||
"//modules/playground/src/sourcemap:index.ts",
|
||||
],
|
||||
server = "//modules/playground/src/sourcemap:devserver",
|
||||
use_legacy_webdriver_types = False,
|
||||
deps = [
|
||||
"@npm//source-map",
|
||||
],
|
||||
|
||||
@@ -14,40 +14,38 @@ import {RawSourceMap, SourceMapConsumer} from 'source-map';
|
||||
describe('sourcemaps', function() {
|
||||
const URL = '/';
|
||||
|
||||
it('should map sources', function() {
|
||||
browser.get(URL);
|
||||
it('should map sources', async function() {
|
||||
await browser.get(URL);
|
||||
|
||||
$('error-app .errorButton').click();
|
||||
await $('error-app .errorButton').click();
|
||||
|
||||
browser.manage().logs().get(logging.Type.BROWSER).then(function(logs: any) {
|
||||
let errorLine: number = null;
|
||||
let errorColumn: number = null;
|
||||
logs.forEach(function(log: any) {
|
||||
const match = log.message.match(/\.createError\s+\(.+:(\d+):(\d+)/m);
|
||||
if (match) {
|
||||
errorLine = parseInt(match[1]);
|
||||
errorColumn = parseInt(match[2]);
|
||||
}
|
||||
});
|
||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
||||
|
||||
expect(errorLine).not.toBeNull();
|
||||
expect(errorColumn).not.toBeNull();
|
||||
|
||||
|
||||
const content =
|
||||
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 SourceMapConsumer(JSON.parse(sourceMapData) as RawSourceMap);
|
||||
const originalPosition = decoder.originalPositionFor({line: errorLine, column: errorColumn});
|
||||
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\'\)/);
|
||||
let errorLine: number|null = null;
|
||||
let errorColumn: number|null = null;
|
||||
logs.forEach(function(log: any) {
|
||||
const match = log.message.match(/\.createError\s+\(.+:(\d+):(\d+)/m);
|
||||
if (match) {
|
||||
errorLine = parseInt(match[1]);
|
||||
errorColumn = parseInt(match[2]);
|
||||
}
|
||||
});
|
||||
|
||||
expect(errorLine).not.toBeNull();
|
||||
expect(errorColumn).not.toBeNull();
|
||||
|
||||
const content = 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 = await new SourceMapConsumer(JSON.parse(sourceMapData) as RawSourceMap);
|
||||
const originalPosition = decoder.originalPositionFor({line: errorLine!, column: errorColumn!});
|
||||
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\'\)/);
|
||||
});
|
||||
});
|
||||
|
||||
+18
-18
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "14.1.0-next.4",
|
||||
"version": "14.1.0",
|
||||
"private": true,
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
"homepage": "https://github.com/angular/angular",
|
||||
@@ -45,29 +45,29 @@
|
||||
},
|
||||
"// 1": "dependencies are used locally and by bazel",
|
||||
"dependencies": {
|
||||
"@angular-devkit/build-angular": "14.1.0-next.4",
|
||||
"@angular-devkit/build-angular": "14.1.0-rc.3",
|
||||
"@angular-devkit/build-optimizer": "0.1302.0-rc.1",
|
||||
"@angular-devkit/core": "14.1.0-next.4",
|
||||
"@angular-devkit/schematics": "14.1.0-next.4",
|
||||
"@angular-devkit/core": "14.1.0-rc.3",
|
||||
"@angular-devkit/schematics": "14.1.0-rc.3",
|
||||
"@angular/animations-12": "npm:@angular/animations@12.2.13",
|
||||
"@angular/cdk": "14.1.0-next.3",
|
||||
"@angular/cli": "14.1.0-next.4",
|
||||
"@angular/cdk": "14.1.0-rc.0",
|
||||
"@angular/cli": "14.1.0-rc.3",
|
||||
"@angular/common-12": "npm:@angular/common@12.2.13",
|
||||
"@angular/core-12": "npm:@angular/core@12.2.13",
|
||||
"@angular/forms-12": "npm:@angular/forms@12.2.13",
|
||||
"@angular/material": "14.1.0-next.3",
|
||||
"@angular/material": "14.1.0-rc.0",
|
||||
"@angular/platform-browser-12": "npm:@angular/platform-browser@12.2.13",
|
||||
"@angular/platform-browser-dynamic-12": "npm:@angular/platform-browser-dynamic@12.2.13",
|
||||
"@angular/platform-server-12": "npm:@angular/platform-server@12.2.13",
|
||||
"@angular/router-12": "npm:@angular/router@12.2.13",
|
||||
"@babel/cli": "7.18.6",
|
||||
"@babel/core": "7.18.6",
|
||||
"@babel/generator": "7.18.7",
|
||||
"@babel/parser": "7.18.8",
|
||||
"@babel/preset-env": "7.18.6",
|
||||
"@babel/cli": "7.18.9",
|
||||
"@babel/core": "7.18.9",
|
||||
"@babel/generator": "7.18.9",
|
||||
"@babel/parser": "7.18.9",
|
||||
"@babel/preset-env": "7.18.9",
|
||||
"@babel/template": "7.18.6",
|
||||
"@babel/traverse": "7.18.8",
|
||||
"@babel/types": "7.18.8",
|
||||
"@babel/traverse": "7.18.9",
|
||||
"@babel/types": "7.18.9",
|
||||
"@bazel/concatjs": "5.5.2",
|
||||
"@bazel/esbuild": "5.5.2",
|
||||
"@bazel/jasmine": "5.5.2",
|
||||
@@ -80,7 +80,7 @@
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-commonjs": "^22.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.0.4",
|
||||
"@schematics/angular": "14.1.0-next.4",
|
||||
"@schematics/angular": "14.1.0-rc.3",
|
||||
"@types/angular": "^1.6.47",
|
||||
"@types/babel__core": "7.1.19",
|
||||
"@types/babel__generator": "7.6.4",
|
||||
@@ -141,7 +141,7 @@
|
||||
"karma": "~6.3.0",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-firefox-launcher": "^2.1.0",
|
||||
"karma-jasmine": "^4.0.1",
|
||||
"karma-jasmine": "^5.0.0",
|
||||
"karma-requirejs": "^1.1.0",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"magic-string": "0.26.2",
|
||||
@@ -160,7 +160,7 @@
|
||||
"selenium-webdriver": "3.5.0",
|
||||
"semver-dsl": "^1.0.1",
|
||||
"shelljs": "^0.8.5",
|
||||
"source-map": "^0.6.1",
|
||||
"source-map": "0.7.4",
|
||||
"source-map-support": "0.5.21",
|
||||
"sourcemap-codec": "^1.4.8",
|
||||
"start-server-and-test": "^1.10.11",
|
||||
@@ -179,7 +179,7 @@
|
||||
},
|
||||
"// 2": "devDependencies are not used under Bazel. Many can be removed after test.sh is deleted.",
|
||||
"devDependencies": {
|
||||
"@angular/dev-infra-private": "https://github.com/angular/dev-infra-private-builds.git#294d737341a13e130fb7184268c0653c335ef388",
|
||||
"@angular/dev-infra-private": "https://github.com/angular/dev-infra-private-builds.git#cdaaa155363dfe5a67493d71803a4b721f01cbad",
|
||||
"@bazel/bazelisk": "^1.7.5",
|
||||
"@bazel/buildifier": "^5.0.0",
|
||||
"@bazel/ibazel": "^0.16.0",
|
||||
|
||||
@@ -730,7 +730,7 @@ export class TimelineBuilder {
|
||||
const val = interpolateParams(value, params, errors);
|
||||
this._pendingStyles.set(prop, val);
|
||||
if (!this._localTimelineStyles.has(prop)) {
|
||||
this._backFill.set(prop, this._globalTimelineStyles.get(prop) || AUTO_STYLE);
|
||||
this._backFill.set(prop, this._globalTimelineStyles.get(prop) ?? AUTO_STYLE);
|
||||
}
|
||||
this._updateStyle(prop, val);
|
||||
}
|
||||
|
||||
@@ -191,9 +191,6 @@ def _ngc_tsconfig(ctx, files, srcs, **kwargs):
|
||||
if not ctx.attr.type_check and ctx.attr.strict_templates:
|
||||
fail("Cannot set type_check = False and strict_templates = True for ng_module()")
|
||||
|
||||
if ctx.attr.experimental_extended_template_diagnostics and not ctx.attr.strict_templates:
|
||||
fail("Cannot set `experimental_extended_template_diagnostics = True` **and** `strict_templates = False` for `ng_module()`")
|
||||
|
||||
angular_compiler_options = {
|
||||
"enableResourceInlining": ctx.attr.inline_resources,
|
||||
"generateCodeForLibraries": False,
|
||||
@@ -503,10 +500,6 @@ NG_MODULE_ATTRIBUTES = {
|
||||
"filter_summaries": attr.bool(default = False),
|
||||
"type_check": attr.bool(default = True),
|
||||
"strict_templates": attr.bool(default = False),
|
||||
"experimental_extended_template_diagnostics": attr.bool(
|
||||
default = False,
|
||||
doc = "Experimental option, not publicly supported.",
|
||||
),
|
||||
"inline_resources": attr.bool(default = True),
|
||||
"no_i18n": attr.bool(default = False),
|
||||
"compiler": attr.label(
|
||||
|
||||
@@ -28,7 +28,6 @@ jasmine_node_test(
|
||||
ng_module(
|
||||
name = "test_module_warnings_lib",
|
||||
srcs = ["test_module_warnings.ts"],
|
||||
experimental_extended_template_diagnostics = True,
|
||||
strict_templates = True,
|
||||
tags = [],
|
||||
deps = ["//packages/core"],
|
||||
|
||||
@@ -49,7 +49,6 @@ esbuild(
|
||||
"dependency-graph",
|
||||
"magic-string",
|
||||
"semver",
|
||||
"source-map",
|
||||
"sourcemap-codec",
|
||||
"tslib",
|
||||
"yargs",
|
||||
|
||||
@@ -52,7 +52,6 @@ const requiredNodeModules = {
|
||||
'buffer-from': resolveNpmTreeArtifact('npm/node_modules/buffer-from'),
|
||||
'reflect-metadata': resolveNpmTreeArtifact('npm/node_modules/reflect-metadata'),
|
||||
'rxjs': resolveNpmTreeArtifact('npm/node_modules/rxjs'),
|
||||
'source-map': resolveNpmTreeArtifact('npm/node_modules/source-map'),
|
||||
'source-map-support': resolveNpmTreeArtifact('npm/node_modules/source-map-support'),
|
||||
'typescript': resolveNpmTreeArtifact('npm/node_modules/typescript'),
|
||||
'zone.js': resolveNpmTreeArtifact('angular/packages/zone.js/npm_package'),
|
||||
|
||||
@@ -36,16 +36,11 @@ export function makeNotStandaloneDiagnostic(
|
||||
if (scope !== null && scope.kind === ComponentScopeKind.NgModule) {
|
||||
// The directive/pipe in question is declared in an NgModule. Check if it's also exported.
|
||||
const isExported = scope.exported.dependencies.some(dep => dep.ref.node === ref.node);
|
||||
if (isExported) {
|
||||
relatedInformation = [makeRelatedInformation(
|
||||
scope.ngModule.name,
|
||||
`It can be imported using its NgModule '${scope.ngModule.name.text}' instead.`)];
|
||||
} else {
|
||||
relatedInformation = [makeRelatedInformation(
|
||||
scope.ngModule.name,
|
||||
`It's declared in the NgModule '${
|
||||
scope.ngModule.name.text}', but is not exported. Consider exporting it.`)];
|
||||
}
|
||||
const relatedInfoMessageText = isExported ?
|
||||
`It can be imported using its '${scope.ngModule.name.text}' NgModule instead.` :
|
||||
`It's declared in the '${scope.ngModule.name.text}' NgModule, but is not exported. ` +
|
||||
'Consider exporting it and importing the NgModule instead.';
|
||||
relatedInformation = [makeRelatedInformation(scope.ngModule.name, relatedInfoMessageText)];
|
||||
} else {
|
||||
// TODO(alxhub): the above case handles directives/pipes in NgModules that are declared in the
|
||||
// current compilation, but not those imported from .d.ts dependencies. We could likely scan the
|
||||
|
||||
+14
-5
@@ -15,14 +15,18 @@ import {NgTemplateDiagnostic} from '../../../api';
|
||||
import {TemplateCheckFactory, TemplateCheckWithVisitor, TemplateContext} from '../../api';
|
||||
|
||||
/**
|
||||
* The list of known control flow directives present in the `CommonModule`.
|
||||
* The list of known control flow directives present in the `CommonModule`,
|
||||
* and their corresponding imports.
|
||||
*
|
||||
* Note: there is no `ngSwitch` here since it's typically used as a regular
|
||||
* binding (e.g. `[ngSwitch]`), however the `ngSwitchCase` and `ngSwitchDefault`
|
||||
* are used as structural directives and a warning would be generated. Once the
|
||||
* `CommonModule` is included, the `ngSwitch` would also be covered.
|
||||
*/
|
||||
const KNOWN_CONTROL_FLOW_DIRECTIVES = new Set(['ngIf', 'ngFor', 'ngSwitchCase', 'ngSwitchDefault']);
|
||||
export const KNOWN_CONTROL_FLOW_DIRECTIVES = new Map([
|
||||
['ngIf', 'NgIf'], ['ngFor', 'NgForOf'], ['ngSwitchCase', 'NgSwitchCase'],
|
||||
['ngSwitchDefault', 'NgSwitchDefault']
|
||||
]);
|
||||
|
||||
/**
|
||||
* Ensures that there are no known control flow directives (such as *ngIf and *ngFor)
|
||||
@@ -64,9 +68,14 @@ class MissingControlFlowDirectiveCheck extends
|
||||
}
|
||||
|
||||
const sourceSpan = controlFlowAttr.keySpan || controlFlowAttr.sourceSpan;
|
||||
const errorMessage = `The \`*${controlFlowAttr.name}\` directive was used in the template, ` +
|
||||
`but the \`CommonModule\` was not imported. Please make sure that the \`CommonModule\` ` +
|
||||
`is included into the \`@Component.imports\` array of this component.`;
|
||||
const correspondingImport = KNOWN_CONTROL_FLOW_DIRECTIVES.get(controlFlowAttr.name);
|
||||
const errorMessage =
|
||||
`The \`*${controlFlowAttr.name}\` directive was used in the template, ` +
|
||||
`but neither the \`${
|
||||
correspondingImport}\` directive nor the \`CommonModule\` was imported. ` +
|
||||
`Please make sure that either the \`${
|
||||
correspondingImport}\` directive or the \`CommonModule\` ` +
|
||||
`is included in the \`@Component.imports\` array of this component.`;
|
||||
const diagnostic = ctx.makeTemplateDiagnostic(sourceSpan, errorMessage);
|
||||
return [diagnostic];
|
||||
}
|
||||
|
||||
+10
-4
@@ -13,14 +13,12 @@ import {absoluteFrom, getSourceFileOrError} from '../../../../../file_system';
|
||||
import {runInEachFileSystem} from '../../../../../file_system/testing';
|
||||
import {getSourceCodeForDiagnostic} from '../../../../../testing';
|
||||
import {getClass, setup} from '../../../../testing';
|
||||
import {factory as missingControlFlowDirectiveCheck} from '../../../checks/missing_control_flow_directive';
|
||||
import {factory as missingControlFlowDirectiveCheck, KNOWN_CONTROL_FLOW_DIRECTIVES} from '../../../checks/missing_control_flow_directive';
|
||||
import {ExtendedTemplateCheckerImpl} from '../../../src/extended_template_checker';
|
||||
|
||||
const KNOWN_CONTROL_FLOW_DIRECTIVES = ['ngIf', 'ngFor', 'ngSwitchCase', 'ngSwitchDefault'];
|
||||
|
||||
runInEachFileSystem(() => {
|
||||
describe('MissingControlFlowDirectiveCheck', () => {
|
||||
KNOWN_CONTROL_FLOW_DIRECTIVES.forEach(directive => {
|
||||
KNOWN_CONTROL_FLOW_DIRECTIVES.forEach((correspondingImport, directive) => {
|
||||
['div', 'ng-template', 'ng-container', 'ng-content'].forEach(element => {
|
||||
it(`should produce a warning when the '${directive}' directive is not imported ` +
|
||||
`(when used on the '${element}' element)`,
|
||||
@@ -47,6 +45,14 @@ runInEachFileSystem(() => {
|
||||
expect(diags.length).toBe(1);
|
||||
expect(diags[0].category).toBe(ts.DiagnosticCategory.Warning);
|
||||
expect(diags[0].code).toBe(ngErrorCode(ErrorCode.MISSING_CONTROL_FLOW_DIRECTIVE));
|
||||
expect(diags[0].messageText)
|
||||
.toBe(
|
||||
`The \`*${directive}\` directive was used in the template, ` +
|
||||
`but neither the \`${
|
||||
correspondingImport}\` directive nor the \`CommonModule\` was imported. ` +
|
||||
`Please make sure that either the \`${
|
||||
correspondingImport}\` directive or the \`CommonModule\` ` +
|
||||
`is included in the \`@Component.imports\` array of this component.`);
|
||||
expect(getSourceCodeForDiagnostic(diags[0])).toBe(directive);
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {MappingItem, RawSourceMap, SourceMapConsumer} from 'source-map';
|
||||
|
||||
import {NgtscTestEnvironment} from './env';
|
||||
|
||||
class TestSourceFile {
|
||||
@@ -63,8 +64,8 @@ export interface SegmentMapping {
|
||||
* @param generatedFileName The name of the generated file to process.
|
||||
* @returns An array of segment mappings for each mapped segment in the given generated file.
|
||||
*/
|
||||
export function getMappedSegments(
|
||||
env: NgtscTestEnvironment, generatedFileName: string): SegmentMapping[] {
|
||||
export async function getMappedSegments(
|
||||
env: NgtscTestEnvironment, generatedFileName: string): Promise<SegmentMapping[]> {
|
||||
const generated = new TestSourceFile(generatedFileName, env.getContents(generatedFileName));
|
||||
const sourceMapFileName = generated.getSourceMapFileName(generated.contents);
|
||||
|
||||
@@ -72,7 +73,7 @@ export function getMappedSegments(
|
||||
const mappings: MappingItem[] = [];
|
||||
|
||||
const mapContents = env.getContents(sourceMapFileName);
|
||||
const sourceMapConsumer = new SourceMapConsumer(JSON.parse(mapContents) as RawSourceMap);
|
||||
const sourceMapConsumer = await 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)));
|
||||
|
||||
@@ -381,7 +381,7 @@ runInEachFileSystem(() => {
|
||||
expect(diags[0].relatedInformation).not.toBeUndefined();
|
||||
expect(getSourceCodeForDiagnostic(diags[0].relatedInformation![0])).toEqual('TestModule');
|
||||
expect(diags[0].relatedInformation![0].messageText)
|
||||
.toContain('It can be imported using its NgModule');
|
||||
.toMatch(/It can be imported using its '.*' NgModule instead./);
|
||||
});
|
||||
|
||||
it('should error when a standalone component imports a ModuleWithProviders using a foreign function',
|
||||
@@ -480,14 +480,12 @@ runInEachFileSystem(() => {
|
||||
expect(diags[0].code).toBe(ngErrorCode(ErrorCode.COMPONENT_IMPORT_NOT_STANDALONE));
|
||||
expect(getSourceCodeForDiagnostic(diags[0])).toEqual('TestDir');
|
||||
|
||||
// The diagnostic produced here should suggest that the directive be imported via its
|
||||
// NgModule instead.
|
||||
expect(diags[0].relatedInformation).not.toBeUndefined();
|
||||
expect(getSourceCodeForDiagnostic(diags[0].relatedInformation![0]))
|
||||
.toEqual('TestModule');
|
||||
expect(diags[0].relatedInformation![0].messageText)
|
||||
.toEqual(
|
||||
`It's declared in the NgModule 'TestModule', but is not exported. Consider exporting it.`);
|
||||
`It's declared in the 'TestModule' NgModule, but is not exported. Consider exporting it and importing the NgModule instead.`);
|
||||
});
|
||||
|
||||
it('should type-check standalone component templates', () => {
|
||||
|
||||
@@ -28,8 +28,8 @@ runInEachFileSystem((os) => {
|
||||
|
||||
describe('Inline templates', () => {
|
||||
describe('(element creation)', () => {
|
||||
it('should map simple element with content', () => {
|
||||
const mappings = compileAndMap('<h1>Heading 1</h1>');
|
||||
it('should map simple element with content', async () => {
|
||||
const mappings = await compileAndMap('<h1>Heading 1</h1>');
|
||||
expectMapping(
|
||||
mappings,
|
||||
{source: '<h1>', generated: 'i0.ɵɵelementStart(0, "h1")', sourceUrl: '../test.ts'});
|
||||
@@ -42,8 +42,8 @@ runInEachFileSystem((os) => {
|
||||
mappings, {source: '</h1>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
|
||||
});
|
||||
|
||||
it('should map void element', () => {
|
||||
const mappings = compileAndMap('<hr>');
|
||||
it('should map void element', async () => {
|
||||
const mappings = await compileAndMap('<hr>');
|
||||
expectMapping(
|
||||
mappings,
|
||||
{source: '<hr>', generated: 'i0.ɵɵelement(0, "hr")', sourceUrl: '../test.ts'});
|
||||
@@ -51,8 +51,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
|
||||
describe('(interpolations)', () => {
|
||||
it('should map a mix of interpolated and static content', () => {
|
||||
const mappings = compileAndMap('<h3>Hello {{ name }}</h3>');
|
||||
it('should map a mix of interpolated and static content', async () => {
|
||||
const mappings = await compileAndMap('<h3>Hello {{ name }}</h3>');
|
||||
expectMapping(
|
||||
mappings,
|
||||
{source: '<h3>', generated: 'i0.ɵɵelementStart(0, "h3")', sourceUrl: '../test.ts'});
|
||||
@@ -65,8 +65,8 @@ runInEachFileSystem((os) => {
|
||||
mappings, {source: '</h3>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
|
||||
});
|
||||
|
||||
it('should map a complex interpolated expression', () => {
|
||||
const mappings = compileAndMap('<h2>{{ greeting + " " + name }}</h2>');
|
||||
it('should map a complex interpolated expression', async () => {
|
||||
const mappings = await compileAndMap('<h2>{{ greeting + " " + name }}</h2>');
|
||||
expectMapping(
|
||||
mappings,
|
||||
{source: '<h2>', generated: 'i0.ɵɵelementStart(0, "h2")', sourceUrl: '../test.ts'});
|
||||
@@ -79,8 +79,8 @@ runInEachFileSystem((os) => {
|
||||
mappings, {source: '</h2>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
|
||||
});
|
||||
|
||||
it('should map interpolated properties', () => {
|
||||
const mappings = compileAndMap('<div id="{{name}}"></div>');
|
||||
it('should map interpolated properties', async () => {
|
||||
const mappings = await compileAndMap('<div id="{{name}}"></div>');
|
||||
expectMapping(mappings, {
|
||||
source: '<div id="{{name}}"></div>',
|
||||
generated: 'i0.ɵɵelement(0, "div", 0)',
|
||||
@@ -93,8 +93,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should map interpolation with pipe', () => {
|
||||
const mappings = compileAndMap('<div>{{200.3 | percent : 2 }}</div>');
|
||||
it('should map interpolation with pipe', async () => {
|
||||
const mappings = await compileAndMap('<div>{{200.3 | percent : 2 }}</div>');
|
||||
expectMapping(
|
||||
mappings,
|
||||
{source: '<div>', generated: 'i0.ɵɵelementStart(0, "div")', sourceUrl: '../test.ts'});
|
||||
@@ -110,8 +110,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
|
||||
describe('(property bindings)', () => {
|
||||
it('should map a simple input binding expression', () => {
|
||||
const mappings = compileAndMap('<div [attr]="name"></div>');
|
||||
it('should map a simple input binding expression', async () => {
|
||||
const mappings = await compileAndMap('<div [attr]="name"></div>');
|
||||
expectMapping(mappings, {
|
||||
source: '<div [attr]="name"></div>',
|
||||
generated: 'i0.ɵɵelement(0, "div", 0)',
|
||||
@@ -124,8 +124,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should map a complex input binding expression', () => {
|
||||
const mappings = compileAndMap('<div [attr]="greeting + name"></div>');
|
||||
it('should map a complex input binding expression', async () => {
|
||||
const mappings = await compileAndMap('<div [attr]="greeting + name"></div>');
|
||||
|
||||
expectMapping(mappings, {
|
||||
source: '<div [attr]="greeting + name"></div>',
|
||||
@@ -139,8 +139,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should map a longhand input binding expression', () => {
|
||||
const mappings = compileAndMap('<div bind-attr="name"></div>');
|
||||
it('should map a longhand input binding expression', async () => {
|
||||
const mappings = await compileAndMap('<div bind-attr="name"></div>');
|
||||
expectMapping(mappings, {
|
||||
source: '<div bind-attr="name"></div>',
|
||||
generated: 'i0.ɵɵelement(0, "div", 0)',
|
||||
@@ -153,8 +153,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should map a simple output binding expression', () => {
|
||||
const mappings = compileAndMap('<button (click)="doSomething()">Do it</button>');
|
||||
it('should map a simple output binding expression', async () => {
|
||||
const mappings = await compileAndMap('<button (click)="doSomething()">Do it</button>');
|
||||
expectMapping(mappings, {
|
||||
source: '<button (click)="doSomething()">',
|
||||
generated: 'i0.ɵɵelementStart(0, "button", 0)',
|
||||
@@ -171,8 +171,8 @@ runInEachFileSystem((os) => {
|
||||
{source: '</button>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
|
||||
});
|
||||
|
||||
it('should map a complex output binding expression', () => {
|
||||
const mappings = compileAndMap(
|
||||
it('should map a complex output binding expression', async () => {
|
||||
const mappings = await compileAndMap(
|
||||
`<button (click)="items.push('item' + items.length)">Add Item</button>`);
|
||||
expectMapping(mappings, {
|
||||
source: `<button (click)="items.push('item' + items.length)">`,
|
||||
@@ -196,8 +196,8 @@ runInEachFileSystem((os) => {
|
||||
{source: '</button>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
|
||||
});
|
||||
|
||||
it('should map a longhand output binding expression', () => {
|
||||
const mappings = compileAndMap('<button on-click="doSomething()">Do it</button>');
|
||||
it('should map a longhand output binding expression', async () => {
|
||||
const mappings = await compileAndMap('<button on-click="doSomething()">Do it</button>');
|
||||
expectMapping(mappings, {
|
||||
source: '<button on-click="doSomething()">',
|
||||
generated: 'i0.ɵɵelementStart(0, "button", 0)',
|
||||
@@ -214,8 +214,8 @@ runInEachFileSystem((os) => {
|
||||
{source: '</button>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
|
||||
});
|
||||
|
||||
it('should map a two-way binding expression', () => {
|
||||
const mappings = compileAndMap('Name: <input [(ngModel)]="name">');
|
||||
it('should map a two-way binding expression', async () => {
|
||||
const mappings = await compileAndMap('Name: <input [(ngModel)]="name">');
|
||||
expectMapping(mappings, {
|
||||
source: '<input [(ngModel)]="name">',
|
||||
generated: 'i0.ɵɵelementStart(1, "input", 0)',
|
||||
@@ -235,8 +235,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should map a longhand two-way binding expression', () => {
|
||||
const mappings = compileAndMap('Name: <input bindon-ngModel="name">');
|
||||
it('should map a longhand two-way binding expression', async () => {
|
||||
const mappings = await compileAndMap('Name: <input bindon-ngModel="name">');
|
||||
expectMapping(mappings, {
|
||||
source: '<input bindon-ngModel="name">',
|
||||
generated: 'i0.ɵɵelementStart(1, "input", 0)',
|
||||
@@ -256,8 +256,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should map a class input binding', () => {
|
||||
const mappings = compileAndMap('<div [class.initial]="isInitial">Message</div>');
|
||||
it('should map a class input binding', async () => {
|
||||
const mappings = await compileAndMap('<div [class.initial]="isInitial">Message</div>');
|
||||
expectMapping(mappings, {
|
||||
source: '<div [class.initial]="isInitial">',
|
||||
generated: 'i0.ɵɵelementStart(0, "div")',
|
||||
@@ -277,8 +277,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
|
||||
describe('(structural directives)', () => {
|
||||
it('should map *ngIf scenario', () => {
|
||||
const mappings = compileAndMap('<div *ngIf="showMessage()">{{ name }}</div>');
|
||||
it('should map *ngIf scenario', async () => {
|
||||
const mappings = await compileAndMap('<div *ngIf="showMessage()">{{ name }}</div>');
|
||||
|
||||
expectMapping(mappings, {
|
||||
source: '<div *ngIf="showMessage()">',
|
||||
@@ -301,8 +301,8 @@ runInEachFileSystem((os) => {
|
||||
// });
|
||||
});
|
||||
|
||||
it('should map ng-template [ngIf] scenario', () => {
|
||||
const mappings = compileAndMap(
|
||||
it('should map ng-template [ngIf] scenario', async () => {
|
||||
const mappings = await compileAndMap(
|
||||
`<ng-template [ngIf]="showMessage()">\n` +
|
||||
` <div>{{ name }}</div>\n` +
|
||||
` <hr>\n` +
|
||||
@@ -327,8 +327,8 @@ runInEachFileSystem((os) => {
|
||||
// });
|
||||
});
|
||||
|
||||
it('should map *ngFor scenario', () => {
|
||||
const mappings = compileAndMap(
|
||||
it('should map *ngFor scenario', async () => {
|
||||
const mappings = await compileAndMap(
|
||||
'<div *ngFor="let item of items; index as i; trackBy: trackByFn">{{ item }}</div>');
|
||||
|
||||
expectMapping(mappings, {
|
||||
@@ -353,8 +353,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
|
||||
describe('(content projection)', () => {
|
||||
it('should map default and selected projection', () => {
|
||||
const mappings = compileAndMap(
|
||||
it('should map default and selected projection', async () => {
|
||||
const mappings = await compileAndMap(
|
||||
`<h3><ng-content select="title"></ng-content></h3>\n` +
|
||||
`<div><ng-content></ng-content></div>`);
|
||||
|
||||
@@ -383,8 +383,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
|
||||
describe('$localize', () => {
|
||||
it('should create simple i18n message source-mapping', () => {
|
||||
const mappings = compileAndMap(`<div i18n>Hello, World!</div>`);
|
||||
it('should create simple i18n message source-mapping', async () => {
|
||||
const mappings = await compileAndMap(`<div i18n>Hello, World!</div>`);
|
||||
expectMapping(mappings, {
|
||||
source: '<div i18n>',
|
||||
generated: 'i0.ɵɵelementStart(0, "div")',
|
||||
@@ -397,8 +397,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should create placeholder source-mappings', () => {
|
||||
const mappings = compileAndMap(`<div i18n>Hello, {{name}}!</div>`);
|
||||
it('should create placeholder source-mappings', async () => {
|
||||
const mappings = await compileAndMap(`<div i18n>Hello, {{name}}!</div>`);
|
||||
expectMapping(mappings, {
|
||||
source: '<div i18n>',
|
||||
generated: 'i0.ɵɵelementStart(0, "div")',
|
||||
@@ -427,8 +427,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
|
||||
it('should correctly handle collapsed whitespace in interpolation placeholder source-mappings',
|
||||
() => {
|
||||
const mappings = compileAndMap(
|
||||
async () => {
|
||||
const mappings = await compileAndMap(
|
||||
`<div i18n title=" pre-title {{name}} post-title" i18n-title> pre-body {{greeting}} post-body</div>`);
|
||||
expectMapping(mappings, {
|
||||
source: '<div i18n title=" pre-title {{name}} post-title" i18n-title>',
|
||||
@@ -458,9 +458,9 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
|
||||
it('should correctly handle collapsed whitespace in element placeholder source-mappings',
|
||||
() => {
|
||||
const mappings =
|
||||
compileAndMap(`<div i18n>\n pre-p\n <p>\n in-p\n </p>\n post-p\n</div>`);
|
||||
async () => {
|
||||
const mappings = await compileAndMap(
|
||||
`<div i18n>\n pre-p\n <p>\n in-p\n </p>\n post-p\n</div>`);
|
||||
// $localize expressions
|
||||
expectMapping(mappings, {
|
||||
sourceUrl: '../test.ts',
|
||||
@@ -515,8 +515,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should create tag (container) placeholder source-mappings', () => {
|
||||
const mappings = compileAndMap(`<div i18n>Hello, <b>World</b>!</div>`);
|
||||
it('should create tag (container) placeholder source-mappings', async () => {
|
||||
const mappings = await compileAndMap(`<div i18n>Hello, <b>World</b>!</div>`);
|
||||
expectMapping(mappings, {
|
||||
source: '<div i18n>',
|
||||
generated: 'i0.ɵɵelementStart(0, "div")',
|
||||
@@ -555,8 +555,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should create (simple string) inline template source-mapping', () => {
|
||||
const mappings = compileAndMap('<div>this is a test</div><div>{{ 1 + 2 }}</div>');
|
||||
it('should create (simple string) inline template source-mapping', async () => {
|
||||
const mappings = await compileAndMap('<div>this is a test</div><div>{{ 1 + 2 }}</div>');
|
||||
|
||||
// Creation mode
|
||||
expectMapping(
|
||||
@@ -586,9 +586,9 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
|
||||
it('should create correct inline template source-mapping when the source contains escape sequences',
|
||||
() => {
|
||||
async () => {
|
||||
// Note that the escaped double quotes, which need un-escaping to be parsed correctly.
|
||||
const mappings = compileAndMap('<div class=\\"some-class\\">this is a test</div>');
|
||||
const mappings = await compileAndMap('<div class=\\"some-class\\">this is a test</div>');
|
||||
|
||||
expectMapping(mappings, {
|
||||
generated: 'i0.ɵɵelementStart(0, "div", 0)',
|
||||
@@ -603,9 +603,9 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
|
||||
describe('External templates (where TS supports source-mapping)', () => {
|
||||
it('should create external template source-mapping', () => {
|
||||
const mappings =
|
||||
compileAndMap('<div>this is a test</div><div>{{ 1 + 2 }}</div>', './dir/test.html');
|
||||
it('should create external template source-mapping', async () => {
|
||||
const mappings = await compileAndMap(
|
||||
'<div>this is a test</div><div>{{ 1 + 2 }}</div>', './dir/test.html');
|
||||
|
||||
// Creation mode
|
||||
expectMapping(mappings, {
|
||||
@@ -641,8 +641,8 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should create correct mappings when templateUrl is in a different rootDir', () => {
|
||||
const mappings = compileAndMap(
|
||||
it('should create correct mappings when templateUrl is in a different rootDir', async () => {
|
||||
const mappings = await compileAndMap(
|
||||
'<div>this is a test</div><div>{{ 1 + 2 }}</div>', 'extraRootDir/test.html');
|
||||
|
||||
// Creation mode
|
||||
@@ -687,7 +687,7 @@ runInEachFileSystem((os) => {
|
||||
});
|
||||
|
||||
|
||||
function compileAndMap(template: string, templateUrl: string|null = null) {
|
||||
async function compileAndMap(template: string, templateUrl: string|null = null) {
|
||||
const templateConfig = templateUrl ? `templateUrl: '${templateUrl}'` :
|
||||
('template: `' + template.replace(/`/g, '\\`') + '`');
|
||||
env.write('test.ts', `
|
||||
@@ -728,7 +728,7 @@ runInEachFileSystem((os) => {
|
||||
env.write(templateUrl, template);
|
||||
}
|
||||
env.driveMain();
|
||||
return getMappedSegments(env, 'test.js');
|
||||
return await getMappedSegments(env, 'test.js');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -637,10 +637,10 @@ function isOutput(value: any): value is Output {
|
||||
}
|
||||
|
||||
function parseInputOutputs(values: string[]): StringMap {
|
||||
return values.reduce((map, value) => {
|
||||
const [field, property] = value.split(',').map(piece => piece.trim());
|
||||
map[field] = property || field;
|
||||
return map;
|
||||
return values.reduce((results, value) => {
|
||||
const [field, property] = value.split(':', 2).map(str => str.trim());
|
||||
results[field] = property || field;
|
||||
return results;
|
||||
}, {} as StringMap);
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,6 @@ function mapToExpression(
|
||||
map: {[key: string]: string|string[]}, keepDeclared?: boolean): o.Expression {
|
||||
return o.literalMap(Object.getOwnPropertyNames(map).map(key => {
|
||||
// canonical syntax: `dirProp: publicProp`
|
||||
// if there is no `:`, use dirProp = elProp
|
||||
const value = map[key];
|
||||
let declaredName: string;
|
||||
let publicName: string;
|
||||
@@ -186,12 +185,9 @@ function mapToExpression(
|
||||
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 = key;
|
||||
publicName = value;
|
||||
needsDeclaredName = false;
|
||||
}
|
||||
return {
|
||||
key: minifiedName,
|
||||
|
||||
@@ -8,9 +8,8 @@
|
||||
|
||||
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '@angular/compiler';
|
||||
import {EmitterVisitorContext} from '@angular/compiler/src/output/abstract_emitter';
|
||||
import {SourceMap} from '@angular/compiler/src/output/source_map';
|
||||
|
||||
import {extractSourceMap, originalPositionFor} from './source_map_util';
|
||||
import {originalPositionFor} from './source_map_util';
|
||||
|
||||
{
|
||||
describe('AbstractEmitter', () => {
|
||||
@@ -43,11 +42,11 @@ import {extractSourceMap, originalPositionFor} from './source_map_util';
|
||||
expectMap(ctx, 1, 0, 'a.js', 0, 4);
|
||||
});
|
||||
|
||||
it('should be able to shift the content', () => {
|
||||
it('should be able to shift the content', async () => {
|
||||
ctx.print(createSourceSpan(fileA, 0), 'fileA-0');
|
||||
|
||||
const sm = ctx.toSourceMapGenerator('o.ts', 10).toJSON()!;
|
||||
expect(originalPositionFor(sm, {line: 11, column: 0})).toEqual({
|
||||
expect(await originalPositionFor(sm, {line: 11, column: 0})).toEqual({
|
||||
line: 1,
|
||||
column: 0,
|
||||
source: 'a.js',
|
||||
@@ -113,16 +112,15 @@ import {extractSourceMap, originalPositionFor} from './source_map_util';
|
||||
|
||||
// All lines / columns indexes are 0-based
|
||||
// Note: source-map line indexes are 1-based, column 0-based
|
||||
function expectMap(
|
||||
async function expectMap(
|
||||
ctx: EmitterVisitorContext, genLine: number, genCol: number, source: string|null = null,
|
||||
srcLine: number|null = null, srcCol: number|null = null) {
|
||||
const sm = ctx.toSourceMapGenerator('o.ts').toJSON()!;
|
||||
const genPosition = {line: genLine + 1, column: genCol};
|
||||
const origPosition = originalPositionFor(sm, genPosition);
|
||||
// TODO: Review use of `any` here (#19904)
|
||||
expect(origPosition.source as any).toEqual(source);
|
||||
expect(origPosition.line as any).toEqual(srcLine === null ? null : srcLine + 1);
|
||||
expect(origPosition.column as any).toEqual(srcCol);
|
||||
const origPosition = await originalPositionFor(sm, genPosition);
|
||||
expect(origPosition.source).toEqual(source);
|
||||
expect(origPosition.line).toEqual(srcLine === null ? null : srcLine + 1);
|
||||
expect(origPosition.column).toEqual(srcCol);
|
||||
}
|
||||
|
||||
// returns the number of segments per line
|
||||
|
||||
@@ -11,16 +11,16 @@ import b64 from 'base64-js';
|
||||
import {SourceMapConsumer} from 'source-map';
|
||||
|
||||
export interface SourceLocation {
|
||||
line: number;
|
||||
column: number;
|
||||
source: string;
|
||||
line: number|null;
|
||||
column: number|null;
|
||||
source: string|null;
|
||||
}
|
||||
|
||||
export function originalPositionFor(
|
||||
sourceMap: SourceMap, genPosition: {line: number, column: number}): SourceLocation {
|
||||
export async function originalPositionFor(
|
||||
sourceMap: SourceMap, genPosition: {line: number, column: number}): Promise<SourceLocation> {
|
||||
// Note: The `SourceMap` type from the compiler is different to `RawSourceMap`
|
||||
// from the `source-map` package, but the method we rely on works as expected.
|
||||
const smc = new SourceMapConsumer(sourceMap as any);
|
||||
const smc = await new SourceMapConsumer(sourceMap as any);
|
||||
// Note: We don't return the original object as it also contains a `name` property
|
||||
// which is always null and we don't want to include that in our assertions...
|
||||
const {line, column, source} = smc.originalPositionFor(genPosition);
|
||||
|
||||
@@ -37,6 +37,7 @@ export * from './core_render3_private_export';
|
||||
export {SecurityContext} from './sanitization/security';
|
||||
export {Sanitizer} from './sanitization/sanitizer';
|
||||
export {createNgModule, createNgModuleRef, createEnvironmentInjector} from './render3/ng_module_ref';
|
||||
export {createComponent, reflectComponentType, ComponentMirror} from './render3/component';
|
||||
|
||||
import {global} from './util/global';
|
||||
if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
||||
|
||||
@@ -272,6 +272,14 @@ export class R3Injector extends EnvironmentInjector {
|
||||
const previousInjectImplementation = setInjectImplementation(undefined);
|
||||
try {
|
||||
const initializers = this.get(ENVIRONMENT_INITIALIZER.multi, EMPTY_ARRAY, InjectFlags.Self);
|
||||
if (ngDevMode && !Array.isArray(initializers)) {
|
||||
throw new RuntimeError(
|
||||
RuntimeErrorCode.INVALID_MULTI_PROVIDER,
|
||||
'Unexpected type of the `ENVIRONMENT_INITIALIZER` token value ' +
|
||||
`(expected an array, but got ${typeof initializers}). ` +
|
||||
'Please check that the `ENVIRONMENT_INITIALIZER` token is configured as a ' +
|
||||
'`multi: true` provider.');
|
||||
}
|
||||
for (const initializer of initializers) {
|
||||
initializer();
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ export const enum RuntimeErrorCode {
|
||||
INJECTOR_ALREADY_DESTROYED = 205,
|
||||
PROVIDER_IN_WRONG_CONTEXT = 207,
|
||||
MISSING_INJECTION_TOKEN = 208,
|
||||
INVALID_MULTI_PROVIDER = 209,
|
||||
|
||||
// Template Errors
|
||||
MULTIPLE_COMPONENTS_MATCH = -300,
|
||||
|
||||
@@ -6,12 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injector} from '../di/injector';
|
||||
import {Type} from '../interface/type';
|
||||
import {stringify} from '../util/stringify';
|
||||
|
||||
import {ComponentFactory, ComponentRef} from './component_factory';
|
||||
import {NgModuleRef} from './ng_module_factory';
|
||||
import {ComponentFactory} from './component_factory';
|
||||
|
||||
export function noComponentFactoryError(component: Function) {
|
||||
const error = Error(`No component factory found for ${
|
||||
|
||||
@@ -6,236 +6,184 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
// We are temporarily importing the existing viewEngine from core so we can be sure we are
|
||||
// correctly implementing its interfaces for backwards compatibility.
|
||||
import {Injector} from '../di/injector';
|
||||
import {Sanitizer} from '../sanitization/sanitizer';
|
||||
import {assertDefined, assertIndexInRange} from '../util/assert';
|
||||
import {EnvironmentInjector, getNullInjector} from '../di/r3_injector';
|
||||
import {Type} from '../interface/type';
|
||||
import {ComponentRef} from '../linker/component_factory';
|
||||
|
||||
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
|
||||
import {throwProviderNotFoundError} from './errors_di';
|
||||
import {registerPostOrderHooks} from './hooks';
|
||||
import {addToViewTree, CLEAN_PROMISE, createLView, getOrCreateTComponentView, getOrCreateTNode, initTNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, markAsComponentHost, registerHostBindingOpCodes} from './instructions/shared';
|
||||
import {ComponentDef, RenderFlags} from './interfaces/definition';
|
||||
import {TElementNode, TNodeType} from './interfaces/node';
|
||||
import {PlayerHandler} from './interfaces/player';
|
||||
import {Renderer, RendererFactory} from './interfaces/renderer';
|
||||
import {RElement} from './interfaces/renderer_dom';
|
||||
import {CONTEXT, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
||||
import {writeDirectClass, writeDirectStyle} from './node_manipulation';
|
||||
import {getCurrentTNode, getLView, setSelectedIndex} from './state';
|
||||
import {computeStaticStyling} from './styling/static_styling';
|
||||
import {setUpAttributes} from './util/attrs_utils';
|
||||
import {defaultScheduler} from './util/misc_utils';
|
||||
import {getRootContext} from './util/view_traversal_utils';
|
||||
|
||||
|
||||
|
||||
/** Options that control how the component should be bootstrapped. */
|
||||
export interface CreateComponentOptions {
|
||||
/** Which renderer factory to use. */
|
||||
rendererFactory?: RendererFactory;
|
||||
|
||||
/** A custom sanitizer instance */
|
||||
sanitizer?: Sanitizer;
|
||||
|
||||
/** A custom animation player handler */
|
||||
playerHandler?: PlayerHandler;
|
||||
|
||||
/**
|
||||
* Host element on which the component will be bootstrapped. If not specified,
|
||||
* the component definition's `tag` is used to query the existing DOM for the
|
||||
* element to bootstrap.
|
||||
*/
|
||||
host?: RElement|string;
|
||||
|
||||
/** Module injector for the component. If unspecified, the injector will be NULL_INJECTOR. */
|
||||
injector?: Injector;
|
||||
|
||||
/**
|
||||
* List of features to be applied to the created component. Features are simply
|
||||
* functions that decorate a component with a certain behavior.
|
||||
*
|
||||
* Typically, the features in this list are features that cannot be added to the
|
||||
* other features list in the component definition because they rely on other factors.
|
||||
*
|
||||
* Example: `LifecycleHooksFeature` is a function that adds lifecycle hook capabilities
|
||||
* to root components in a tree-shakable way. It cannot be added to the component
|
||||
* features list because there's no way of knowing when the component will be used as
|
||||
* a root component.
|
||||
*/
|
||||
hostFeatures?: HostFeature[];
|
||||
|
||||
/**
|
||||
* A function which is used to schedule change detection work in the future.
|
||||
*
|
||||
* When marking components as dirty, it is necessary to schedule the work of
|
||||
* change detection in the future. This is done to coalesce multiple
|
||||
* {@link markDirty} calls into a single changed detection processing.
|
||||
*
|
||||
* The default value of the scheduler is the `requestAnimationFrame` function.
|
||||
*
|
||||
* It is also useful to override this function for testing purposes.
|
||||
*/
|
||||
scheduler?: (work: () => void) => void;
|
||||
}
|
||||
|
||||
/** See CreateComponentOptions.hostFeatures */
|
||||
type HostFeature = (<T>(component: T, componentDef: ComponentDef<T>) => void);
|
||||
|
||||
// TODO: A hack to not pull in the NullInjector from @angular/core.
|
||||
export const NULL_INJECTOR: Injector = {
|
||||
get: (token: any, notFoundValue?: any) => {
|
||||
throwProviderNotFoundError(token, 'NullInjector');
|
||||
}
|
||||
};
|
||||
import {ComponentFactory} from './component_ref';
|
||||
import {getComponentDef} from './definition';
|
||||
import {assertComponentDef} from './errors';
|
||||
|
||||
/**
|
||||
* Creates the root component view and the root component node.
|
||||
* Creates a `ComponentRef` instance based on provided component type and a set of options.
|
||||
*
|
||||
* @param rNode Render host element.
|
||||
* @param def ComponentDef
|
||||
* @param rootView The parent view where the host node is stored
|
||||
* @param rendererFactory Factory to be used for creating child renderers.
|
||||
* @param hostRenderer The current renderer
|
||||
* @param sanitizer The sanitizer, if provided
|
||||
* @usageNotes
|
||||
*
|
||||
* @returns Component view created
|
||||
* The example below demonstrates how the `createComponent` function can be used
|
||||
* to create an instance of a ComponentRef dynamically and attach it to an ApplicationRef,
|
||||
* so that it gets included into change detection cycles.
|
||||
*
|
||||
* Note: the example uses standalone components, but the function can also be used for
|
||||
* non-standalone components (declared in an NgModule) as well.
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* standalone: true,
|
||||
* template: `Hello {{ name }}!`
|
||||
* })
|
||||
* class HelloComponent {
|
||||
* name = 'Angular';
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* standalone: true,
|
||||
* template: `<div id="hello-component-host"></div>`
|
||||
* })
|
||||
* class RootComponent {}
|
||||
*
|
||||
* // Bootstrap an application.
|
||||
* const applicationRef = await bootstrapApplication(RootComponent);
|
||||
*
|
||||
* // Locate a DOM node that would be used as a host.
|
||||
* const host = document.getElementById('hello-component-host');
|
||||
*
|
||||
* // Get an `EnvironmentInjector` instance from the `ApplicationRef`.
|
||||
* const environmentInjector = applicationRef.injector;
|
||||
*
|
||||
* // We can now create a `ComponentRef` instance.
|
||||
* const componentRef = createComponent(HelloComponent, {host, environmentInjector});
|
||||
*
|
||||
* // Last step is to register the newly created ref using the `ApplicationRef` instance
|
||||
* // to include the component view into change detection cycles.
|
||||
* applicationRef.attachView(componentRef.hostView);
|
||||
* ```
|
||||
*
|
||||
* @param component Component class reference.
|
||||
* @param options Set of options to use:
|
||||
* * `environmentInjector`: An `EnvironmentInjector` instance to be used for the component, see
|
||||
* additional info about it at https://angular.io/guide/standalone-components#environment-injectors.
|
||||
* * `hostElement` (optional): A DOM node that should act as a host node for the component. If not
|
||||
* provided, Angular creates one based on the tag name used in the component selector (and falls
|
||||
* back to using `div` if selector doesn't have tag name info).
|
||||
* * `elementInjector` (optional): An `ElementInjector` instance, see additional info about it at
|
||||
* https://angular.io/guide/hierarchical-dependency-injection#elementinjector.
|
||||
* * `projectableNodes` (optional): A list of DOM nodes that should be projected through
|
||||
* [`<ng-content>`](api/core/ng-content) of the new component instance.
|
||||
* @returns ComponentRef instance that represents a given Component.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function createRootComponentView(
|
||||
rNode: RElement|null, def: ComponentDef<any>, rootView: LView, rendererFactory: RendererFactory,
|
||||
hostRenderer: Renderer, sanitizer?: Sanitizer|null): LView {
|
||||
const tView = rootView[TVIEW];
|
||||
const index = HEADER_OFFSET;
|
||||
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 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;
|
||||
if (mergedAttrs !== null) {
|
||||
computeStaticStyling(tNode, mergedAttrs, true);
|
||||
if (rNode !== null) {
|
||||
setUpAttributes(hostRenderer, rNode, mergedAttrs);
|
||||
if (tNode.classes !== null) {
|
||||
writeDirectClass(hostRenderer, rNode, tNode.classes);
|
||||
}
|
||||
if (tNode.styles !== null) {
|
||||
writeDirectStyle(hostRenderer, rNode, tNode.styles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const viewRenderer = rendererFactory.createRenderer(rNode, def);
|
||||
const componentView = createLView(
|
||||
rootView, getOrCreateTComponentView(def), null,
|
||||
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, rootView[index], tNode,
|
||||
rendererFactory, viewRenderer, sanitizer || null, null, null);
|
||||
|
||||
if (tView.firstCreatePass) {
|
||||
diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), tView, def.type);
|
||||
markAsComponentHost(tView, tNode);
|
||||
initTNodeFlags(tNode, rootView.length, 1);
|
||||
}
|
||||
|
||||
addToViewTree(rootView, componentView);
|
||||
|
||||
// Store component view at node index, with node as the HOST
|
||||
return rootView[index] = componentView;
|
||||
export function createComponent<C>(component: Type<C>, options: {
|
||||
environmentInjector: EnvironmentInjector,
|
||||
hostElement?: Element,
|
||||
elementInjector?: Injector,
|
||||
projectableNodes?: Node[][],
|
||||
}): ComponentRef<C> {
|
||||
ngDevMode && assertComponentDef(component);
|
||||
const componentDef = getComponentDef(component)!;
|
||||
const elementInjector = options.elementInjector || getNullInjector();
|
||||
const factory = new ComponentFactory<C>(componentDef);
|
||||
return factory.create(
|
||||
elementInjector, options.projectableNodes, options.hostElement, options.environmentInjector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a root component and sets it up with features and host bindings. Shared by
|
||||
* renderComponent() and ViewContainerRef.createComponent().
|
||||
* An interface that describes the subset of component metadata
|
||||
* that can be retrieved using the `reflectComponentType` function.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function createRootComponent<T>(
|
||||
componentView: LView, componentDef: ComponentDef<T>, rootLView: LView, rootContext: RootContext,
|
||||
hostFeatures: HostFeature[]|null): any {
|
||||
const tView = rootLView[TVIEW];
|
||||
// Create directive instance with factory() and store at next index in viewData
|
||||
const component = instantiateRootComponent(tView, rootLView, componentDef);
|
||||
|
||||
rootContext.components.push(component);
|
||||
componentView[CONTEXT] = component;
|
||||
|
||||
if (hostFeatures !== null) {
|
||||
for (const feature of hostFeatures) {
|
||||
feature(component, componentDef);
|
||||
}
|
||||
}
|
||||
|
||||
// We want to generate an empty QueryList for root content queries for backwards
|
||||
// compatibility with ViewEngine.
|
||||
if (componentDef.contentQueries) {
|
||||
const tNode = getCurrentTNode()!;
|
||||
ngDevMode && assertDefined(tNode, 'TNode expected');
|
||||
componentDef.contentQueries(RenderFlags.Create, component, tNode.directiveStart);
|
||||
}
|
||||
|
||||
const rootTNode = getCurrentTNode()!;
|
||||
ngDevMode && assertDefined(rootTNode, 'tNode should have been already created');
|
||||
if (tView.firstCreatePass &&
|
||||
(componentDef.hostBindings !== null || componentDef.hostAttrs !== null)) {
|
||||
setSelectedIndex(rootTNode.index);
|
||||
|
||||
const rootTView = rootLView[TVIEW];
|
||||
registerHostBindingOpCodes(
|
||||
rootTView, rootTNode, rootLView, rootTNode.directiveStart, rootTNode.directiveEnd,
|
||||
componentDef);
|
||||
|
||||
invokeHostBindingsInCreationMode(componentDef, component);
|
||||
}
|
||||
return component;
|
||||
export interface ComponentMirror<C> {
|
||||
/**
|
||||
* The component's HTML selector.
|
||||
*/
|
||||
get selector(): string;
|
||||
/**
|
||||
* The type of component the factory will create.
|
||||
*/
|
||||
get type(): Type<C>;
|
||||
/**
|
||||
* The inputs of the component.
|
||||
*/
|
||||
get inputs(): ReadonlyArray<{readonly propName: string, readonly templateName: string}>;
|
||||
/**
|
||||
* The outputs of the component.
|
||||
*/
|
||||
get outputs(): ReadonlyArray<{readonly propName: string, readonly templateName: string}>;
|
||||
/**
|
||||
* Selector for all <ng-content> elements in the component.
|
||||
*/
|
||||
get ngContentSelectors(): ReadonlyArray<string>;
|
||||
/**
|
||||
* Whether this component is marked as standalone.
|
||||
* Note: an extra flag, not present in `ComponentFactory`.
|
||||
*/
|
||||
get isStandalone(): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an object that allows to retrieve component metadata.
|
||||
*
|
||||
* @usageNotes
|
||||
*
|
||||
* The example below demonstrates how to use the function and how the fields
|
||||
* of the returned object map to the component metadata.
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* standalone: true,
|
||||
* selector: 'foo-component',
|
||||
* template: `
|
||||
* <ng-content></ng-content>
|
||||
* <ng-content select="content-selector-a"></ng-content>
|
||||
* `,
|
||||
* })
|
||||
* class FooComponent {
|
||||
* @Input('inputName') inputPropName: string;
|
||||
* @Output('outputName') outputPropName = new EventEmitter<void>();
|
||||
* }
|
||||
*
|
||||
* const mirror = reflectComponentType(FooComponent);
|
||||
* expect(mirror.type).toBe(FooComponent);
|
||||
* expect(mirror.selector).toBe('foo-component');
|
||||
* expect(mirror.isStandalone).toBe(true);
|
||||
* expect(mirror.inputs).toEqual([{propName: 'inputName', templateName: 'inputPropName'}]);
|
||||
* expect(mirror.outputs).toEqual([{propName: 'outputName', templateName: 'outputPropName'}]);
|
||||
* expect(mirror.ngContentSelectors).toEqual([
|
||||
* '*', // first `<ng-content>` in a template, the selector defaults to `*`
|
||||
* 'content-selector-a' // second `<ng-content>` in a template
|
||||
* ]);
|
||||
* ```
|
||||
*
|
||||
* @param component Component class reference.
|
||||
* @returns An object that allows to retrieve component metadata.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function reflectComponentType<C>(component: Type<C>): ComponentMirror<C>|null {
|
||||
const componentDef = getComponentDef(component);
|
||||
if (!componentDef) return null;
|
||||
|
||||
export function createRootContext(
|
||||
scheduler?: (workFn: () => void) => void, playerHandler?: PlayerHandler|null): RootContext {
|
||||
const factory = new ComponentFactory<C>(componentDef);
|
||||
return {
|
||||
components: [],
|
||||
scheduler: scheduler || defaultScheduler,
|
||||
clean: CLEAN_PROMISE,
|
||||
playerHandler: playerHandler || null,
|
||||
flags: RootContextFlags.Empty
|
||||
get selector(): string {
|
||||
return factory.selector;
|
||||
},
|
||||
get type(): Type<C> {
|
||||
return factory.componentType;
|
||||
},
|
||||
get inputs(): ReadonlyArray<{propName: string, templateName: string}> {
|
||||
return factory.inputs;
|
||||
},
|
||||
get outputs(): ReadonlyArray<{propName: string, templateName: string}> {
|
||||
return factory.outputs;
|
||||
},
|
||||
get ngContentSelectors(): ReadonlyArray<string> {
|
||||
return factory.ngContentSelectors;
|
||||
},
|
||||
get isStandalone(): boolean {
|
||||
return componentDef.standalone;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to enable lifecycle hooks on the root component.
|
||||
*
|
||||
* Include this feature when calling `renderComponent` if the root component
|
||||
* you are rendering has lifecycle hooks defined. Otherwise, the hooks won't
|
||||
* be called properly.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* renderComponent(AppComponent, {hostFeatures: [LifecycleHooksFeature]});
|
||||
* ```
|
||||
*/
|
||||
export function LifecycleHooksFeature(): void {
|
||||
const tNode = getCurrentTNode()!;
|
||||
ngDevMode && assertDefined(tNode, 'TNode is required');
|
||||
registerPostOrderHooks(getLView()[TVIEW], tNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait on component until it is rendered.
|
||||
*
|
||||
* This function returns a `Promise` which is resolved when the component's
|
||||
* change detection is executed. This is determined by finding the scheduler
|
||||
* associated with the `component`'s render tree and waiting until the scheduler
|
||||
* flushes. If nothing is scheduled, the function returns a resolved promise.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* await whenRendered(myComponent);
|
||||
* ```
|
||||
*
|
||||
* @param component Component to wait upon
|
||||
* @returns Promise which resolves when the component is rendered.
|
||||
*/
|
||||
export function whenRendered(component: any): Promise<null> {
|
||||
return getRootContext(component).clean;
|
||||
}
|
||||
|
||||
@@ -11,33 +11,40 @@ import {Injector} from '../di/injector';
|
||||
import {InjectFlags} from '../di/interface/injector';
|
||||
import {ProviderToken} from '../di/provider_token';
|
||||
import {EnvironmentInjector} from '../di/r3_injector';
|
||||
import {formatRuntimeError, RuntimeError, RuntimeErrorCode} from '../errors';
|
||||
import {RuntimeError, RuntimeErrorCode} from '../errors';
|
||||
import {Type} from '../interface/type';
|
||||
import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
|
||||
import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as AbstractComponentRef} from '../linker/component_factory';
|
||||
import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver';
|
||||
import {createElementRef, ElementRef as viewEngine_ElementRef} from '../linker/element_ref';
|
||||
import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory';
|
||||
import {RendererFactory2} from '../render/api';
|
||||
import {Sanitizer} from '../sanitization/sanitizer';
|
||||
import {assertDefined, assertIndexInRange} from '../util/assert';
|
||||
import {VERSION} from '../version';
|
||||
import {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from '../view/provider_flags';
|
||||
|
||||
import {assertComponentType} from './assert';
|
||||
import {createRootComponent, createRootComponentView, createRootContext, LifecycleHooksFeature} from './component';
|
||||
import {getComponentDef} from './definition';
|
||||
import {NodeInjector} from './di';
|
||||
import {diPublicInInjector, getOrCreateNodeInjectorForNode, NodeInjector} from './di';
|
||||
import {throwProviderNotFoundError} from './errors_di';
|
||||
import {registerPostOrderHooks} from './hooks';
|
||||
import {reportUnknownPropertyError} from './instructions/element_validation';
|
||||
import {createLView, createTView, initializeInputAndOutputAliases, locateHostElement, markDirtyIfOnPush, renderView, setInputsForProperty} from './instructions/shared';
|
||||
import {ComponentDef} from './interfaces/definition';
|
||||
import {PropertyAliasValue, TContainerNode, TElementContainerNode, TElementNode, TNode} from './interfaces/node';
|
||||
import {RNode} from './interfaces/renderer_dom';
|
||||
import {HEADER_OFFSET, LView, LViewFlags, TVIEW, TViewType} from './interfaces/view';
|
||||
import {addToViewTree, CLEAN_PROMISE, createLView, createTView, getOrCreateTComponentView, getOrCreateTNode, initTNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, markAsComponentHost, markDirtyIfOnPush, registerHostBindingOpCodes, renderView, setInputsForProperty} from './instructions/shared';
|
||||
import {ComponentDef, RenderFlags} from './interfaces/definition';
|
||||
import {PropertyAliasValue, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType} from './interfaces/node';
|
||||
import {PlayerHandler} from './interfaces/player';
|
||||
import {Renderer, RendererFactory} from './interfaces/renderer';
|
||||
import {RElement, RNode} from './interfaces/renderer_dom';
|
||||
import {CONTEXT, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW, TViewType} from './interfaces/view';
|
||||
import {MATH_ML_NAMESPACE, SVG_NAMESPACE} from './namespaces';
|
||||
import {createElementNode, writeDirectClass} from './node_manipulation';
|
||||
import {createElementNode, writeDirectClass, writeDirectStyle} from './node_manipulation';
|
||||
import {extractAttrsAndClassesFromSelector, stringifyCSSSelectorList} from './node_selector_matcher';
|
||||
import {enterView, leaveView} from './state';
|
||||
import {enterView, getCurrentTNode, getLView, leaveView, setSelectedIndex} from './state';
|
||||
import {computeStaticStyling} from './styling/static_styling';
|
||||
import {setUpAttributes} from './util/attrs_utils';
|
||||
import {defaultScheduler} from './util/misc_utils';
|
||||
import {stringifyForError} from './util/stringify_utils';
|
||||
import {getRootContext} from './util/view_traversal_utils';
|
||||
import {getTNode} from './util/view_utils';
|
||||
import {RootViewRef, ViewRef} from './view_ref';
|
||||
|
||||
@@ -131,7 +138,7 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
||||
override create(
|
||||
injector: Injector, projectableNodes?: any[][]|undefined, rootSelectorOrNode?: any,
|
||||
environmentInjector?: viewEngine_NgModuleRef<any>|EnvironmentInjector|
|
||||
undefined): viewEngine_ComponentRef<T> {
|
||||
undefined): AbstractComponentRef<T> {
|
||||
environmentInjector = environmentInjector || this.ngModule;
|
||||
|
||||
let realEnvironmentInjector = environmentInjector instanceof EnvironmentInjector ?
|
||||
@@ -260,7 +267,7 @@ export function injectComponentFactoryResolver(): viewEngine_ComponentFactoryRes
|
||||
* method.
|
||||
*
|
||||
*/
|
||||
export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
|
||||
export class ComponentRef<T> extends AbstractComponentRef<T> {
|
||||
override instance: T;
|
||||
override hostView: ViewRef<T>;
|
||||
override changeDetectorRef: ViewEngine_ChangeDetectorRef;
|
||||
@@ -307,3 +314,214 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
|
||||
this.hostView.onDestroy(callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Options that control how the component should be bootstrapped. */
|
||||
export interface CreateComponentOptions {
|
||||
/** Which renderer factory to use. */
|
||||
rendererFactory?: RendererFactory;
|
||||
|
||||
/** A custom sanitizer instance */
|
||||
sanitizer?: Sanitizer;
|
||||
|
||||
/** A custom animation player handler */
|
||||
playerHandler?: PlayerHandler;
|
||||
|
||||
/**
|
||||
* Host element on which the component will be bootstrapped. If not specified,
|
||||
* the component definition's `tag` is used to query the existing DOM for the
|
||||
* element to bootstrap.
|
||||
*/
|
||||
host?: RElement|string;
|
||||
|
||||
/** Module injector for the component. If unspecified, the injector will be NULL_INJECTOR. */
|
||||
injector?: Injector;
|
||||
|
||||
/**
|
||||
* List of features to be applied to the created component. Features are simply
|
||||
* functions that decorate a component with a certain behavior.
|
||||
*
|
||||
* Typically, the features in this list are features that cannot be added to the
|
||||
* other features list in the component definition because they rely on other factors.
|
||||
*
|
||||
* Example: `LifecycleHooksFeature` is a function that adds lifecycle hook capabilities
|
||||
* to root components in a tree-shakable way. It cannot be added to the component
|
||||
* features list because there's no way of knowing when the component will be used as
|
||||
* a root component.
|
||||
*/
|
||||
hostFeatures?: HostFeature[];
|
||||
|
||||
/**
|
||||
* A function which is used to schedule change detection work in the future.
|
||||
*
|
||||
* When marking components as dirty, it is necessary to schedule the work of
|
||||
* change detection in the future. This is done to coalesce multiple
|
||||
* {@link markDirty} calls into a single changed detection processing.
|
||||
*
|
||||
* The default value of the scheduler is the `requestAnimationFrame` function.
|
||||
*
|
||||
* It is also useful to override this function for testing purposes.
|
||||
*/
|
||||
scheduler?: (work: () => void) => void;
|
||||
}
|
||||
|
||||
/** See CreateComponentOptions.hostFeatures */
|
||||
type HostFeature = (<T>(component: T, componentDef: ComponentDef<T>) => void);
|
||||
|
||||
// TODO: A hack to not pull in the NullInjector from @angular/core.
|
||||
export const NULL_INJECTOR: Injector = {
|
||||
get: (token: any, notFoundValue?: any) => {
|
||||
throwProviderNotFoundError(token, 'NullInjector');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the root component view and the root component node.
|
||||
*
|
||||
* @param rNode Render host element.
|
||||
* @param def ComponentDef
|
||||
* @param rootView The parent view where the host node is stored
|
||||
* @param rendererFactory Factory to be used for creating child renderers.
|
||||
* @param hostRenderer The current renderer
|
||||
* @param sanitizer The sanitizer, if provided
|
||||
*
|
||||
* @returns Component view created
|
||||
*/
|
||||
export function createRootComponentView(
|
||||
rNode: RElement|null, def: ComponentDef<any>, rootView: LView, rendererFactory: RendererFactory,
|
||||
hostRenderer: Renderer, sanitizer?: Sanitizer|null): LView {
|
||||
const tView = rootView[TVIEW];
|
||||
const index = HEADER_OFFSET;
|
||||
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 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;
|
||||
if (mergedAttrs !== null) {
|
||||
computeStaticStyling(tNode, mergedAttrs, true);
|
||||
if (rNode !== null) {
|
||||
setUpAttributes(hostRenderer, rNode, mergedAttrs);
|
||||
if (tNode.classes !== null) {
|
||||
writeDirectClass(hostRenderer, rNode, tNode.classes);
|
||||
}
|
||||
if (tNode.styles !== null) {
|
||||
writeDirectStyle(hostRenderer, rNode, tNode.styles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const viewRenderer = rendererFactory.createRenderer(rNode, def);
|
||||
const componentView = createLView(
|
||||
rootView, getOrCreateTComponentView(def), null,
|
||||
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, rootView[index], tNode,
|
||||
rendererFactory, viewRenderer, sanitizer || null, null, null);
|
||||
|
||||
if (tView.firstCreatePass) {
|
||||
diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), tView, def.type);
|
||||
markAsComponentHost(tView, tNode);
|
||||
initTNodeFlags(tNode, rootView.length, 1);
|
||||
}
|
||||
|
||||
addToViewTree(rootView, componentView);
|
||||
|
||||
// Store component view at node index, with node as the HOST
|
||||
return rootView[index] = componentView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a root component and sets it up with features and host bindings.Shared by
|
||||
* renderComponent() and ViewContainerRef.createComponent().
|
||||
*/
|
||||
export function createRootComponent<T>(
|
||||
componentView: LView, componentDef: ComponentDef<T>, rootLView: LView, rootContext: RootContext,
|
||||
hostFeatures: HostFeature[]|null): any {
|
||||
const tView = rootLView[TVIEW];
|
||||
// Create directive instance with factory() and store at next index in viewData
|
||||
const component = instantiateRootComponent(tView, rootLView, componentDef);
|
||||
|
||||
rootContext.components.push(component);
|
||||
componentView[CONTEXT] = component;
|
||||
|
||||
if (hostFeatures !== null) {
|
||||
for (const feature of hostFeatures) {
|
||||
feature(component, componentDef);
|
||||
}
|
||||
}
|
||||
|
||||
// We want to generate an empty QueryList for root content queries for backwards
|
||||
// compatibility with ViewEngine.
|
||||
if (componentDef.contentQueries) {
|
||||
const tNode = getCurrentTNode()!;
|
||||
ngDevMode && assertDefined(tNode, 'TNode expected');
|
||||
componentDef.contentQueries(RenderFlags.Create, component, tNode.directiveStart);
|
||||
}
|
||||
|
||||
const rootTNode = getCurrentTNode()!;
|
||||
ngDevMode && assertDefined(rootTNode, 'tNode should have been already created');
|
||||
if (tView.firstCreatePass &&
|
||||
(componentDef.hostBindings !== null || componentDef.hostAttrs !== null)) {
|
||||
setSelectedIndex(rootTNode.index);
|
||||
|
||||
const rootTView = rootLView[TVIEW];
|
||||
registerHostBindingOpCodes(
|
||||
rootTView, rootTNode, rootLView, rootTNode.directiveStart, rootTNode.directiveEnd,
|
||||
componentDef);
|
||||
|
||||
invokeHostBindingsInCreationMode(componentDef, component);
|
||||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
|
||||
export function createRootContext(
|
||||
scheduler?: (workFn: () => void) => void, playerHandler?: PlayerHandler|null): RootContext {
|
||||
return {
|
||||
components: [],
|
||||
scheduler: scheduler || defaultScheduler,
|
||||
clean: CLEAN_PROMISE,
|
||||
playerHandler: playerHandler || null,
|
||||
flags: RootContextFlags.Empty
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to enable lifecycle hooks on the root component.
|
||||
*
|
||||
* Include this feature when calling `renderComponent` if the root component
|
||||
* you are rendering has lifecycle hooks defined. Otherwise, the hooks won't
|
||||
* be called properly.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* renderComponent(AppComponent, {hostFeatures: [LifecycleHooksFeature]});
|
||||
* ```
|
||||
*/
|
||||
export function LifecycleHooksFeature(): void {
|
||||
const tNode = getCurrentTNode()!;
|
||||
ngDevMode && assertDefined(tNode, 'TNode is required');
|
||||
registerPostOrderHooks(getLView()[TVIEW], tNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait on component until it is rendered.
|
||||
*
|
||||
* This function returns a `Promise` which is resolved when the component's
|
||||
* change detection is executed. This is determined by finding the scheduler
|
||||
* associated with the `component`'s render tree and waiting until the scheduler
|
||||
* flushes. If nothing is scheduled, the function returns a resolved promise.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* await whenRendered(myComponent);
|
||||
* ```
|
||||
*
|
||||
* @param component Component to wait upon
|
||||
* @returns Promise which resolves when the component is rendered.
|
||||
*/
|
||||
export function whenRendered(component: any): Promise<null> {
|
||||
return getRootContext(component).clean;
|
||||
}
|
||||
|
||||
@@ -18,13 +18,8 @@ import {stringifyForError} from './util/stringify_utils';
|
||||
|
||||
/** Verifies that a given type is a Standalone Component. */
|
||||
export function assertStandaloneComponentType(type: Type<unknown>) {
|
||||
const componentDef = getComponentDef(type);
|
||||
if (!componentDef) {
|
||||
throw new RuntimeError(
|
||||
RuntimeErrorCode.MISSING_GENERATED_DEF,
|
||||
`The ${stringifyForError(type)} is not an Angular component, ` +
|
||||
`make sure it has the \`@Component\` decorator.`);
|
||||
}
|
||||
assertComponentDef(type);
|
||||
const componentDef = getComponentDef(type)!;
|
||||
if (!componentDef.standalone) {
|
||||
throw new RuntimeError(
|
||||
RuntimeErrorCode.TYPE_IS_NOT_STANDALONE,
|
||||
@@ -35,6 +30,16 @@ export function assertStandaloneComponentType(type: Type<unknown>) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Verifies whether a given type is a component */
|
||||
export function assertComponentDef(type: Type<unknown>) {
|
||||
if (!getComponentDef(type)) {
|
||||
throw new RuntimeError(
|
||||
RuntimeErrorCode.MISSING_GENERATED_DEF,
|
||||
`The ${stringifyForError(type)} is not an Angular component, ` +
|
||||
`make sure it has the \`@Component\` decorator.`);
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when there are multiple component selectors that match a given node */
|
||||
export function throwMultipleComponentError(
|
||||
tNode: TNode, first: Type<unknown>, second: Type<unknown>): never {
|
||||
|
||||
@@ -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 {LifecycleHooksFeature, whenRendered} from './component';
|
||||
import {LifecycleHooksFeature, whenRendered} from './component_ref';
|
||||
import {ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdefineNgModule, ɵɵdefinePipe, ɵɵsetComponentScope, ɵɵsetNgModuleScope} from './definition';
|
||||
import {ɵɵCopyDefinitionFeature} from './features/copy_definition_feature';
|
||||
import {ɵɵInheritDefinitionFeature} from './features/inherit_definition_feature';
|
||||
|
||||
@@ -44,13 +44,11 @@ export function createNgModule<T>(
|
||||
* @deprecated Use `createNgModule` instead.
|
||||
*/
|
||||
export const createNgModuleRef = createNgModule;
|
||||
export class NgModuleRef<T> extends viewEngine_NgModuleRef<T> implements InternalNgModuleRef<T>,
|
||||
EnvironmentInjector {
|
||||
export class NgModuleRef<T> extends viewEngine_NgModuleRef<T> implements InternalNgModuleRef<T> {
|
||||
// tslint:disable-next-line:require-internal-with-underscore
|
||||
_bootstrapComponents: Type<any>[] = [];
|
||||
// tslint:disable-next-line:require-internal-with-underscore
|
||||
_r3Injector: R3Injector;
|
||||
override injector: EnvironmentInjector = this;
|
||||
override instance: T;
|
||||
destroyCbs: (() => void)[]|null = [];
|
||||
|
||||
@@ -86,19 +84,11 @@ export class NgModuleRef<T> extends viewEngine_NgModuleRef<T> implements Interna
|
||||
// the module might be trying to use this ref in its constructor for DI which will cause a
|
||||
// circular error that will eventually error out, because the injector isn't created yet.
|
||||
this._r3Injector.resolveInjectorInitializers();
|
||||
this.instance = this.get(ngModuleType);
|
||||
this.instance = this._r3Injector.get(ngModuleType);
|
||||
}
|
||||
|
||||
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND,
|
||||
injectFlags: InjectFlags = InjectFlags.Default): any {
|
||||
if (token === Injector || token === viewEngine_NgModuleRef || token === INJECTOR) {
|
||||
return this;
|
||||
}
|
||||
return this._r3Injector.get(token, notFoundValue, injectFlags);
|
||||
}
|
||||
|
||||
runInContext<ReturnT>(fn: () => ReturnT): ReturnT {
|
||||
return this.injector.runInContext(fn);
|
||||
override get injector(): EnvironmentInjector {
|
||||
return this._r3Injector;
|
||||
}
|
||||
|
||||
override destroy(): void {
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
*/
|
||||
|
||||
import {DOCUMENT} from '@angular/common';
|
||||
import {ApplicationRef, Component, ComponentFactoryResolver, ComponentRef, ElementRef, InjectionToken, Injector, Input, NgModule, OnDestroy, Renderer2, RendererFactory2, Type, ViewChild, ViewContainerRef, ViewEncapsulation, ɵsetDocument} from '@angular/core';
|
||||
import {ApplicationRef, Component, ComponentFactoryResolver, ComponentRef, createComponent, createEnvironmentInjector, Directive, ElementRef, EmbeddedViewRef, EnvironmentInjector, inject, Injectable, InjectionToken, Injector, Input, NgModule, OnDestroy, reflectComponentType, Renderer2, Type, ViewChild, ViewContainerRef, ViewEncapsulation, ɵsetDocument} from '@angular/core';
|
||||
import {stringifyForError} from '@angular/core/src/render3/util/stringify_utils';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
|
||||
@@ -639,4 +640,317 @@ describe('component', () => {
|
||||
expect(targetEl.innerHTML).not.toContain('Existing content in slot B');
|
||||
expect(targetEl.innerHTML).toContain('DynamicComponent Content');
|
||||
});
|
||||
|
||||
describe('createComponent', () => {
|
||||
it('should create an instance of a standalone component', () => {
|
||||
@Component({
|
||||
standalone: true,
|
||||
template: 'Hello {{ name }}!',
|
||||
})
|
||||
class StandaloneComponent {
|
||||
name = 'Angular';
|
||||
}
|
||||
|
||||
const hostElement = document.createElement('div');
|
||||
const environmentInjector = TestBed.inject(EnvironmentInjector);
|
||||
const componentRef = createComponent(StandaloneComponent, {hostElement, environmentInjector});
|
||||
|
||||
componentRef.changeDetectorRef.detectChanges();
|
||||
expect(hostElement.textContent).toBe('Hello Angular!');
|
||||
|
||||
// Verify basic change detection works.
|
||||
componentRef.instance.name = 'ZoneJS';
|
||||
componentRef.changeDetectorRef.detectChanges();
|
||||
expect(hostElement.textContent).toBe('Hello ZoneJS!');
|
||||
});
|
||||
|
||||
it('should create an instance of an NgModule-based component', () => {
|
||||
@Component({
|
||||
template: 'Hello {{ name }}!',
|
||||
})
|
||||
class NgModuleBasedComponent {
|
||||
name = 'Angular';
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [NgModuleBasedComponent],
|
||||
})
|
||||
class AppModule {
|
||||
}
|
||||
|
||||
const hostElement = document.createElement('div');
|
||||
const environmentInjector = TestBed.inject(EnvironmentInjector);
|
||||
const componentRef =
|
||||
createComponent(NgModuleBasedComponent, {hostElement, environmentInjector});
|
||||
|
||||
componentRef.changeDetectorRef.detectChanges();
|
||||
expect(hostElement.textContent).toBe('Hello Angular!');
|
||||
|
||||
// Verify basic change detection works.
|
||||
componentRef.instance.name = 'ZoneJS';
|
||||
componentRef.changeDetectorRef.detectChanges();
|
||||
expect(hostElement.textContent).toBe('Hello ZoneJS!');
|
||||
});
|
||||
|
||||
it('should render projected content', () => {
|
||||
@Component({
|
||||
standalone: true,
|
||||
template: `
|
||||
<ng-content></ng-content>|
|
||||
<ng-content></ng-content>|
|
||||
<ng-content></ng-content>
|
||||
`,
|
||||
})
|
||||
class StandaloneComponent {
|
||||
}
|
||||
|
||||
// Helper method to create a `<p>` element
|
||||
const p = (content: string): Element => {
|
||||
const element = document.createElement('p');
|
||||
element.innerHTML = content;
|
||||
return element;
|
||||
};
|
||||
const hostElement = document.createElement('div');
|
||||
const environmentInjector = TestBed.inject(EnvironmentInjector);
|
||||
const projectableNodes = [[p('1')], [p('2')], [p('3')]];
|
||||
const componentRef = createComponent(
|
||||
StandaloneComponent, {hostElement, environmentInjector, projectableNodes});
|
||||
|
||||
componentRef.changeDetectorRef.detectChanges();
|
||||
expect(hostElement.innerHTML.replace(/\s*/g, '')).toBe('<p>1</p>|<p>2</p>|<p>3</p>');
|
||||
});
|
||||
|
||||
it('should be able to inject tokens from EnvironmentInjector', () => {
|
||||
const A = new InjectionToken('A');
|
||||
@Component({
|
||||
standalone: true,
|
||||
template: 'Token: {{ a }}',
|
||||
})
|
||||
class StandaloneComponent {
|
||||
a = inject(A);
|
||||
}
|
||||
|
||||
const hostElement = document.createElement('div');
|
||||
const parentInjector = TestBed.inject(EnvironmentInjector);
|
||||
const providers = [{provide: A, useValue: 'EnvironmentInjector(A)'}];
|
||||
const environmentInjector = createEnvironmentInjector(providers, parentInjector);
|
||||
const componentRef = createComponent(StandaloneComponent, {hostElement, environmentInjector});
|
||||
componentRef.changeDetectorRef.detectChanges();
|
||||
|
||||
expect(hostElement.textContent).toBe('Token: EnvironmentInjector(A)');
|
||||
});
|
||||
|
||||
it('should be able to use NodeInjector from the node hierarchy', () => {
|
||||
const A = new InjectionToken('A');
|
||||
const B = new InjectionToken('B');
|
||||
@Component({
|
||||
standalone: true,
|
||||
template: '{{ a }} and {{ b }}',
|
||||
})
|
||||
class ChildStandaloneComponent {
|
||||
a = inject(A);
|
||||
b = inject(B);
|
||||
}
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
template: 'Tokens: <div #target></div>',
|
||||
providers: [{provide: A, useValue: 'ElementInjector(A)'}]
|
||||
})
|
||||
class RootStandaloneComponent {
|
||||
@ViewChild('target', {read: ElementRef}) target!: ElementRef;
|
||||
constructor(private injector: Injector) {}
|
||||
|
||||
createChildComponent() {
|
||||
const hostElement = this.target.nativeElement;
|
||||
const parentInjector = this.injector.get(EnvironmentInjector);
|
||||
const providers = [
|
||||
{provide: A, useValue: 'EnvironmentInjector(A)'},
|
||||
{provide: B, useValue: 'EnvironmentInjector(B)'}
|
||||
];
|
||||
const environmentInjector = createEnvironmentInjector(providers, parentInjector);
|
||||
const childComponentRef = createComponent(
|
||||
ChildStandaloneComponent,
|
||||
{hostElement, elementInjector: this.injector, environmentInjector});
|
||||
childComponentRef.changeDetectorRef.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
const fixture = TestBed.createComponent(RootStandaloneComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.createChildComponent();
|
||||
|
||||
const rootEl = fixture.nativeElement;
|
||||
|
||||
// Token A is coming from the Element Injector, token B - from the Environment Injector.
|
||||
expect(rootEl.textContent).toBe('Tokens: ElementInjector(A) and EnvironmentInjector(B)');
|
||||
});
|
||||
|
||||
it('should create a host element if none provided', () => {
|
||||
const selector = 'standalone-comp';
|
||||
@Component({
|
||||
selector,
|
||||
standalone: true,
|
||||
template: 'Hello {{ name }}!',
|
||||
})
|
||||
class StandaloneComponent {
|
||||
name = 'Angular';
|
||||
}
|
||||
|
||||
const environmentInjector = TestBed.inject(EnvironmentInjector);
|
||||
const componentRef = createComponent(StandaloneComponent, {environmentInjector});
|
||||
componentRef.changeDetectorRef.detectChanges();
|
||||
|
||||
const hostElement =
|
||||
(componentRef.hostView as EmbeddedViewRef<StandaloneComponent>).rootNodes[0];
|
||||
|
||||
// A host element that matches component's selector.
|
||||
expect(hostElement.tagName.toLowerCase()).toBe(selector);
|
||||
|
||||
expect(hostElement.textContent).toBe('Hello Angular!');
|
||||
});
|
||||
|
||||
it('should fall-back to use a `div` as a host element if none provided ' +
|
||||
'and element selector does not have a tag name',
|
||||
() => {
|
||||
@Component({
|
||||
selector: '.some-class',
|
||||
standalone: true,
|
||||
template: 'Hello {{ name }}!',
|
||||
})
|
||||
class StandaloneComponent {
|
||||
name = 'Angular';
|
||||
}
|
||||
|
||||
const environmentInjector = TestBed.inject(EnvironmentInjector);
|
||||
const componentRef = createComponent(StandaloneComponent, {environmentInjector});
|
||||
componentRef.changeDetectorRef.detectChanges();
|
||||
|
||||
const hostElement =
|
||||
(componentRef.hostView as EmbeddedViewRef<StandaloneComponent>).rootNodes[0];
|
||||
|
||||
// A host element has the `div` tag name, since component's selector doesn't contain
|
||||
// tag name information (only a class name).
|
||||
expect(hostElement.tagName.toLowerCase()).toBe('div');
|
||||
|
||||
expect(hostElement.textContent).toBe('Hello Angular!');
|
||||
});
|
||||
|
||||
describe('error checking', () => {
|
||||
it('should throw when provided class is not a component', () => {
|
||||
class NotAComponent {}
|
||||
|
||||
@Directive()
|
||||
class ADirective {
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class AnInjectiable {
|
||||
}
|
||||
|
||||
const errorFor = (type: Type<unknown>): string =>
|
||||
`NG0906: The ${stringifyForError(type)} is not an Angular component, ` +
|
||||
`make sure it has the \`@Component\` decorator.`;
|
||||
const hostElement = document.createElement('div');
|
||||
const environmentInjector = TestBed.inject(EnvironmentInjector);
|
||||
|
||||
expect(() => createComponent(NotAComponent, {hostElement, environmentInjector}))
|
||||
.toThrowError(errorFor(NotAComponent));
|
||||
|
||||
expect(() => createComponent(ADirective, {hostElement, environmentInjector}))
|
||||
.toThrowError(errorFor(ADirective));
|
||||
|
||||
expect(() => createComponent(AnInjectiable, {hostElement, environmentInjector}))
|
||||
.toThrowError(errorFor(AnInjectiable));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('reflectComponentType', () => {
|
||||
it('should create an ComponentMirror for a standalone component', () => {
|
||||
@Component({
|
||||
selector: 'standalone-component',
|
||||
standalone: true,
|
||||
template: `
|
||||
<ng-content></ng-content>
|
||||
<ng-content select="content-selector-a"></ng-content>
|
||||
<ng-content select="content-selector-b"></ng-content>
|
||||
<ng-content></ng-content>
|
||||
`,
|
||||
inputs: ['input-a', 'input-b:input-alias-b'],
|
||||
outputs: ['output-a', 'output-b:output-alias-b'],
|
||||
})
|
||||
class StandaloneComponent {
|
||||
}
|
||||
|
||||
const mirror = reflectComponentType(StandaloneComponent)!;
|
||||
|
||||
expect(mirror.selector).toBe('standalone-component');
|
||||
expect(mirror.type).toBe(StandaloneComponent);
|
||||
expect(mirror.isStandalone).toEqual(true);
|
||||
expect(mirror.inputs).toEqual([
|
||||
{propName: 'input-a', templateName: 'input-a'},
|
||||
{propName: 'input-b', templateName: 'input-alias-b'}
|
||||
]);
|
||||
expect(mirror.outputs).toEqual([
|
||||
{propName: 'output-a', templateName: 'output-a'},
|
||||
{propName: 'output-b', templateName: 'output-alias-b'}
|
||||
]);
|
||||
expect(mirror.ngContentSelectors).toEqual([
|
||||
'*', 'content-selector-a', 'content-selector-b', '*'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should create an ComponentMirror for a non-standalone component', () => {
|
||||
@Component({
|
||||
selector: 'non-standalone-component',
|
||||
template: `
|
||||
<ng-content></ng-content>
|
||||
<ng-content select="content-selector-a"></ng-content>
|
||||
<ng-content select="content-selector-b"></ng-content>
|
||||
<ng-content></ng-content>
|
||||
`,
|
||||
inputs: ['input-a', 'input-b:input-alias-b'],
|
||||
outputs: ['output-a', 'output-b:output-alias-b'],
|
||||
})
|
||||
class NonStandaloneComponent {
|
||||
}
|
||||
|
||||
const mirror = reflectComponentType(NonStandaloneComponent)!;
|
||||
|
||||
expect(mirror.selector).toBe('non-standalone-component');
|
||||
expect(mirror.type).toBe(NonStandaloneComponent);
|
||||
expect(mirror.isStandalone).toEqual(false);
|
||||
expect(mirror.inputs).toEqual([
|
||||
{propName: 'input-a', templateName: 'input-a'},
|
||||
{propName: 'input-b', templateName: 'input-alias-b'}
|
||||
]);
|
||||
expect(mirror.outputs).toEqual([
|
||||
{propName: 'output-a', templateName: 'output-a'},
|
||||
{propName: 'output-b', templateName: 'output-alias-b'}
|
||||
]);
|
||||
expect(mirror.ngContentSelectors).toEqual([
|
||||
'*', 'content-selector-a', 'content-selector-b', '*'
|
||||
]);
|
||||
});
|
||||
|
||||
describe('error checking', () => {
|
||||
it('should throw when provided class is not a component', () => {
|
||||
class NotAnnotated {}
|
||||
|
||||
@Directive()
|
||||
class ADirective {
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class AnInjectiable {
|
||||
}
|
||||
|
||||
expect(reflectComponentType(NotAnnotated)).toBe(null);
|
||||
expect(reflectComponentType(ADirective)).toBe(null);
|
||||
expect(reflectComponentType(AnInjectiable)).toBe(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -96,7 +96,7 @@ describe('environment injector', () => {
|
||||
expect(cRef.instance.service).toBeInstanceOf(Service);
|
||||
});
|
||||
|
||||
it('should support the ENVIRONMENT_INITIALIZER muli-token', () => {
|
||||
it('should support the ENVIRONMENT_INITIALIZER multi-token', () => {
|
||||
let initialized = false;
|
||||
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
|
||||
createEnvironmentInjector(
|
||||
@@ -110,6 +110,20 @@ describe('environment injector', () => {
|
||||
expect(initialized).toBeTrue();
|
||||
});
|
||||
|
||||
it('should throw when the ENVIRONMENT_INITIALIZER is not a multi-token', () => {
|
||||
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
|
||||
const providers = [{
|
||||
provide: ENVIRONMENT_INITIALIZER,
|
||||
useValue: () => {},
|
||||
}];
|
||||
expect(() => createEnvironmentInjector(providers, parentEnvInjector))
|
||||
.toThrowError(
|
||||
'NG0209: Unexpected type of the `ENVIRONMENT_INITIALIZER` token value ' +
|
||||
'(expected an array, but got function). ' +
|
||||
'Please check that the `ENVIRONMENT_INITIALIZER` token is configured as ' +
|
||||
'a `multi: true` provider.');
|
||||
});
|
||||
|
||||
it('should adopt environment-scoped providers', () => {
|
||||
const parentEnvInjector = TestBed.inject(EnvironmentInjector);
|
||||
const injector = createEnvironmentInjector([], parentEnvInjector);
|
||||
@@ -127,6 +141,19 @@ describe('environment injector', () => {
|
||||
expect(returnValue).toBe(3);
|
||||
});
|
||||
|
||||
it('should work with an NgModuleRef injector', () => {
|
||||
const ref = TestBed.inject(NgModuleRef);
|
||||
const returnValue = ref.injector.runInContext(() => 3);
|
||||
expect(returnValue).toBe(3);
|
||||
});
|
||||
|
||||
it('should return correct injector reference', () => {
|
||||
const ngModuleRef = TestBed.inject(NgModuleRef);
|
||||
const ref1 = ngModuleRef.injector.runInContext(() => inject(Injector));
|
||||
const ref2 = ngModuleRef.injector.get(Injector);
|
||||
expect(ref1).toBe(ref2);
|
||||
});
|
||||
|
||||
it('should make inject() available', () => {
|
||||
const TOKEN = new InjectionToken<string>('TOKEN');
|
||||
const injector = createEnvironmentInjector(
|
||||
|
||||
@@ -3981,6 +3981,45 @@ describe('animation tests', function() {
|
||||
expect(pcp.duration).toEqual(7333); // 2s + 3s + 2000 + 111 + 222
|
||||
});
|
||||
|
||||
it('should keep (transition from/to) styles defined in different timelines', () => {
|
||||
@Component({
|
||||
selector: 'cmp',
|
||||
template: '<div @animation *ngIf="exp"></div>',
|
||||
animations: [trigger(
|
||||
'animation',
|
||||
[transition(':enter', [group([
|
||||
style({opacity: 0, color: 'red'}),
|
||||
// Note: the objective of this test is to make sure the animation
|
||||
// transitions from opacity 0 and color red to opacity 1 and color blue,
|
||||
// even though the two styles are defined in different timelines
|
||||
animate(500, style({opacity: 1, color: 'blue'})),
|
||||
])])])]
|
||||
})
|
||||
class Cmp {
|
||||
exp: boolean = false;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.exp = true;
|
||||
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
const players = getLog();
|
||||
expect(players.length).toEqual(1);
|
||||
|
||||
const [player] = players;
|
||||
|
||||
expect(player.keyframes).toEqual([
|
||||
new Map<string, string|number>([['opacity', '0'], ['color', 'red'], ['offset', 0]]),
|
||||
new Map<string, string|number>([['opacity', '1'], ['color', 'blue'], ['offset', 1]]),
|
||||
]);
|
||||
});
|
||||
|
||||
describe('errors for not using the animation module', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
|
||||
@@ -350,9 +350,6 @@
|
||||
{
|
||||
"name": "createPlatformFactory"
|
||||
},
|
||||
{
|
||||
"name": "createRootComponentView"
|
||||
},
|
||||
{
|
||||
"name": "createTView"
|
||||
},
|
||||
|
||||
@@ -416,9 +416,6 @@
|
||||
{
|
||||
"name": "createNodeInjector"
|
||||
},
|
||||
{
|
||||
"name": "createRootComponentView"
|
||||
},
|
||||
{
|
||||
"name": "createTView"
|
||||
},
|
||||
|
||||
@@ -65,161 +65,157 @@ describe('jit source mapping', () => {
|
||||
function declareTests({ngUrl, templateDecorator}: TestConfig) {
|
||||
const generatedUrl = 'ng:///MyComp.js';
|
||||
|
||||
it('should use the right source url in html parse errors', fakeAsync(() => {
|
||||
const template = '<div>\n </error>';
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
}
|
||||
it('should use the right source url in html parse errors', async () => {
|
||||
const template = '<div>\n </error>';
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
resolveCompileAndCreateComponent(MyComp, template);
|
||||
}).toThrowError(new RegExp(`${escapeRegExp(ngUrl)}@1:2`));
|
||||
}));
|
||||
await expectAsync(resolveCompileAndCreateComponent(MyComp, template))
|
||||
.toBeRejectedWithError(new RegExp(`${escapeRegExp(ngUrl)}@1:2`));
|
||||
});
|
||||
|
||||
it('should create a sourceMap for templates', fakeAsync(() => {
|
||||
const template = `Hello World!`;
|
||||
it('should create a sourceMap for templates', async () => {
|
||||
const template = `Hello World!`;
|
||||
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
}
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
}
|
||||
|
||||
resolveCompileAndCreateComponent(MyComp, template);
|
||||
await resolveCompileAndCreateComponent(MyComp, template);
|
||||
|
||||
const sourceMap = jitEvaluator.getSourceMap(generatedUrl);
|
||||
expect(sourceMap.sources).toEqual([generatedUrl, ngUrl]);
|
||||
expect(sourceMap.sourcesContent).toEqual([' ', template]);
|
||||
}));
|
||||
const sourceMap = jitEvaluator.getSourceMap(generatedUrl);
|
||||
expect(sourceMap.sources).toEqual([generatedUrl, ngUrl]);
|
||||
expect(sourceMap.sourcesContent).toEqual([' ', template]);
|
||||
});
|
||||
|
||||
|
||||
it('should report source location for di errors', fakeAsync(() => {
|
||||
const template = `<div>\n <div someDir></div></div>`;
|
||||
it('should report source location for di errors', async () => {
|
||||
const template = `<div>\n <div someDir></div></div>`;
|
||||
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
}
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
}
|
||||
|
||||
@Directive({selector: '[someDir]'})
|
||||
class SomeDir {
|
||||
constructor() {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
@Directive({selector: '[someDir]'})
|
||||
class SomeDir {
|
||||
constructor() {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [SomeDir]});
|
||||
let error: any;
|
||||
try {
|
||||
resolveCompileAndCreateComponent(MyComp, template);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
// The error should be logged from the element
|
||||
expect(jitEvaluator.getSourcePositionForStack(error.stack, generatedUrl)).toEqual({
|
||||
line: 2,
|
||||
column: 4,
|
||||
source: ngUrl,
|
||||
});
|
||||
}));
|
||||
TestBed.configureTestingModule({declarations: [SomeDir]});
|
||||
let error: any;
|
||||
try {
|
||||
await resolveCompileAndCreateComponent(MyComp, template);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
// The error should be logged from the element
|
||||
expect(await jitEvaluator.getSourcePositionForStack(error.stack, generatedUrl)).toEqual({
|
||||
line: 2,
|
||||
column: 4,
|
||||
source: ngUrl,
|
||||
});
|
||||
});
|
||||
|
||||
it('should report di errors with multiple elements and directives', fakeAsync(() => {
|
||||
const template = `<div someDir></div>|<div someDir="throw"></div>`;
|
||||
it('should report di errors with multiple elements and directives', async () => {
|
||||
const template = `<div someDir></div>|<div someDir="throw"></div>`;
|
||||
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
}
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
}
|
||||
|
||||
@Directive({selector: '[someDir]'})
|
||||
class SomeDir {
|
||||
constructor(@Attribute('someDir') someDir: string) {
|
||||
if (someDir === 'throw') {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
}
|
||||
@Directive({selector: '[someDir]'})
|
||||
class SomeDir {
|
||||
constructor(@Attribute('someDir') someDir: string) {
|
||||
if (someDir === 'throw') {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [SomeDir]});
|
||||
let error: any;
|
||||
try {
|
||||
resolveCompileAndCreateComponent(MyComp, template);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
// The error should be logged from the 2nd-element
|
||||
expect(jitEvaluator.getSourcePositionForStack(error.stack, generatedUrl)).toEqual({
|
||||
line: 1,
|
||||
column: 20,
|
||||
source: ngUrl,
|
||||
});
|
||||
}));
|
||||
TestBed.configureTestingModule({declarations: [SomeDir]});
|
||||
let error: any;
|
||||
try {
|
||||
await resolveCompileAndCreateComponent(MyComp, template);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
// The error should be logged from the 2nd-element
|
||||
expect(await jitEvaluator.getSourcePositionForStack(error.stack, generatedUrl)).toEqual({
|
||||
line: 1,
|
||||
column: 20,
|
||||
source: ngUrl,
|
||||
});
|
||||
});
|
||||
|
||||
it('should report source location for binding errors', fakeAsync(() => {
|
||||
const template = `<div>\n <span [title]="createError()"></span></div>`;
|
||||
it('should report source location for binding errors', async () => {
|
||||
const template = `<div>\n <span [title]="createError()"></span></div>`;
|
||||
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
createError() {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
createError() {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
|
||||
const comp = resolveCompileAndCreateComponent(MyComp, template);
|
||||
const comp = await resolveCompileAndCreateComponent(MyComp, template);
|
||||
|
||||
let error: any;
|
||||
try {
|
||||
comp.detectChanges();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
// the stack should point to the binding
|
||||
expect(jitEvaluator.getSourcePositionForStack(error.stack, generatedUrl)).toEqual({
|
||||
line: 2,
|
||||
column: 12,
|
||||
source: ngUrl,
|
||||
});
|
||||
}));
|
||||
let error: any;
|
||||
try {
|
||||
comp.detectChanges();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
// the stack should point to the binding
|
||||
expect(await jitEvaluator.getSourcePositionForStack(error.stack, generatedUrl)).toEqual({
|
||||
line: 2,
|
||||
column: 12,
|
||||
source: ngUrl,
|
||||
});
|
||||
});
|
||||
|
||||
it('should report source location for event errors', fakeAsync(() => {
|
||||
const template = `<div>\n <span (click)="createError()"></span></div>`;
|
||||
it('should report source location for event errors', async () => {
|
||||
const template = `<div>\n <span (click)="createError()"></span></div>`;
|
||||
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
createError() {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
createError() {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
|
||||
const comp = resolveCompileAndCreateComponent(MyComp, template);
|
||||
const comp = await resolveCompileAndCreateComponent(MyComp, template);
|
||||
|
||||
let error: any;
|
||||
const errorHandler = TestBed.inject(ErrorHandler);
|
||||
spyOn(errorHandler, 'handleError').and.callFake((e: any) => error = e);
|
||||
try {
|
||||
comp.debugElement.children[0].children[0].triggerEventHandler('click', 'EVENT');
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeTruthy();
|
||||
// the stack should point to the binding
|
||||
expect(jitEvaluator.getSourcePositionForStack(error.stack, generatedUrl)).toEqual({
|
||||
line: 2,
|
||||
column: 21,
|
||||
source: ngUrl,
|
||||
});
|
||||
}));
|
||||
let error: any;
|
||||
const errorHandler = TestBed.inject(ErrorHandler);
|
||||
spyOn(errorHandler, 'handleError').and.callFake((e: any) => error = e);
|
||||
try {
|
||||
comp.debugElement.children[0].children[0].triggerEventHandler('click', 'EVENT');
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeTruthy();
|
||||
// the stack should point to the binding
|
||||
expect(await jitEvaluator.getSourcePositionForStack(error.stack, generatedUrl)).toEqual({
|
||||
line: 2,
|
||||
column: 21,
|
||||
source: ngUrl,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function compileAndCreateComponent(comType: any) {
|
||||
async function compileAndCreateComponent(comType: any) {
|
||||
TestBed.configureTestingModule({declarations: [comType]});
|
||||
|
||||
let error: any;
|
||||
TestBed.compileComponents().catch((e) => error = e);
|
||||
await TestBed.compileComponents();
|
||||
|
||||
if (resourceLoader.hasPendingRequests()) {
|
||||
resourceLoader.flush();
|
||||
}
|
||||
tick();
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return TestBed.createComponent(comType);
|
||||
}
|
||||
|
||||
@@ -227,9 +223,9 @@ describe('jit source mapping', () => {
|
||||
return (_url: string) => Promise.resolve(contents);
|
||||
}
|
||||
|
||||
function resolveCompileAndCreateComponent(comType: any, template: string) {
|
||||
resolveComponentResources(createResolver(template));
|
||||
return compileAndCreateComponent(comType);
|
||||
async function resolveCompileAndCreateComponent(comType: any, template: string) {
|
||||
await resolveComponentResources(createResolver(template));
|
||||
return await compileAndCreateComponent(comType);
|
||||
}
|
||||
|
||||
let ɵcompilerFacade: CompilerFacade;
|
||||
@@ -255,9 +251,9 @@ describe('jit source mapping', () => {
|
||||
}
|
||||
|
||||
interface SourcePos {
|
||||
source: string;
|
||||
line: number;
|
||||
column: number;
|
||||
source: string|null;
|
||||
line: number|null;
|
||||
column: number|null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,7 +278,7 @@ describe('jit source mapping', () => {
|
||||
.find(map => !!(map && map.file === genFile))!;
|
||||
}
|
||||
|
||||
getSourcePositionForStack(stack: string, genFile: string): SourcePos {
|
||||
async getSourcePositionForStack(stack: string, genFile: string): Promise<SourcePos> {
|
||||
const urlRegexp = new RegExp(`(${escapeRegExp(genFile)}):(\\d+):(\\d+)`);
|
||||
const pos = stack.split('\n')
|
||||
.map(line => urlRegexp.exec(line))
|
||||
@@ -297,7 +293,7 @@ describe('jit source mapping', () => {
|
||||
throw new Error(`${genFile} was not mentioned in this stack:\n${stack}`);
|
||||
}
|
||||
const sourceMap = this.getSourceMap(pos.file);
|
||||
return originalPositionFor(sourceMap, pos);
|
||||
return await originalPositionFor(sourceMap, pos);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11,16 +11,16 @@ import b64 from 'base64-js';
|
||||
import {SourceMapConsumer} from 'source-map';
|
||||
|
||||
export interface SourceLocation {
|
||||
line: number;
|
||||
column: number;
|
||||
source: string;
|
||||
line: number|null;
|
||||
column: number|null;
|
||||
source: string|null;
|
||||
}
|
||||
|
||||
export function originalPositionFor(
|
||||
sourceMap: SourceMap, genPosition: {line: number, column: number}): SourceLocation {
|
||||
export async function originalPositionFor(
|
||||
sourceMap: SourceMap, genPosition: {line: number, column: number}): Promise<SourceLocation> {
|
||||
// Note: The `SourceMap` type from the compiler is different to `RawSourceMap`
|
||||
// from the `source-map` package, but the method we rely on works as expected.
|
||||
const smc = new SourceMapConsumer(sourceMap as any);
|
||||
const smc = await new SourceMapConsumer(sourceMap as any);
|
||||
// Note: We don't return the original object as it also contains a `name` property
|
||||
// which is always null and we don't want to include that in our assertions...
|
||||
const {line, column, source} = smc.originalPositionFor(genPosition);
|
||||
|
||||
@@ -6,17 +6,15 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CommonModule, DOCUMENT} from '@angular/common';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {withBody} from '@angular/private/testing';
|
||||
|
||||
import {Component, DoCheck, OnInit, RendererFactory2} from '../../src/core';
|
||||
import {whenRendered} from '../../src/render3/component';
|
||||
import {whenRendered} from '../../src/render3/component_ref';
|
||||
import {getRenderedText} from '../../src/render3/index';
|
||||
import {detectChanges, markDirty} from '../../src/render3/instructions/all';
|
||||
|
||||
import {MockRendererFactory} from './instructions/mock_renderer_factory';
|
||||
|
||||
describe('change detection', () => {
|
||||
describe('markDirty, detectChanges, whenRendered, getRenderedText', () => {
|
||||
let mycompOninit: MyComponentWithOnInit;
|
||||
|
||||
@@ -44,7 +44,7 @@ describe('testing', () => {
|
||||
describe('requestAnimationFrame', () => {
|
||||
it('should have requestAnimationFrame', (done) => {
|
||||
// In Browser we have requestAnimationFrame, but verify that we also have it node.js
|
||||
requestAnimationFrame(done);
|
||||
requestAnimationFrame(() => done());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -96,7 +96,7 @@ export interface TestEnvironmentOptions {
|
||||
* @publicApi
|
||||
*/
|
||||
export interface ModuleTeardownOptions {
|
||||
/** Whether the test module should be destroyed after every test. */
|
||||
/** Whether the test module should be destroyed after every test. Defaults to `true`. */
|
||||
destroyAfterEach: boolean;
|
||||
|
||||
/** Whether errors during test module destruction should be re-thrown. Defaults to `true`. */
|
||||
|
||||
@@ -190,27 +190,16 @@ export class FormBuilder {
|
||||
any}|null = null):
|
||||
FormGroup {
|
||||
const reducedControls = this._reduceControls(controls);
|
||||
|
||||
let validators: ValidatorFn|ValidatorFn[]|null = null;
|
||||
let asyncValidators: AsyncValidatorFn|AsyncValidatorFn[]|null = null;
|
||||
let updateOn: FormHooks|undefined = undefined;
|
||||
|
||||
if (options !== null) {
|
||||
if (isAbstractControlOptions(options)) {
|
||||
// `options` are `AbstractControlOptions`
|
||||
validators = options.validators != null ? options.validators : null;
|
||||
asyncValidators = options.asyncValidators != null ? options.asyncValidators : null;
|
||||
updateOn = options.updateOn != null ? options.updateOn : undefined;
|
||||
} else {
|
||||
// `options` are legacy form group options
|
||||
validators = (options as any)['validator'] != null ? (options as any)['validator'] : null;
|
||||
asyncValidators =
|
||||
(options as any)['asyncValidator'] != null ? (options as any)['asyncValidator'] : null;
|
||||
}
|
||||
let newOptions: FormControlOptions = {};
|
||||
if (isAbstractControlOptions(options)) {
|
||||
// `options` are `AbstractControlOptions`
|
||||
newOptions = options;
|
||||
} else if (options !== null) {
|
||||
// `options` are legacy form group options
|
||||
newOptions.validators = (options as any).validator;
|
||||
newOptions.asyncValidators = (options as any).asyncValidator;
|
||||
}
|
||||
|
||||
// Cast to `any` because the inferred types are not as specific as Element.
|
||||
return new FormGroup(reducedControls, {asyncValidators, updateOn, validators}) as any;
|
||||
return new FormGroup(reducedControls, newOptions);
|
||||
}
|
||||
|
||||
/** @deprecated Use `nonNullable` instead. */
|
||||
|
||||
@@ -156,6 +156,13 @@ describe('Form Builder', () => {
|
||||
expect(g.asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
|
||||
it('should create groups with null options', () => {
|
||||
const g = b.group({'login': 'some value'}, null);
|
||||
|
||||
expect(g.validator).toBe(null);
|
||||
expect(g.asyncValidator).toBe(null);
|
||||
});
|
||||
|
||||
it('should create control arrays', () => {
|
||||
const c = b.control('three');
|
||||
const e = b.control(null);
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"./fesm2020/init.mjs"
|
||||
],
|
||||
"dependencies": {
|
||||
"@babel/core": "7.18.6",
|
||||
"@babel/core": "7.18.9",
|
||||
"glob": "8.0.3",
|
||||
"yargs": "^17.2.1"
|
||||
},
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {EnvironmentInjector, Injector, Type} from '@angular/core';
|
||||
import {EnvironmentInjector, Type} from '@angular/core';
|
||||
import {MonoTypeOperatorFunction} from 'rxjs';
|
||||
import {map, mergeMap} from 'rxjs/operators';
|
||||
|
||||
import {Route} from '../models';
|
||||
import {recognize as recognizeFn} from '../recognize';
|
||||
import {NavigationTransition} from '../router';
|
||||
import {UrlSerializer, UrlTree} from '../url_tree';
|
||||
import {UrlSerializer} from '../url_tree';
|
||||
|
||||
export function recognize(
|
||||
injector: EnvironmentInjector, rootComponentType: Type<any>|null, config: Route[],
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
import {inject, Injectable} from '@angular/core';
|
||||
import {Title} from '@angular/platform-browser';
|
||||
|
||||
import {RouteTitle as TitleKey} from './operators/resolve_data';
|
||||
@@ -36,6 +36,7 @@ import {PRIMARY_OUTLET} from './shared';
|
||||
* @publicApi
|
||||
* @see [Page title guide](guide/router#setting-the-page-title)
|
||||
*/
|
||||
@Injectable({providedIn: 'root', useFactory: () => inject(DefaultTitleStrategy)})
|
||||
export abstract class TitleStrategy {
|
||||
/** Performs the application title update. */
|
||||
abstract updateTitle(snapshot: RouterStateSnapshot): void;
|
||||
|
||||
+344
-338
@@ -569,362 +569,368 @@ export class Router {
|
||||
NavigationTransition)),
|
||||
|
||||
// Using switchMap so we cancel executing navigations when a new one comes in
|
||||
switchMap(t => {
|
||||
switchMap(overallTransitionState => {
|
||||
let completed = false;
|
||||
let errored = false;
|
||||
return of(t).pipe(
|
||||
// Store the Navigation object
|
||||
tap(t => {
|
||||
this.currentNavigation = {
|
||||
id: t.id,
|
||||
initialUrl: t.rawUrl,
|
||||
extractedUrl: t.extractedUrl,
|
||||
trigger: t.source,
|
||||
extras: t.extras,
|
||||
previousNavigation: this.lastSuccessfulNavigation ?
|
||||
{...this.lastSuccessfulNavigation, previousNavigation: null} :
|
||||
null
|
||||
};
|
||||
}),
|
||||
switchMap(t => {
|
||||
const browserUrlTree = this.browserUrlTree.toString();
|
||||
const urlTransition = !this.navigated ||
|
||||
t.extractedUrl.toString() !== browserUrlTree ||
|
||||
// Navigations which succeed or ones which fail and are cleaned up
|
||||
// correctly should result in `browserUrlTree` and `currentUrlTree`
|
||||
// matching. If this is not the case, assume something went wrong and try
|
||||
// processing the URL again.
|
||||
browserUrlTree !== this.currentUrlTree.toString();
|
||||
const processCurrentUrl =
|
||||
(this.onSameUrlNavigation === 'reload' ? true : urlTransition) &&
|
||||
this.urlHandlingStrategy.shouldProcessUrl(t.rawUrl);
|
||||
return of(overallTransitionState)
|
||||
.pipe(
|
||||
// Store the Navigation object
|
||||
tap(t => {
|
||||
this.currentNavigation = {
|
||||
id: t.id,
|
||||
initialUrl: t.rawUrl,
|
||||
extractedUrl: t.extractedUrl,
|
||||
trigger: t.source,
|
||||
extras: t.extras,
|
||||
previousNavigation: this.lastSuccessfulNavigation ?
|
||||
{...this.lastSuccessfulNavigation, previousNavigation: null} :
|
||||
null
|
||||
};
|
||||
}),
|
||||
switchMap(t => {
|
||||
const browserUrlTree = this.browserUrlTree.toString();
|
||||
const urlTransition = !this.navigated ||
|
||||
t.extractedUrl.toString() !== browserUrlTree ||
|
||||
// Navigations which succeed or ones which fail and are cleaned up
|
||||
// correctly should result in `browserUrlTree` and `currentUrlTree`
|
||||
// matching. If this is not the case, assume something went wrong and
|
||||
// try processing the URL again.
|
||||
browserUrlTree !== this.currentUrlTree.toString();
|
||||
const processCurrentUrl =
|
||||
(this.onSameUrlNavigation === 'reload' ? true : urlTransition) &&
|
||||
this.urlHandlingStrategy.shouldProcessUrl(t.rawUrl);
|
||||
|
||||
|
||||
if (processCurrentUrl) {
|
||||
// If the source of the navigation is from a browser event, the URL is
|
||||
// already updated. We already need to sync the internal state.
|
||||
if (isBrowserTriggeredNavigation(t.source)) {
|
||||
this.browserUrlTree = t.extractedUrl;
|
||||
}
|
||||
return of(t).pipe(
|
||||
// Fire NavigationStart event
|
||||
switchMap(t => {
|
||||
const transition = this.transitions.getValue();
|
||||
eventsSubject.next(new NavigationStart(
|
||||
t.id, this.serializeUrl(t.extractedUrl), t.source,
|
||||
t.restoredState));
|
||||
if (transition !== this.transitions.getValue()) {
|
||||
return EMPTY;
|
||||
}
|
||||
if (processCurrentUrl) {
|
||||
// If the source of the navigation is from a browser event, the URL is
|
||||
// already updated. We already need to sync the internal state.
|
||||
if (isBrowserTriggeredNavigation(t.source)) {
|
||||
this.browserUrlTree = t.extractedUrl;
|
||||
}
|
||||
return of(t).pipe(
|
||||
// Fire NavigationStart event
|
||||
switchMap(t => {
|
||||
const transition = this.transitions.getValue();
|
||||
eventsSubject.next(new NavigationStart(
|
||||
t.id, this.serializeUrl(t.extractedUrl), t.source,
|
||||
t.restoredState));
|
||||
if (transition !== this.transitions.getValue()) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
// This delay is required to match old behavior that forced
|
||||
// navigation to always be async
|
||||
return Promise.resolve(t);
|
||||
}),
|
||||
// This delay is required to match old behavior that forced
|
||||
// navigation to always be async
|
||||
return Promise.resolve(t);
|
||||
}),
|
||||
|
||||
// ApplyRedirects
|
||||
applyRedirects(
|
||||
this.ngModule.injector, this.configLoader, this.urlSerializer,
|
||||
this.config),
|
||||
// ApplyRedirects
|
||||
applyRedirects(
|
||||
this.ngModule.injector, this.configLoader, this.urlSerializer,
|
||||
this.config),
|
||||
|
||||
// Update the currentNavigation
|
||||
// `urlAfterRedirects` is guaranteed to be set after this point
|
||||
tap(t => {
|
||||
this.currentNavigation = {
|
||||
...this.currentNavigation!,
|
||||
finalUrl: t.urlAfterRedirects
|
||||
};
|
||||
}),
|
||||
// Update the currentNavigation
|
||||
// `urlAfterRedirects` is guaranteed to be set after this point
|
||||
tap(t => {
|
||||
this.currentNavigation = {
|
||||
...this.currentNavigation!,
|
||||
finalUrl: t.urlAfterRedirects
|
||||
};
|
||||
overallTransitionState.urlAfterRedirects = t.urlAfterRedirects;
|
||||
}),
|
||||
|
||||
// Recognize
|
||||
recognize(
|
||||
this.ngModule.injector, this.rootComponentType, this.config,
|
||||
this.urlSerializer, this.paramsInheritanceStrategy,
|
||||
this.relativeLinkResolution),
|
||||
// Recognize
|
||||
recognize(
|
||||
this.ngModule.injector, this.rootComponentType, this.config,
|
||||
this.urlSerializer, this.paramsInheritanceStrategy,
|
||||
this.relativeLinkResolution),
|
||||
|
||||
// Update URL if in `eager` update mode
|
||||
tap(t => {
|
||||
if (this.urlUpdateStrategy === 'eager') {
|
||||
if (!t.extras.skipLocationChange) {
|
||||
const rawUrl = this.urlHandlingStrategy.merge(
|
||||
t.urlAfterRedirects!, t.rawUrl);
|
||||
this.setBrowserUrl(rawUrl, t);
|
||||
}
|
||||
this.browserUrlTree = t.urlAfterRedirects!;
|
||||
}
|
||||
|
||||
// Fire RoutesRecognized
|
||||
const routesRecognized = new RoutesRecognized(
|
||||
t.id, this.serializeUrl(t.extractedUrl),
|
||||
this.serializeUrl(t.urlAfterRedirects!), t.targetSnapshot!);
|
||||
eventsSubject.next(routesRecognized);
|
||||
}));
|
||||
} else {
|
||||
const processPreviousUrl = urlTransition && this.rawUrlTree &&
|
||||
this.urlHandlingStrategy.shouldProcessUrl(this.rawUrlTree);
|
||||
/* When the current URL shouldn't be processed, but the previous one was,
|
||||
* we handle this "error condition" by navigating to the previously
|
||||
* successful URL, but leaving the URL intact.*/
|
||||
if (processPreviousUrl) {
|
||||
const {id, extractedUrl, source, restoredState, extras} = t;
|
||||
const navStart = new NavigationStart(
|
||||
id, this.serializeUrl(extractedUrl), source, restoredState);
|
||||
eventsSubject.next(navStart);
|
||||
const targetSnapshot =
|
||||
createEmptyState(extractedUrl, this.rootComponentType).snapshot;
|
||||
|
||||
return of({
|
||||
...t,
|
||||
targetSnapshot,
|
||||
urlAfterRedirects: extractedUrl,
|
||||
extras: {...extras, skipLocationChange: false, replaceUrl: false},
|
||||
});
|
||||
} else {
|
||||
/* When neither the current or previous URL can be processed, do nothing
|
||||
* other than update router's internal reference to the current "settled"
|
||||
* URL. This way the next navigation will be coming from the current URL
|
||||
* in the browser.
|
||||
*/
|
||||
this.rawUrlTree = t.rawUrl;
|
||||
t.resolve(null);
|
||||
return EMPTY;
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
// --- GUARDS ---
|
||||
tap(t => {
|
||||
const guardsStart = new GuardsCheckStart(
|
||||
t.id, this.serializeUrl(t.extractedUrl),
|
||||
this.serializeUrl(t.urlAfterRedirects!), t.targetSnapshot!);
|
||||
this.triggerEvent(guardsStart);
|
||||
}),
|
||||
|
||||
map(t => ({
|
||||
...t,
|
||||
guards: getAllRouteGuards(
|
||||
t.targetSnapshot!, t.currentSnapshot, this.rootContexts)
|
||||
})),
|
||||
|
||||
checkGuards(this.ngModule.injector, (evt: Event) => this.triggerEvent(evt)),
|
||||
tap(t => {
|
||||
if (isUrlTree(t.guardsResult)) {
|
||||
throw redirectingNavigationError(this.urlSerializer, t.guardsResult);
|
||||
}
|
||||
|
||||
const guardsEnd = new GuardsCheckEnd(
|
||||
t.id, this.serializeUrl(t.extractedUrl),
|
||||
this.serializeUrl(t.urlAfterRedirects!), t.targetSnapshot!,
|
||||
!!t.guardsResult);
|
||||
this.triggerEvent(guardsEnd);
|
||||
}),
|
||||
|
||||
filter(t => {
|
||||
if (!t.guardsResult) {
|
||||
this.restoreHistory(t);
|
||||
this.cancelNavigationTransition(
|
||||
t, '', NavigationCancellationCode.GuardRejected);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
|
||||
// --- RESOLVE ---
|
||||
switchTap(t => {
|
||||
if (t.guards.canActivateChecks.length) {
|
||||
return of(t).pipe(
|
||||
tap(t => {
|
||||
const resolveStart = new ResolveStart(
|
||||
t.id, this.serializeUrl(t.extractedUrl),
|
||||
this.serializeUrl(t.urlAfterRedirects!), t.targetSnapshot!);
|
||||
this.triggerEvent(resolveStart);
|
||||
}),
|
||||
switchMap(t => {
|
||||
let dataResolved = false;
|
||||
return of(t).pipe(
|
||||
resolveData(
|
||||
this.paramsInheritanceStrategy, this.ngModule.injector),
|
||||
tap({
|
||||
next: () => dataResolved = true,
|
||||
complete: () => {
|
||||
if (!dataResolved) {
|
||||
this.restoreHistory(t);
|
||||
this.cancelNavigationTransition(
|
||||
t,
|
||||
NG_DEV_MODE ?
|
||||
`At least one route resolver didn't emit any value.` :
|
||||
'',
|
||||
NavigationCancellationCode.NoDataFromResolver);
|
||||
}
|
||||
// Update URL if in `eager` update mode
|
||||
tap(t => {
|
||||
overallTransitionState.targetSnapshot = t.targetSnapshot;
|
||||
if (this.urlUpdateStrategy === 'eager') {
|
||||
if (!t.extras.skipLocationChange) {
|
||||
const rawUrl = this.urlHandlingStrategy.merge(
|
||||
t.urlAfterRedirects!, t.rawUrl);
|
||||
this.setBrowserUrl(rawUrl, t);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}),
|
||||
tap(t => {
|
||||
const resolveEnd = new ResolveEnd(
|
||||
t.id, this.serializeUrl(t.extractedUrl),
|
||||
this.serializeUrl(t.urlAfterRedirects!), t.targetSnapshot!);
|
||||
this.triggerEvent(resolveEnd);
|
||||
}));
|
||||
}
|
||||
return undefined;
|
||||
}),
|
||||
this.browserUrlTree = t.urlAfterRedirects!;
|
||||
}
|
||||
|
||||
// --- LOAD COMPONENTS ---
|
||||
switchTap((t: NavigationTransition) => {
|
||||
const loadComponents =
|
||||
(route: ActivatedRouteSnapshot): Array<Observable<void>> => {
|
||||
const loaders: Array<Observable<void>> = [];
|
||||
if (route.routeConfig?.loadComponent &&
|
||||
!route.routeConfig._loadedComponent) {
|
||||
loaders.push(this.configLoader.loadComponent(route.routeConfig)
|
||||
.pipe(
|
||||
tap(loadedComponent => {
|
||||
route.component = loadedComponent;
|
||||
}),
|
||||
map(() => void 0),
|
||||
));
|
||||
// Fire RoutesRecognized
|
||||
const routesRecognized = new RoutesRecognized(
|
||||
t.id, this.serializeUrl(t.extractedUrl),
|
||||
this.serializeUrl(t.urlAfterRedirects!), t.targetSnapshot!);
|
||||
eventsSubject.next(routesRecognized);
|
||||
}));
|
||||
} else {
|
||||
const processPreviousUrl = urlTransition && this.rawUrlTree &&
|
||||
this.urlHandlingStrategy.shouldProcessUrl(this.rawUrlTree);
|
||||
/* When the current URL shouldn't be processed, but the previous one
|
||||
* was, we handle this "error condition" by navigating to the
|
||||
* previously successful URL, but leaving the URL intact.*/
|
||||
if (processPreviousUrl) {
|
||||
const {id, extractedUrl, source, restoredState, extras} = t;
|
||||
const navStart = new NavigationStart(
|
||||
id, this.serializeUrl(extractedUrl), source, restoredState);
|
||||
eventsSubject.next(navStart);
|
||||
const targetSnapshot =
|
||||
createEmptyState(extractedUrl, this.rootComponentType).snapshot;
|
||||
|
||||
overallTransitionState = {
|
||||
...t,
|
||||
targetSnapshot,
|
||||
urlAfterRedirects: extractedUrl,
|
||||
extras: {...extras, skipLocationChange: false, replaceUrl: false},
|
||||
};
|
||||
return of(overallTransitionState);
|
||||
} else {
|
||||
/* When neither the current or previous URL can be processed, do
|
||||
* nothing other than update router's internal reference to the
|
||||
* current "settled" URL. This way the next navigation will be coming
|
||||
* from the current URL in the browser.
|
||||
*/
|
||||
this.rawUrlTree = t.rawUrl;
|
||||
t.resolve(null);
|
||||
return EMPTY;
|
||||
}
|
||||
for (const child of route.children) {
|
||||
loaders.push(...loadComponents(child));
|
||||
}
|
||||
}),
|
||||
|
||||
// --- GUARDS ---
|
||||
tap(t => {
|
||||
const guardsStart = new GuardsCheckStart(
|
||||
t.id, this.serializeUrl(t.extractedUrl),
|
||||
this.serializeUrl(t.urlAfterRedirects!), t.targetSnapshot!);
|
||||
this.triggerEvent(guardsStart);
|
||||
}),
|
||||
|
||||
map(t => {
|
||||
overallTransitionState = {
|
||||
...t,
|
||||
guards: getAllRouteGuards(
|
||||
t.targetSnapshot!, t.currentSnapshot, this.rootContexts)
|
||||
};
|
||||
return overallTransitionState;
|
||||
}),
|
||||
|
||||
checkGuards(
|
||||
this.ngModule.injector, (evt: Event) => this.triggerEvent(evt)),
|
||||
tap(t => {
|
||||
overallTransitionState.guardsResult = t.guardsResult;
|
||||
if (isUrlTree(t.guardsResult)) {
|
||||
throw redirectingNavigationError(this.urlSerializer, t.guardsResult);
|
||||
}
|
||||
|
||||
const guardsEnd = new GuardsCheckEnd(
|
||||
t.id, this.serializeUrl(t.extractedUrl),
|
||||
this.serializeUrl(t.urlAfterRedirects!), t.targetSnapshot!,
|
||||
!!t.guardsResult);
|
||||
this.triggerEvent(guardsEnd);
|
||||
}),
|
||||
|
||||
filter(t => {
|
||||
if (!t.guardsResult) {
|
||||
this.restoreHistory(t);
|
||||
this.cancelNavigationTransition(
|
||||
t, '', NavigationCancellationCode.GuardRejected);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
|
||||
// --- RESOLVE ---
|
||||
switchTap(t => {
|
||||
if (t.guards.canActivateChecks.length) {
|
||||
return of(t).pipe(
|
||||
tap(t => {
|
||||
const resolveStart = new ResolveStart(
|
||||
t.id, this.serializeUrl(t.extractedUrl),
|
||||
this.serializeUrl(t.urlAfterRedirects!), t.targetSnapshot!);
|
||||
this.triggerEvent(resolveStart);
|
||||
}),
|
||||
switchMap(t => {
|
||||
let dataResolved = false;
|
||||
return of(t).pipe(
|
||||
resolveData(
|
||||
this.paramsInheritanceStrategy, this.ngModule.injector),
|
||||
tap({
|
||||
next: () => dataResolved = true,
|
||||
complete: () => {
|
||||
if (!dataResolved) {
|
||||
this.restoreHistory(t);
|
||||
this.cancelNavigationTransition(
|
||||
t,
|
||||
NG_DEV_MODE ?
|
||||
`At least one route resolver didn't emit any value.` :
|
||||
'',
|
||||
NavigationCancellationCode.NoDataFromResolver);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
}),
|
||||
tap(t => {
|
||||
const resolveEnd = new ResolveEnd(
|
||||
t.id, this.serializeUrl(t.extractedUrl),
|
||||
this.serializeUrl(t.urlAfterRedirects!), t.targetSnapshot!);
|
||||
this.triggerEvent(resolveEnd);
|
||||
}));
|
||||
}
|
||||
return undefined;
|
||||
}),
|
||||
|
||||
// --- LOAD COMPONENTS ---
|
||||
switchTap((t: NavigationTransition) => {
|
||||
const loadComponents =
|
||||
(route: ActivatedRouteSnapshot): Array<Observable<void>> => {
|
||||
const loaders: Array<Observable<void>> = [];
|
||||
if (route.routeConfig?.loadComponent &&
|
||||
!route.routeConfig._loadedComponent) {
|
||||
loaders.push(this.configLoader.loadComponent(route.routeConfig)
|
||||
.pipe(
|
||||
tap(loadedComponent => {
|
||||
route.component = loadedComponent;
|
||||
}),
|
||||
map(() => void 0),
|
||||
));
|
||||
}
|
||||
for (const child of route.children) {
|
||||
loaders.push(...loadComponents(child));
|
||||
}
|
||||
return loaders;
|
||||
};
|
||||
return combineLatest(loadComponents(t.targetSnapshot!.root))
|
||||
.pipe(defaultIfEmpty(), take(1));
|
||||
}),
|
||||
|
||||
switchTap(() => this.afterPreactivation()),
|
||||
|
||||
map((t: NavigationTransition) => {
|
||||
const targetRouterState = createRouterState(
|
||||
this.routeReuseStrategy, t.targetSnapshot!, t.currentRouterState);
|
||||
overallTransitionState = {...t, targetRouterState};
|
||||
return (overallTransitionState);
|
||||
}),
|
||||
|
||||
/* Once here, we are about to activate synchronously. The assumption is
|
||||
this will succeed, and user code may read from the Router service.
|
||||
Therefore before activation, we need to update router properties storing
|
||||
the current URL and the RouterState, as well as updated the browser URL.
|
||||
All this should happen *before* activating. */
|
||||
tap((t: NavigationTransition) => {
|
||||
this.currentUrlTree = t.urlAfterRedirects!;
|
||||
this.rawUrlTree =
|
||||
this.urlHandlingStrategy.merge(t.urlAfterRedirects!, t.rawUrl);
|
||||
|
||||
(this as {routerState: RouterState}).routerState = t.targetRouterState!;
|
||||
|
||||
if (this.urlUpdateStrategy === 'deferred') {
|
||||
if (!t.extras.skipLocationChange) {
|
||||
this.setBrowserUrl(this.rawUrlTree, t);
|
||||
}
|
||||
return loaders;
|
||||
};
|
||||
return combineLatest(loadComponents(t.targetSnapshot!.root))
|
||||
.pipe(defaultIfEmpty(), take(1));
|
||||
}),
|
||||
this.browserUrlTree = t.urlAfterRedirects!;
|
||||
}
|
||||
}),
|
||||
|
||||
switchTap(() => this.afterPreactivation()),
|
||||
activateRoutes(
|
||||
this.rootContexts, this.routeReuseStrategy,
|
||||
(evt: Event) => this.triggerEvent(evt)),
|
||||
|
||||
map((t: NavigationTransition) => {
|
||||
const targetRouterState = createRouterState(
|
||||
this.routeReuseStrategy, t.targetSnapshot!, t.currentRouterState);
|
||||
return ({...t, targetRouterState});
|
||||
}),
|
||||
tap({
|
||||
next() {
|
||||
completed = true;
|
||||
},
|
||||
complete() {
|
||||
completed = true;
|
||||
}
|
||||
}),
|
||||
finalize(() => {
|
||||
/* When the navigation stream finishes either through error or success,
|
||||
* we set the `completed` or `errored` flag. However, there are some
|
||||
* situations where we could get here without either of those being set.
|
||||
* For instance, a redirect during NavigationStart. Therefore, this is a
|
||||
* catch-all to make sure the NavigationCancel event is fired when a
|
||||
* navigation gets cancelled but not caught by other means. */
|
||||
if (!completed && !errored) {
|
||||
const cancelationReason = NG_DEV_MODE ?
|
||||
`Navigation ID ${
|
||||
overallTransitionState
|
||||
.id} is not equal to the current navigation id ${
|
||||
this.navigationId}` :
|
||||
'';
|
||||
this.cancelNavigationTransition(
|
||||
overallTransitionState, cancelationReason,
|
||||
NavigationCancellationCode.SupersededByNewNavigation);
|
||||
}
|
||||
// Only clear current navigation if it is still set to the one that
|
||||
// finalized.
|
||||
if (this.currentNavigation?.id === overallTransitionState.id) {
|
||||
this.currentNavigation = null;
|
||||
}
|
||||
}),
|
||||
catchError((e) => {
|
||||
errored = true;
|
||||
/* This error type is issued during Redirect, and is handled as a
|
||||
* cancellation rather than an error. */
|
||||
if (isNavigationCancelingError(e)) {
|
||||
if (!isRedirectingNavigationCancelingError(e)) {
|
||||
// Set property only if we're not redirecting. If we landed on a page
|
||||
// and redirect to `/` route, the new navigation is going to see the
|
||||
// `/` isn't a change from the default currentUrlTree and won't
|
||||
// navigate. This is only applicable with initial navigation, so
|
||||
// setting `navigated` only when not redirecting resolves this
|
||||
// scenario.
|
||||
this.navigated = true;
|
||||
this.restoreHistory(overallTransitionState, true);
|
||||
}
|
||||
const navCancel = new NavigationCancel(
|
||||
overallTransitionState.id,
|
||||
this.serializeUrl(overallTransitionState.extractedUrl), e.message,
|
||||
e.cancellationCode);
|
||||
eventsSubject.next(navCancel);
|
||||
|
||||
/* Once here, we are about to activate synchronously. The assumption is this
|
||||
will succeed, and user code may read from the Router service. Therefore
|
||||
before activation, we need to update router properties storing the current
|
||||
URL and the RouterState, as well as updated the browser URL. All this should
|
||||
happen *before* activating. */
|
||||
tap((t: NavigationTransition) => {
|
||||
this.currentUrlTree = t.urlAfterRedirects!;
|
||||
this.rawUrlTree =
|
||||
this.urlHandlingStrategy.merge(t.urlAfterRedirects!, t.rawUrl);
|
||||
// When redirecting, we need to delay resolving the navigation
|
||||
// promise and push it to the redirect navigation
|
||||
if (!isRedirectingNavigationCancelingError(e)) {
|
||||
overallTransitionState.resolve(false);
|
||||
} else {
|
||||
const mergedTree =
|
||||
this.urlHandlingStrategy.merge(e.url, this.rawUrlTree);
|
||||
const extras = {
|
||||
skipLocationChange:
|
||||
overallTransitionState.extras.skipLocationChange,
|
||||
// The URL is already updated at this point if we have 'eager' URL
|
||||
// updates or if the navigation was triggered by the browser (back
|
||||
// button, URL bar, etc). We want to replace that item in history
|
||||
// if the navigation is rejected.
|
||||
replaceUrl: this.urlUpdateStrategy === 'eager' ||
|
||||
isBrowserTriggeredNavigation(overallTransitionState.source)
|
||||
};
|
||||
|
||||
(this as {routerState: RouterState}).routerState = t.targetRouterState!;
|
||||
this.scheduleNavigation(mergedTree, 'imperative', null, extras, {
|
||||
resolve: overallTransitionState.resolve,
|
||||
reject: overallTransitionState.reject,
|
||||
promise: overallTransitionState.promise
|
||||
});
|
||||
}
|
||||
|
||||
if (this.urlUpdateStrategy === 'deferred') {
|
||||
if (!t.extras.skipLocationChange) {
|
||||
this.setBrowserUrl(this.rawUrlTree, t);
|
||||
}
|
||||
this.browserUrlTree = t.urlAfterRedirects!;
|
||||
}
|
||||
}),
|
||||
|
||||
activateRoutes(
|
||||
this.rootContexts, this.routeReuseStrategy,
|
||||
(evt: Event) => this.triggerEvent(evt)),
|
||||
|
||||
tap({
|
||||
next() {
|
||||
completed = true;
|
||||
},
|
||||
complete() {
|
||||
completed = true;
|
||||
}
|
||||
}),
|
||||
finalize(() => {
|
||||
/* When the navigation stream finishes either through error or success, we
|
||||
* set the `completed` or `errored` flag. However, there are some situations
|
||||
* where we could get here without either of those being set. For instance, a
|
||||
* redirect during NavigationStart. Therefore, this is a catch-all to make
|
||||
* sure the NavigationCancel
|
||||
* event is fired when a navigation gets cancelled but not caught by other
|
||||
* means. */
|
||||
if (!completed && !errored) {
|
||||
const cancelationReason = NG_DEV_MODE ?
|
||||
`Navigation ID ${t.id} is not equal to the current navigation id ${
|
||||
this.navigationId}` :
|
||||
'';
|
||||
this.cancelNavigationTransition(
|
||||
t, cancelationReason,
|
||||
NavigationCancellationCode.SupersededByNewNavigation);
|
||||
}
|
||||
// Only clear current navigation if it is still set to the one that
|
||||
// finalized.
|
||||
if (this.currentNavigation?.id === t.id) {
|
||||
this.currentNavigation = null;
|
||||
}
|
||||
}),
|
||||
catchError((e) => {
|
||||
// TODO(atscott): The NavigationTransition `t` used here does not accurately
|
||||
// reflect the current state of the whole transition because some operations
|
||||
// return a new object rather than modifying the one in the outermost
|
||||
// `switchMap`.
|
||||
// The fix can likely be to:
|
||||
// 1. Rename the outer `t` variable so it's not shadowed all the time and
|
||||
// confusing
|
||||
// 2. Keep reassigning to the outer variable after each stage to ensure it
|
||||
// gets updated. Or change the implementations to not return a copy.
|
||||
// Not changed yet because it affects existing code and would need to be
|
||||
// tested more thoroughly.
|
||||
errored = true;
|
||||
/* This error type is issued during Redirect, and is handled as a
|
||||
* cancellation rather than an error. */
|
||||
if (isNavigationCancelingError(e)) {
|
||||
if (!isRedirectingNavigationCancelingError(e)) {
|
||||
// Set property only if we're not redirecting. If we landed on a page and
|
||||
// redirect to `/` route, the new navigation is going to see the `/`
|
||||
// isn't a change from the default currentUrlTree and won't navigate.
|
||||
// This is only applicable with initial navigation, so setting
|
||||
// `navigated` only when not redirecting resolves this scenario.
|
||||
this.navigated = true;
|
||||
this.restoreHistory(t, true);
|
||||
}
|
||||
const navCancel = new NavigationCancel(
|
||||
t.id, this.serializeUrl(t.extractedUrl), e.message,
|
||||
e.cancellationCode);
|
||||
eventsSubject.next(navCancel);
|
||||
|
||||
// When redirecting, we need to delay resolving the navigation
|
||||
// promise and push it to the redirect navigation
|
||||
if (!isRedirectingNavigationCancelingError(e)) {
|
||||
t.resolve(false);
|
||||
} else {
|
||||
const mergedTree =
|
||||
this.urlHandlingStrategy.merge(e.url, this.rawUrlTree);
|
||||
const extras = {
|
||||
skipLocationChange: t.extras.skipLocationChange,
|
||||
// The URL is already updated at this point if we have 'eager' URL
|
||||
// updates or if the navigation was triggered by the browser (back
|
||||
// button, URL bar, etc). We want to replace that item in history if
|
||||
// the navigation is rejected.
|
||||
replaceUrl: this.urlUpdateStrategy === 'eager' ||
|
||||
isBrowserTriggeredNavigation(t.source)
|
||||
};
|
||||
|
||||
this.scheduleNavigation(
|
||||
mergedTree, 'imperative', null, extras,
|
||||
{resolve: t.resolve, reject: t.reject, promise: t.promise});
|
||||
}
|
||||
|
||||
/* All other errors should reset to the router's internal URL reference to
|
||||
* the pre-error state. */
|
||||
} else {
|
||||
this.restoreHistory(t, true);
|
||||
const navError = new NavigationError(
|
||||
t.id, this.serializeUrl(t.extractedUrl), e,
|
||||
t.targetSnapshot ?? undefined);
|
||||
eventsSubject.next(navError);
|
||||
try {
|
||||
t.resolve(this.errorHandler(e));
|
||||
} catch (ee) {
|
||||
t.reject(ee);
|
||||
}
|
||||
}
|
||||
return EMPTY;
|
||||
}));
|
||||
/* All other errors should reset to the router's internal URL reference
|
||||
* to the pre-error state. */
|
||||
} else {
|
||||
this.restoreHistory(overallTransitionState, true);
|
||||
const navError = new NavigationError(
|
||||
overallTransitionState.id,
|
||||
this.serializeUrl(overallTransitionState.extractedUrl), e,
|
||||
overallTransitionState.targetSnapshot ?? undefined);
|
||||
eventsSubject.next(navError);
|
||||
try {
|
||||
overallTransitionState.resolve(this.errorHandler(e));
|
||||
} catch (ee) {
|
||||
overallTransitionState.reject(ee);
|
||||
}
|
||||
}
|
||||
return EMPTY;
|
||||
}));
|
||||
// TODO(jasonaden): remove cast once g3 is on updated TypeScript
|
||||
})) as any as Observable<NavigationTransition>;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import {RouterOutlet} from './directives/router_outlet';
|
||||
import {RuntimeErrorCode} from './errors';
|
||||
import {Event, NavigationCancel, NavigationCancellationCode, NavigationEnd, NavigationError, stringifyEvent} from './events';
|
||||
import {Route, Routes} from './models';
|
||||
import {DefaultTitleStrategy, TitleStrategy} from './page_title_strategy';
|
||||
import {TitleStrategy} from './page_title_strategy';
|
||||
import {RouteReuseStrategy} from './route_reuse_strategy';
|
||||
import {ErrorHandler, Router} from './router';
|
||||
import {RouterConfigLoader, ROUTES} from './router_config_loader';
|
||||
@@ -64,9 +64,9 @@ export const ROUTER_PROVIDERS: Provider[] = [
|
||||
provide: Router,
|
||||
useFactory: setupRouter,
|
||||
deps: [
|
||||
UrlSerializer, ChildrenOutletContexts, Location, Injector, Compiler, ROUTES,
|
||||
ROUTER_CONFIGURATION, DefaultTitleStrategy, [TitleStrategy, new Optional()],
|
||||
[UrlHandlingStrategy, new Optional()], [RouteReuseStrategy, new Optional()]
|
||||
UrlSerializer, ChildrenOutletContexts, Location, Injector, Compiler, ROUTES, TitleStrategy,
|
||||
ROUTER_CONFIGURATION, [UrlHandlingStrategy, new Optional()],
|
||||
[RouteReuseStrategy, new Optional()]
|
||||
]
|
||||
},
|
||||
ChildrenOutletContexts,
|
||||
@@ -461,9 +461,9 @@ export interface ExtraOptions {
|
||||
|
||||
export function setupRouter(
|
||||
urlSerializer: UrlSerializer, contexts: ChildrenOutletContexts, location: Location,
|
||||
injector: Injector, compiler: Compiler, config: Route[][], opts: ExtraOptions = {},
|
||||
defaultTitleStrategy: DefaultTitleStrategy, titleStrategy?: TitleStrategy,
|
||||
urlHandlingStrategy?: UrlHandlingStrategy, routeReuseStrategy?: RouteReuseStrategy) {
|
||||
injector: Injector, compiler: Compiler, config: Route[][], titleStrategy: TitleStrategy,
|
||||
opts: ExtraOptions = {}, urlHandlingStrategy?: UrlHandlingStrategy,
|
||||
routeReuseStrategy?: RouteReuseStrategy) {
|
||||
const router =
|
||||
new Router(null, urlSerializer, contexts, location, injector, compiler, flatten(config));
|
||||
|
||||
@@ -475,7 +475,7 @@ export function setupRouter(
|
||||
router.routeReuseStrategy = routeReuseStrategy;
|
||||
}
|
||||
|
||||
router.titleStrategy = titleStrategy ?? defaultTitleStrategy;
|
||||
router.titleStrategy = titleStrategy;
|
||||
|
||||
assignExtraOptionsToRouter(opts, router);
|
||||
|
||||
|
||||
@@ -391,7 +391,7 @@ describe('bootstrap', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should restore the scrolling position', async (done) => {
|
||||
it('should restore the scrolling position', async () => {
|
||||
@Component({
|
||||
selector: 'component-a',
|
||||
template: `
|
||||
@@ -456,7 +456,6 @@ describe('bootstrap', () => {
|
||||
await router.navigateByUrl('/aa#marker3');
|
||||
expect(window.pageYOffset).toBeGreaterThanOrEqual(8900);
|
||||
expect(window.pageYOffset).toBeLessThan(9000);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should cleanup "popstate" and "hashchange" listeners', async () => {
|
||||
@@ -486,7 +485,7 @@ describe('bootstrap', () => {
|
||||
expect(window.removeEventListener).toHaveBeenCalledWith('hashchange', jasmine.any(Function));
|
||||
});
|
||||
|
||||
it('can schedule a navigation from the NavigationEnd event #37460', async (done) => {
|
||||
it('can schedule a navigation from the NavigationEnd event #37460', (done) => {
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@@ -504,17 +503,19 @@ describe('bootstrap', () => {
|
||||
class TestModule {
|
||||
}
|
||||
|
||||
const res = await platformBrowserDynamic([]).bootstrapModule(TestModule);
|
||||
const router = res.injector.get(Router);
|
||||
router.events.subscribe(() => {
|
||||
expect(router.getCurrentNavigation()?.id).toBeDefined();
|
||||
});
|
||||
router.events.subscribe(async (e) => {
|
||||
if (e instanceof NavigationEnd && e.url === '/b') {
|
||||
await router.navigate(['a']);
|
||||
done();
|
||||
}
|
||||
});
|
||||
await router.navigateByUrl('/b');
|
||||
(async () => {
|
||||
const res = await platformBrowserDynamic([]).bootstrapModule(TestModule);
|
||||
const router = res.injector.get(Router);
|
||||
router.events.subscribe(() => {
|
||||
expect(router.getCurrentNavigation()?.id).toBeDefined();
|
||||
});
|
||||
router.events.subscribe(async (e) => {
|
||||
if (e instanceof NavigationEnd && e.url === '/b') {
|
||||
await router.navigate(['a']);
|
||||
done();
|
||||
}
|
||||
});
|
||||
await router.navigateByUrl('/b');
|
||||
})();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2245,6 +2245,35 @@ describe('Integration', () => {
|
||||
expect(e).toEqual(null);
|
||||
})));
|
||||
|
||||
it('should include target snapshot in NavigationError when resolver throws', async () => {
|
||||
const router = TestBed.inject(Router);
|
||||
const errorMessage = 'throwing resolver';
|
||||
@Injectable({providedIn: 'root'})
|
||||
class ThrowingResolver {
|
||||
resolve() {
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
let caughtError: NavigationError|undefined;
|
||||
router.events.subscribe(e => {
|
||||
if (e instanceof NavigationError) {
|
||||
caughtError = e;
|
||||
}
|
||||
});
|
||||
router.resetConfig(
|
||||
[{path: 'throwing', resolve: {thrower: ThrowingResolver}, component: BlankCmp}]);
|
||||
try {
|
||||
await router.navigateByUrl('/throwing');
|
||||
fail('navigation should throw');
|
||||
} catch (e: unknown) {
|
||||
expect((e as Error).message).toEqual(errorMessage);
|
||||
}
|
||||
|
||||
expect(caughtError).toBeDefined();
|
||||
expect(caughtError?.target).toBeDefined();
|
||||
});
|
||||
|
||||
it('should preserve resolved data', fakeAsync(inject([Router], (router: Router) => {
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
|
||||
|
||||
@@ -111,12 +111,12 @@ describe('RouterPreloader', () => {
|
||||
const injector: any = getLoadedInjector(c[0]);
|
||||
const loadedRoutes: Route[] = getLoadedRoutes(c[0])!;
|
||||
expect(loadedRoutes[0].path).toEqual('LoadedModule1');
|
||||
expect(injector._parent).toBe((testModule as any)._r3Injector);
|
||||
expect(injector.parent).toBe((testModule as any)._r3Injector);
|
||||
|
||||
const injector2: any = getLoadedInjector(loadedRoutes[0]);
|
||||
const loadedRoutes2: Route[] = getLoadedRoutes(loadedRoutes[0])!;
|
||||
expect(loadedRoutes2[0].path).toEqual('LoadedModule2');
|
||||
expect(injector2._parent).toBe(injector);
|
||||
expect(injector2.parent).toBe(injector);
|
||||
|
||||
expect(events.map(e => e.toString())).toEqual([
|
||||
'RouteConfigLoadStart(path: lazy)',
|
||||
@@ -213,11 +213,11 @@ describe('RouterPreloader', () => {
|
||||
|
||||
const injector = getLoadedInjector(c[0]) as any;
|
||||
const loadedRoutes = getLoadedRoutes(c[0])!;
|
||||
expect(injector._parent).toBe((testModule as any)._r3Injector);
|
||||
expect(injector.parent).toBe((testModule as any)._r3Injector);
|
||||
|
||||
const loadedRoutes2: Route[] = getLoadedRoutes(loadedRoutes[0])!;
|
||||
const injector3: any = getLoadedInjector(loadedRoutes2[0]);
|
||||
expect(injector3._parent).toBe(module2.injector);
|
||||
expect(injector3.parent).toBe(module2.injector);
|
||||
})));
|
||||
});
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import {Location, LocationStrategy} from '@angular/common';
|
||||
import {MockLocationStrategy, SpyLocation} from '@angular/common/testing';
|
||||
import {Compiler, Injector, ModuleWithProviders, NgModule, Optional} from '@angular/core';
|
||||
import {ChildrenOutletContexts, DefaultTitleStrategy, ExtraOptions, NoPreloading, PreloadingStrategy, provideRoutes, Route, Router, ROUTER_CONFIGURATION, RouteReuseStrategy, RouterModule, ROUTES, Routes, TitleStrategy, UrlHandlingStrategy, UrlSerializer, ɵassignExtraOptionsToRouter as assignExtraOptionsToRouter, ɵflatten as flatten, ɵprovidePreloading as providePreloading, ɵROUTER_PROVIDERS as ROUTER_PROVIDERS} from '@angular/router';
|
||||
import {ChildrenOutletContexts, ExtraOptions, NoPreloading, provideRoutes, Route, Router, ROUTER_CONFIGURATION, RouteReuseStrategy, RouterModule, ROUTES, Routes, TitleStrategy, UrlHandlingStrategy, UrlSerializer, ɵassignExtraOptionsToRouter as assignExtraOptionsToRouter, ɵflatten as flatten, ɵprovidePreloading as providePreloading, ɵROUTER_PROVIDERS as ROUTER_PROVIDERS} from '@angular/router';
|
||||
|
||||
import {EXTRA_ROUTER_TESTING_PROVIDERS} from './extra_router_testing_providers';
|
||||
|
||||
@@ -25,14 +25,20 @@ function isUrlHandlingStrategy(opts: ExtraOptions|
|
||||
* marked as publicApi cleaner (i.e. not having _both_ `TitleStrategy` and `DefaultTitleStrategy`).
|
||||
*/
|
||||
export function setupTestingRouterInternal(
|
||||
urlSerializer: UrlSerializer, contexts: ChildrenOutletContexts, location: Location,
|
||||
compiler: Compiler, injector: Injector, routes: Route[][],
|
||||
opts?: ExtraOptions|UrlHandlingStrategy, urlHandlingStrategy?: UrlHandlingStrategy,
|
||||
routeReuseStrategy?: RouteReuseStrategy, defaultTitleStrategy?: DefaultTitleStrategy,
|
||||
titleStrategy?: TitleStrategy) {
|
||||
urlSerializer: UrlSerializer,
|
||||
contexts: ChildrenOutletContexts,
|
||||
location: Location,
|
||||
compiler: Compiler,
|
||||
injector: Injector,
|
||||
routes: Route[][],
|
||||
titleStrategy: TitleStrategy,
|
||||
opts?: ExtraOptions|UrlHandlingStrategy,
|
||||
urlHandlingStrategy?: UrlHandlingStrategy,
|
||||
routeReuseStrategy?: RouteReuseStrategy,
|
||||
) {
|
||||
return setupTestingRouter(
|
||||
urlSerializer, contexts, location, compiler, injector, routes, opts, urlHandlingStrategy,
|
||||
routeReuseStrategy, titleStrategy ?? defaultTitleStrategy);
|
||||
routeReuseStrategy, titleStrategy);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,11 +118,10 @@ export function setupTestingRouter(
|
||||
Compiler,
|
||||
Injector,
|
||||
ROUTES,
|
||||
TitleStrategy,
|
||||
ROUTER_CONFIGURATION,
|
||||
[UrlHandlingStrategy, new Optional()],
|
||||
[RouteReuseStrategy, new Optional()],
|
||||
[DefaultTitleStrategy, new Optional()],
|
||||
[TitleStrategy, new Optional()],
|
||||
]
|
||||
},
|
||||
providePreloading(NoPreloading),
|
||||
|
||||
@@ -20,6 +20,13 @@ const INITIAL_VALUE = {
|
||||
};
|
||||
const NOT_SUPPORTED: any = 'NOT_SUPPORTED';
|
||||
|
||||
function getInputPropertyMapName(name: string): string {
|
||||
return `input_${name}`;
|
||||
}
|
||||
|
||||
function getOutputPropertyMapName(name: string): string {
|
||||
return `output_${name}`;
|
||||
}
|
||||
|
||||
export class UpgradeNg1ComponentAdapterBuilder {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@@ -33,7 +40,7 @@ export class UpgradeNg1ComponentAdapterBuilder {
|
||||
propertyMap: {[name: string]: string} = {};
|
||||
directive: IDirective|null = null;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
template !: string;
|
||||
template!: string;
|
||||
|
||||
constructor(public name: string) {
|
||||
const selector =
|
||||
@@ -76,9 +83,9 @@ export class UpgradeNg1ComponentAdapterBuilder {
|
||||
|
||||
// QUESTION: What about `=*`? Ignore? Throw? Support?
|
||||
|
||||
const inputName = `input_${attrName}`;
|
||||
const inputName = getInputPropertyMapName(attrName);
|
||||
const inputNameRename = `${inputName}: ${attrName}`;
|
||||
const outputName = `output_${attrName}`;
|
||||
const outputName = getOutputPropertyMapName(attrName);
|
||||
const outputNameRename = `${outputName}: ${attrName}`;
|
||||
const outputNameRenameChange = `${outputNameRename}Change`;
|
||||
|
||||
@@ -225,8 +232,9 @@ class UpgradeNg1ComponentAdapter implements OnInit, OnChanges, DoCheck {
|
||||
const ng1Changes: any = {};
|
||||
Object.keys(changes).forEach(name => {
|
||||
const change: SimpleChange = changes[name];
|
||||
this.setComponentProperty(name, change.currentValue);
|
||||
ng1Changes[this.propertyMap[name]] = change;
|
||||
const propertyMapName = getInputPropertyMapName(name);
|
||||
this.setComponentProperty(propertyMapName, change.currentValue);
|
||||
ng1Changes[this.propertyMap[propertyMapName]] = change;
|
||||
});
|
||||
|
||||
if (isFunction(this.destinationObj!.$onChanges)) {
|
||||
|
||||
@@ -38,10 +38,6 @@ Zone.__load_patch('jasmine', (global: any, Zone: ZoneType, api: _ZonePrivate) =>
|
||||
if (!ProxyZoneSpec) throw new Error('Missing: ProxyZoneSpec');
|
||||
|
||||
const ambientZone = Zone.current;
|
||||
// Create a synchronous-only zone in which to run `describe` blocks in order to raise an
|
||||
// error if any asynchronous operations are attempted inside of a `describe` but outside of
|
||||
// a `beforeEach` or `it`.
|
||||
const syncZone = ambientZone.fork(new SyncTestZoneSpec('jasmine.describe'));
|
||||
|
||||
const symbol = Zone.__symbol__;
|
||||
|
||||
@@ -100,7 +96,8 @@ Zone.__load_patch('jasmine', (global: any, Zone: ZoneType, api: _ZonePrivate) =>
|
||||
['describe', 'xdescribe', 'fdescribe'].forEach(methodName => {
|
||||
let originalJasmineFn: Function = jasmineEnv[methodName];
|
||||
jasmineEnv[methodName] = function(description: string, specDefinitions: Function) {
|
||||
return originalJasmineFn.call(this, description, wrapDescribeInZone(specDefinitions));
|
||||
return originalJasmineFn.call(
|
||||
this, description, wrapDescribeInZone(description, specDefinitions));
|
||||
};
|
||||
});
|
||||
['it', 'xit', 'fit'].forEach(methodName => {
|
||||
@@ -198,8 +195,11 @@ Zone.__load_patch('jasmine', (global: any, Zone: ZoneType, api: _ZonePrivate) =>
|
||||
* Gets a function wrapping the body of a Jasmine `describe` block to execute in a
|
||||
* synchronous-only zone.
|
||||
*/
|
||||
function wrapDescribeInZone(describeBody: Function): Function {
|
||||
function wrapDescribeInZone(description: string, describeBody: Function): Function {
|
||||
return function(this: unknown) {
|
||||
// Create a synchronous-only zone in which to run `describe` blocks in order to raise an
|
||||
// error if any asynchronous operations are attempted inside of a `describe`.
|
||||
const syncZone = ambientZone.fork(new SyncTestZoneSpec(`jasmine.describe#${description}`));
|
||||
return syncZone.run(describeBody, this, (arguments as any) as any[]);
|
||||
};
|
||||
}
|
||||
@@ -332,6 +332,10 @@ Zone.__load_patch('jasmine', (global: any, Zone: ZoneType, api: _ZonePrivate) =>
|
||||
|
||||
if (!isChildOfAmbientZone) throw new Error('Unexpected Zone: ' + Zone.current.name);
|
||||
|
||||
|
||||
Error.stackTraceLimit = Infinity;
|
||||
let context = new Error().stack;
|
||||
|
||||
// This is the zone which will be used for running individual tests.
|
||||
// It will be a proxy zone, so that the tests function can retroactively install
|
||||
// different zones.
|
||||
|
||||
@@ -13,6 +13,8 @@ class AsyncTestZoneSpec implements ZoneSpec {
|
||||
_pendingMacroTasks: boolean = false;
|
||||
_alreadyErrored: boolean = false;
|
||||
_isSync: boolean = false;
|
||||
_existingFinishTimer: ReturnType<typeof setTimeout>|null = null;
|
||||
|
||||
entryFunction: Function|null = null;
|
||||
runZone = Zone.current;
|
||||
unresolvedChainedPromiseCount = 0;
|
||||
@@ -32,11 +34,31 @@ class AsyncTestZoneSpec implements ZoneSpec {
|
||||
}
|
||||
|
||||
_finishCallbackIfDone() {
|
||||
// NOTE: Technically the `onHasTask` could fire together with the initial synchronous
|
||||
// completion in `onInvoke`. `onHasTask` might call this method when it captured e.g.
|
||||
// microtasks in the proxy zone that now complete as part of this async zone run.
|
||||
// Consider the following scenario:
|
||||
// 1. A test `beforeEach` schedules a microtask in the ProxyZone.
|
||||
// 2. An actual empty `it` spec executes in the AsyncTestZone` (using e.g. `waitForAsync`).
|
||||
// 3. The `onInvoke` invokes `_finishCallbackIfDone` because the spec runs synchronously.
|
||||
// 4. We wait the scheduled timeout (see below) to account for unhandled promises.
|
||||
// 5. The microtask from (1) finishes and `onHasTask` is invoked.
|
||||
// --> We register a second `_finishCallbackIfDone` even though we have scheduled a timeout.
|
||||
|
||||
// If the finish timeout from below is already scheduled, terminate the existing scheduled
|
||||
// finish invocation, avoiding calling `jasmine` `done` multiple times. *Note* that we would
|
||||
// want to schedule a new finish callback in case the task state changes again.
|
||||
if (this._existingFinishTimer !== null) {
|
||||
clearTimeout(this._existingFinishTimer);
|
||||
this._existingFinishTimer = null;
|
||||
}
|
||||
|
||||
if (!(this._pendingMicroTasks || this._pendingMacroTasks ||
|
||||
(this.supportWaitUnresolvedChainedPromise && this.isUnresolvedChainedPromisePending()))) {
|
||||
// We do this because we would like to catch unhandled rejected promises.
|
||||
// We wait until the next tick because we would like to catch unhandled promises which could
|
||||
// cause test logic to be executed. In such cases we cannot finish with tasks pending then.
|
||||
this.runZone.run(() => {
|
||||
setTimeout(() => {
|
||||
this._existingFinishTimer = setTimeout(() => {
|
||||
if (!this._alreadyErrored && !(this._pendingMicroTasks || this._pendingMacroTasks)) {
|
||||
this.finishCallback();
|
||||
}
|
||||
@@ -116,7 +138,6 @@ class AsyncTestZoneSpec implements ZoneSpec {
|
||||
this._isSync = true;
|
||||
return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source);
|
||||
} finally {
|
||||
const afterTaskCounts: any = (parentZoneDelegate as any)._taskCounts;
|
||||
// We need to check the delegate is the same as entryFunction or not.
|
||||
// Consider the following case.
|
||||
//
|
||||
@@ -245,7 +266,7 @@ Zone.__load_patch('asynctest', (global: any, Zone: ZoneType, api: _ZonePrivate)
|
||||
// Need to restore the original zone.
|
||||
if (proxyZoneSpec.getDelegate() == testZoneSpec) {
|
||||
// Only reset the zone spec if it's
|
||||
// sill this one. Otherwise, assume
|
||||
// still this one. Otherwise, assume
|
||||
// it's OK.
|
||||
proxyZoneSpec.setDelegate(previousDelegate);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class SyncTestZoneSpec implements ZoneSpec {
|
||||
switch (task.type) {
|
||||
case 'microTask':
|
||||
case 'macroTask':
|
||||
throw new Error(`Cannot call ${task.source} from within a sync test.`);
|
||||
throw new Error(`Cannot call ${task.source} from within a sync test (${this.name}).`);
|
||||
case 'eventTask':
|
||||
task = delegate.scheduleTask(target, task);
|
||||
break;
|
||||
|
||||
@@ -13,7 +13,6 @@ describe('FileReader', ifEnvSupports('FileReader', function() {
|
||||
let fileReader: FileReader;
|
||||
let blob: Blob;
|
||||
const data = 'Hello, World!';
|
||||
const testZone = Zone.current.fork({name: 'TestZone'});
|
||||
|
||||
// Android 4.3's native browser doesn't implement add/RemoveEventListener for FileReader
|
||||
function supportsEventTargetFns() {
|
||||
@@ -39,6 +38,8 @@ describe('FileReader', ifEnvSupports('FileReader', function() {
|
||||
|
||||
describe('EventTarget methods', ifEnvSupports(supportsEventTargetFns, function() {
|
||||
it('should bind addEventListener listeners', function(done) {
|
||||
const testZone = Zone.current.fork({name: 'TestZone'});
|
||||
|
||||
testZone.run(function() {
|
||||
fileReader.addEventListener('load', function() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
@@ -51,6 +52,7 @@ describe('FileReader', ifEnvSupports('FileReader', function() {
|
||||
});
|
||||
|
||||
it('should remove listeners via removeEventListener', function(done) {
|
||||
const testZone = Zone.current.fork({name: 'TestZone'});
|
||||
const listenerSpy = jasmine.createSpy('listener');
|
||||
|
||||
testZone.run(function() {
|
||||
@@ -67,6 +69,7 @@ describe('FileReader', ifEnvSupports('FileReader', function() {
|
||||
}));
|
||||
|
||||
it('should bind onEventType listeners', function(done) {
|
||||
const testZone = Zone.current.fork({name: 'TestZone'});
|
||||
let listenersCalled = 0;
|
||||
|
||||
testZone.run(function() {
|
||||
|
||||
@@ -11,69 +11,70 @@ import {ifEnvSupports} from '../test-util';
|
||||
function supportsImports() {
|
||||
return 'import' in document.createElement('link');
|
||||
}
|
||||
(<any>supportsImports).message = 'HTML Imports';
|
||||
|
||||
describe('HTML Imports', ifEnvSupports(supportsImports, function() {
|
||||
const testZone = Zone.current.fork({name: 'test'});
|
||||
if (supportsImports()) {
|
||||
describe('HTML Imports', function() {
|
||||
const testZone = Zone.current.fork({name: 'test'});
|
||||
|
||||
it('should work with addEventListener', function(done) {
|
||||
let link: HTMLLinkElement;
|
||||
it('should work with addEventListener', function(done) {
|
||||
let link: HTMLLinkElement;
|
||||
|
||||
testZone.run(function() {
|
||||
link = document.createElement('link');
|
||||
link.rel = 'import';
|
||||
link.href = 'someUrl';
|
||||
link.addEventListener('error', function() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
document.head.removeChild(link);
|
||||
done();
|
||||
});
|
||||
});
|
||||
testZone.run(function() {
|
||||
link = document.createElement('link');
|
||||
link.rel = 'import';
|
||||
link.href = 'someUrl';
|
||||
link.addEventListener('error', function() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
document.head.removeChild(link);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
document.head.appendChild(link!);
|
||||
});
|
||||
document.head.appendChild(link!);
|
||||
});
|
||||
|
||||
function supportsOnEvents() {
|
||||
const link = document.createElement('link');
|
||||
const linkPropDesc = Object.getOwnPropertyDescriptor(link, 'onerror');
|
||||
return !(linkPropDesc && linkPropDesc.value === null);
|
||||
}
|
||||
(<any>supportsOnEvents).message = 'Supports HTMLLinkElement#onxxx patching';
|
||||
function supportsOnEvents() {
|
||||
const link = document.createElement('link');
|
||||
const linkPropDesc = Object.getOwnPropertyDescriptor(link, 'onerror');
|
||||
return !(linkPropDesc && linkPropDesc.value === null);
|
||||
}
|
||||
(<any>supportsOnEvents).message = 'Supports HTMLLinkElement#onxxx patching';
|
||||
|
||||
|
||||
ifEnvSupports(supportsOnEvents, function() {
|
||||
it('should work with onerror', function(done) {
|
||||
let link: HTMLLinkElement;
|
||||
ifEnvSupports(supportsOnEvents, function() {
|
||||
it('should work with onerror', function(done) {
|
||||
let link: HTMLLinkElement;
|
||||
|
||||
testZone.run(function() {
|
||||
link = document.createElement('link');
|
||||
link.rel = 'import';
|
||||
link.href = 'anotherUrl';
|
||||
link.onerror = function() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
document.head.removeChild(link);
|
||||
done();
|
||||
};
|
||||
});
|
||||
testZone.run(function() {
|
||||
link = document.createElement('link');
|
||||
link.rel = 'import';
|
||||
link.href = 'anotherUrl';
|
||||
link.onerror = function() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
document.head.removeChild(link);
|
||||
done();
|
||||
};
|
||||
});
|
||||
|
||||
document.head.appendChild(link!);
|
||||
});
|
||||
document.head.appendChild(link!);
|
||||
});
|
||||
|
||||
it('should work with onload', function(done) {
|
||||
let link: HTMLLinkElement;
|
||||
it('should work with onload', function(done) {
|
||||
let link: HTMLLinkElement;
|
||||
|
||||
testZone.run(function() {
|
||||
link = document.createElement('link');
|
||||
link.rel = 'import';
|
||||
link.href = '/base/angular/packages/zone.js/test/assets/import.html';
|
||||
link.onload = function() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
document.head.removeChild(link);
|
||||
done();
|
||||
};
|
||||
});
|
||||
testZone.run(function() {
|
||||
link = document.createElement('link');
|
||||
link.rel = 'import';
|
||||
link.href = '/base/angular/packages/zone.js/test/assets/import.html';
|
||||
link.onload = function() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
document.head.removeChild(link);
|
||||
done();
|
||||
};
|
||||
});
|
||||
|
||||
document.head.appendChild(link!);
|
||||
});
|
||||
});
|
||||
}));
|
||||
document.head.appendChild(link!);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,13 +12,13 @@ declare const global: any;
|
||||
|
||||
describe('MutationObserver', ifEnvSupports('MutationObserver', function() {
|
||||
let elt: HTMLDivElement;
|
||||
const testZone = Zone.current.fork({name: 'test'});
|
||||
|
||||
beforeEach(function() {
|
||||
elt = document.createElement('div');
|
||||
});
|
||||
|
||||
it('should run observers within the zone', function(done) {
|
||||
const testZone = Zone.current.fork({name: 'test'});
|
||||
let ob;
|
||||
|
||||
testZone.run(function() {
|
||||
@@ -54,9 +54,8 @@ describe('MutationObserver', ifEnvSupports('MutationObserver', function() {
|
||||
}));
|
||||
|
||||
describe('WebKitMutationObserver', ifEnvSupports('WebKitMutationObserver', function() {
|
||||
const testZone = Zone.current.fork({name: 'test'});
|
||||
|
||||
it('should run observers within the zone', function(done) {
|
||||
const testZone = Zone.current.fork({name: 'test'});
|
||||
let elt: HTMLDivElement;
|
||||
|
||||
testZone.run(function() {
|
||||
|
||||
@@ -334,15 +334,25 @@ describe('XMLHttpRequest', function() {
|
||||
function(done) {
|
||||
testZone.run(function() {
|
||||
const req = new XMLHttpRequest();
|
||||
|
||||
req.open('get', '/', true);
|
||||
req.send();
|
||||
|
||||
let count = 0;
|
||||
const listener = function(ev: any) {
|
||||
if (req.readyState >= 2) {
|
||||
const isInitial = count++ === 0;
|
||||
|
||||
expect(() => {
|
||||
// this triggers a synchronous dispatch of the state change event.
|
||||
req.abort();
|
||||
}).not.toThrow();
|
||||
|
||||
req.removeEventListener('readystatechange', listener);
|
||||
done();
|
||||
|
||||
if (isInitial) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
};
|
||||
req.addEventListener('readystatechange', listener);
|
||||
|
||||
@@ -3398,145 +3398,140 @@ describe('Zone', function() {
|
||||
});
|
||||
|
||||
|
||||
describe(
|
||||
'pointer event in IE',
|
||||
ifEnvSupports(
|
||||
() => {
|
||||
return getIEVersion() === 11;
|
||||
},
|
||||
() => {
|
||||
const pointerEventsMap: {[key: string]: string} = {
|
||||
'MSPointerCancel': 'pointercancel',
|
||||
'MSPointerDown': 'pointerdown',
|
||||
'MSPointerEnter': 'pointerenter',
|
||||
'MSPointerHover': 'pointerhover',
|
||||
'MSPointerLeave': 'pointerleave',
|
||||
'MSPointerMove': 'pointermove',
|
||||
'MSPointerOut': 'pointerout',
|
||||
'MSPointerOver': 'pointerover',
|
||||
'MSPointerUp': 'pointerup'
|
||||
};
|
||||
if (getIEVersion() === 11) {
|
||||
describe('pointer event in IE', () => {
|
||||
const pointerEventsMap: {[key: string]: string} = {
|
||||
'MSPointerCancel': 'pointercancel',
|
||||
'MSPointerDown': 'pointerdown',
|
||||
'MSPointerEnter': 'pointerenter',
|
||||
'MSPointerHover': 'pointerhover',
|
||||
'MSPointerLeave': 'pointerleave',
|
||||
'MSPointerMove': 'pointermove',
|
||||
'MSPointerOut': 'pointerout',
|
||||
'MSPointerOver': 'pointerover',
|
||||
'MSPointerUp': 'pointerup'
|
||||
};
|
||||
|
||||
let div: HTMLDivElement;
|
||||
beforeEach(() => {
|
||||
div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
});
|
||||
afterEach(() => {
|
||||
document.body.removeChild(div);
|
||||
});
|
||||
Object.keys(pointerEventsMap).forEach(key => {
|
||||
it(`${key} and ${pointerEventsMap[key]} should both be triggered`, (done: DoneFn) => {
|
||||
const logs: string[] = [];
|
||||
div.addEventListener(key, (event: any) => {
|
||||
expect(event.type).toEqual(pointerEventsMap[key]);
|
||||
logs.push(`${key} triggered`);
|
||||
});
|
||||
div.addEventListener(pointerEventsMap[key], (event: any) => {
|
||||
expect(event.type).toEqual(pointerEventsMap[key]);
|
||||
logs.push(`${pointerEventsMap[key]} triggered`);
|
||||
});
|
||||
const evt1 = document.createEvent('Event');
|
||||
evt1.initEvent(key, true, true);
|
||||
div.dispatchEvent(evt1);
|
||||
let div: HTMLDivElement;
|
||||
beforeEach(() => {
|
||||
div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
});
|
||||
afterEach(() => {
|
||||
document.body.removeChild(div);
|
||||
});
|
||||
Object.keys(pointerEventsMap).forEach(key => {
|
||||
it(`${key} and ${pointerEventsMap[key]} should both be triggered`, (done: DoneFn) => {
|
||||
const logs: string[] = [];
|
||||
div.addEventListener(key, (event: any) => {
|
||||
expect(event.type).toEqual(pointerEventsMap[key]);
|
||||
logs.push(`${key} triggered`);
|
||||
});
|
||||
div.addEventListener(pointerEventsMap[key], (event: any) => {
|
||||
expect(event.type).toEqual(pointerEventsMap[key]);
|
||||
logs.push(`${pointerEventsMap[key]} triggered`);
|
||||
});
|
||||
const evt1 = document.createEvent('Event');
|
||||
evt1.initEvent(key, true, true);
|
||||
div.dispatchEvent(evt1);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([`${key} triggered`, `${pointerEventsMap[key]} triggered`]);
|
||||
});
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([`${key} triggered`, `${pointerEventsMap[key]} triggered`]);
|
||||
});
|
||||
|
||||
const evt2 = document.createEvent('Event');
|
||||
evt2.initEvent(pointerEventsMap[key], true, true);
|
||||
div.dispatchEvent(evt2);
|
||||
const evt2 = document.createEvent('Event');
|
||||
evt2.initEvent(pointerEventsMap[key], true, true);
|
||||
div.dispatchEvent(evt2);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([`${key} triggered`, `${pointerEventsMap[key]} triggered`]);
|
||||
});
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([`${key} triggered`, `${pointerEventsMap[key]} triggered`]);
|
||||
});
|
||||
|
||||
setTimeout(done);
|
||||
});
|
||||
setTimeout(done);
|
||||
});
|
||||
|
||||
it(`${key} and ${
|
||||
pointerEventsMap[key]} with same listener should not be triggered twice`,
|
||||
(done: DoneFn) => {
|
||||
const logs: string[] = [];
|
||||
const listener = function(event: any) {
|
||||
expect(event.type).toEqual(pointerEventsMap[key]);
|
||||
logs.push(`${key} triggered`);
|
||||
};
|
||||
div.addEventListener(key, listener);
|
||||
div.addEventListener(pointerEventsMap[key], listener);
|
||||
it(`${key} and ${pointerEventsMap[key]} with same listener should not be triggered twice`,
|
||||
(done: DoneFn) => {
|
||||
const logs: string[] = [];
|
||||
const listener = function(event: any) {
|
||||
expect(event.type).toEqual(pointerEventsMap[key]);
|
||||
logs.push(`${key} triggered`);
|
||||
};
|
||||
div.addEventListener(key, listener);
|
||||
div.addEventListener(pointerEventsMap[key], listener);
|
||||
|
||||
const evt1 = document.createEvent('Event');
|
||||
evt1.initEvent(key, true, true);
|
||||
div.dispatchEvent(evt1);
|
||||
const evt1 = document.createEvent('Event');
|
||||
evt1.initEvent(key, true, true);
|
||||
div.dispatchEvent(evt1);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([`${key} triggered`]);
|
||||
});
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([`${key} triggered`]);
|
||||
});
|
||||
|
||||
const evt2 = document.createEvent('Event');
|
||||
evt2.initEvent(pointerEventsMap[key], true, true);
|
||||
div.dispatchEvent(evt2);
|
||||
const evt2 = document.createEvent('Event');
|
||||
evt2.initEvent(pointerEventsMap[key], true, true);
|
||||
div.dispatchEvent(evt2);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([`${pointerEventsMap[key]} triggered`]);
|
||||
});
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([`${pointerEventsMap[key]} triggered`]);
|
||||
});
|
||||
|
||||
setTimeout(done);
|
||||
});
|
||||
setTimeout(done);
|
||||
});
|
||||
|
||||
it(`${key} and ${
|
||||
pointerEventsMap[key]} should be able to be removed with removeEventListener`,
|
||||
(done: DoneFn) => {
|
||||
const logs: string[] = [];
|
||||
const listener1 = function(event: any) {
|
||||
logs.push(`${key} triggered`);
|
||||
};
|
||||
const listener2 = function(event: any) {
|
||||
logs.push(`${pointerEventsMap[key]} triggered`);
|
||||
};
|
||||
div.addEventListener(key, listener1);
|
||||
div.addEventListener(pointerEventsMap[key], listener2);
|
||||
it(`${key} and ${
|
||||
pointerEventsMap[key]} should be able to be removed with removeEventListener`,
|
||||
(done: DoneFn) => {
|
||||
const logs: string[] = [];
|
||||
const listener1 = function(event: any) {
|
||||
logs.push(`${key} triggered`);
|
||||
};
|
||||
const listener2 = function(event: any) {
|
||||
logs.push(`${pointerEventsMap[key]} triggered`);
|
||||
};
|
||||
div.addEventListener(key, listener1);
|
||||
div.addEventListener(pointerEventsMap[key], listener2);
|
||||
|
||||
div.removeEventListener(key, listener1);
|
||||
div.removeEventListener(key, listener2);
|
||||
div.removeEventListener(key, listener1);
|
||||
div.removeEventListener(key, listener2);
|
||||
|
||||
const evt1 = document.createEvent('Event');
|
||||
evt1.initEvent(key, true, true);
|
||||
div.dispatchEvent(evt1);
|
||||
const evt1 = document.createEvent('Event');
|
||||
evt1.initEvent(key, true, true);
|
||||
div.dispatchEvent(evt1);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([]);
|
||||
});
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([]);
|
||||
});
|
||||
|
||||
const evt2 = document.createEvent('Event');
|
||||
evt2.initEvent(pointerEventsMap[key], true, true);
|
||||
div.dispatchEvent(evt2);
|
||||
const evt2 = document.createEvent('Event');
|
||||
evt2.initEvent(pointerEventsMap[key], true, true);
|
||||
div.dispatchEvent(evt2);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([]);
|
||||
});
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([]);
|
||||
});
|
||||
|
||||
div.addEventListener(key, listener1);
|
||||
div.addEventListener(pointerEventsMap[key], listener2);
|
||||
div.addEventListener(key, listener1);
|
||||
div.addEventListener(pointerEventsMap[key], listener2);
|
||||
|
||||
div.removeEventListener(pointerEventsMap[key], listener1);
|
||||
div.removeEventListener(pointerEventsMap[key], listener2);
|
||||
div.removeEventListener(pointerEventsMap[key], listener1);
|
||||
div.removeEventListener(pointerEventsMap[key], listener2);
|
||||
|
||||
div.dispatchEvent(evt1);
|
||||
div.dispatchEvent(evt1);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([]);
|
||||
});
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([]);
|
||||
});
|
||||
|
||||
div.dispatchEvent(evt2);
|
||||
div.dispatchEvent(evt2);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([]);
|
||||
});
|
||||
setTimeout(() => {
|
||||
expect(logs).toEqual([]);
|
||||
});
|
||||
|
||||
setTimeout(done);
|
||||
});
|
||||
});
|
||||
}));
|
||||
setTimeout(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11,161 +11,159 @@
|
||||
* is properly patched
|
||||
*/
|
||||
|
||||
import {ifEnvSupports} from '../test-util';
|
||||
|
||||
function registerElement() {
|
||||
return ('registerElement' in document) && (typeof customElements === 'undefined');
|
||||
}
|
||||
(<any>registerElement).message = 'document.registerElement';
|
||||
|
||||
describe(
|
||||
'document.registerElement', ifEnvSupports(registerElement, function() {
|
||||
// register a custom element for each callback
|
||||
const callbackNames = ['created', 'attached', 'detached', 'attributeChanged'];
|
||||
const callbacks: any = {};
|
||||
const testZone = Zone.current.fork({name: 'test'});
|
||||
let customElements;
|
||||
if (registerElement()) {
|
||||
describe('document.registerElement', function() {
|
||||
// register a custom element for each callback
|
||||
const callbackNames = ['created', 'attached', 'detached', 'attributeChanged'];
|
||||
const callbacks: any = {};
|
||||
const testZone = Zone.current.fork({name: 'test'});
|
||||
let customElements;
|
||||
|
||||
customElements = testZone.run(function() {
|
||||
callbackNames.forEach(function(callbackName) {
|
||||
const fullCallbackName = callbackName + 'Callback';
|
||||
const proto = Object.create(HTMLElement.prototype);
|
||||
(proto as any)[fullCallbackName] = function(arg: any) {
|
||||
callbacks[callbackName](arg);
|
||||
};
|
||||
(<any>document).registerElement('x-' + callbackName.toLowerCase(), {prototype: proto});
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with createdCallback', function(done) {
|
||||
callbacks.created = function() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
done();
|
||||
customElements = testZone.run(function() {
|
||||
callbackNames.forEach(function(callbackName) {
|
||||
const fullCallbackName = callbackName + 'Callback';
|
||||
const proto = Object.create(HTMLElement.prototype);
|
||||
(proto as any)[fullCallbackName] = function(arg: any) {
|
||||
callbacks[callbackName](arg);
|
||||
};
|
||||
|
||||
document.createElement('x-created');
|
||||
(<any>document).registerElement('x-' + callbackName.toLowerCase(), {prototype: proto});
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with createdCallback', function(done) {
|
||||
callbacks.created = function() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
done();
|
||||
};
|
||||
|
||||
document.createElement('x-created');
|
||||
});
|
||||
|
||||
|
||||
it('should work with attachedCallback', function(done) {
|
||||
callbacks.attached = function() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
done();
|
||||
};
|
||||
it('should work with attachedCallback', function(done) {
|
||||
callbacks.attached = function() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
done();
|
||||
};
|
||||
|
||||
const elt = document.createElement('x-attached');
|
||||
document.body.appendChild(elt);
|
||||
document.body.removeChild(elt);
|
||||
});
|
||||
const elt = document.createElement('x-attached');
|
||||
document.body.appendChild(elt);
|
||||
document.body.removeChild(elt);
|
||||
});
|
||||
|
||||
|
||||
it('should work with detachedCallback', function(done) {
|
||||
callbacks.detached = function() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
done();
|
||||
};
|
||||
it('should work with detachedCallback', function(done) {
|
||||
callbacks.detached = function() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
done();
|
||||
};
|
||||
|
||||
const elt = document.createElement('x-detached');
|
||||
document.body.appendChild(elt);
|
||||
document.body.removeChild(elt);
|
||||
});
|
||||
const elt = document.createElement('x-detached');
|
||||
document.body.appendChild(elt);
|
||||
document.body.removeChild(elt);
|
||||
});
|
||||
|
||||
|
||||
it('should work with attributeChanged', function(done) {
|
||||
callbacks.attributeChanged = function() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
done();
|
||||
};
|
||||
it('should work with attributeChanged', function(done) {
|
||||
callbacks.attributeChanged = function() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
done();
|
||||
};
|
||||
|
||||
const elt = document.createElement('x-attributechanged');
|
||||
elt.id = 'bar';
|
||||
});
|
||||
const elt = document.createElement('x-attributechanged');
|
||||
elt.id = 'bar';
|
||||
});
|
||||
|
||||
|
||||
it('should work with non-writable, non-configurable prototypes created with defineProperty',
|
||||
function(done) {
|
||||
testZone.run(function() {
|
||||
const proto = Object.create(HTMLElement.prototype);
|
||||
it('should work with non-writable, non-configurable prototypes created with defineProperty',
|
||||
function(done) {
|
||||
testZone.run(function() {
|
||||
const proto = Object.create(HTMLElement.prototype);
|
||||
|
||||
Object.defineProperty(
|
||||
proto, 'createdCallback',
|
||||
<any>{writable: false, configurable: false, value: checkZone});
|
||||
Object.defineProperty(
|
||||
proto, 'createdCallback',
|
||||
<any>{writable: false, configurable: false, value: checkZone});
|
||||
|
||||
(<any>document).registerElement('x-prop-desc', {prototype: proto});
|
||||
(<any>document).registerElement('x-prop-desc', {prototype: proto});
|
||||
|
||||
function checkZone() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
done();
|
||||
function checkZone() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
const elt = document.createElement('x-prop-desc');
|
||||
});
|
||||
|
||||
|
||||
it('should work with non-writable, non-configurable prototypes created with defineProperties',
|
||||
function(done) {
|
||||
testZone.run(function() {
|
||||
const proto = Object.create(HTMLElement.prototype);
|
||||
|
||||
Object.defineProperties(proto, {
|
||||
createdCallback: <any> {
|
||||
writable: false, configurable: false, value: checkZone
|
||||
}
|
||||
});
|
||||
|
||||
const elt = document.createElement('x-prop-desc');
|
||||
(<any>document).registerElement('x-props-desc', {prototype: proto});
|
||||
|
||||
function checkZone() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
const elt = document.createElement('x-props-desc');
|
||||
});
|
||||
|
||||
it('should work with non-writable, non-configurable prototypes created with defineProperties',
|
||||
function(done) {
|
||||
testZone.run(function() {
|
||||
const proto = Object.create(HTMLElement.prototype);
|
||||
|
||||
Object.defineProperties(proto, {
|
||||
createdCallback: <any> {
|
||||
writable: false, configurable: false, value: checkZone
|
||||
}
|
||||
});
|
||||
|
||||
(<any>document).registerElement('x-props-desc', {prototype: proto});
|
||||
|
||||
function checkZone() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
const elt = document.createElement('x-props-desc');
|
||||
});
|
||||
|
||||
it('should not throw with frozen prototypes ', function() {
|
||||
testZone.run(function() {
|
||||
const proto = Object.create(HTMLElement.prototype, Object.freeze(<PropertyDescriptorMap>{
|
||||
createdCallback: <PropertyDescriptor> {
|
||||
value: () => {}, writable: true, configurable: true
|
||||
}
|
||||
}));
|
||||
|
||||
Object.defineProperty(
|
||||
proto, 'createdCallback', <any>{writable: false, configurable: false});
|
||||
|
||||
expect(function() {
|
||||
(<any>document).registerElement('x-frozen-desc', {prototype: proto});
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should check bind callback if not own property', function(done) {
|
||||
testZone.run(function() {
|
||||
const originalProto = {createdCallback: checkZone};
|
||||
|
||||
const secondaryProto = Object.create(originalProto);
|
||||
expect(secondaryProto.createdCallback).toBe(originalProto.createdCallback);
|
||||
|
||||
(<any>document).registerElement('x-inherited-callback', {prototype: secondaryProto});
|
||||
expect(secondaryProto.createdCallback).not.toBe(originalProto.createdCallback);
|
||||
|
||||
function checkZone() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
done();
|
||||
it('should not throw with frozen prototypes ', function() {
|
||||
testZone.run(function() {
|
||||
const proto = Object.create(HTMLElement.prototype, Object.freeze(<PropertyDescriptorMap>{
|
||||
createdCallback: <PropertyDescriptor> {
|
||||
value: () => {}, writable: true, configurable: true
|
||||
}
|
||||
}));
|
||||
|
||||
const elt = document.createElement('x-inherited-callback');
|
||||
});
|
||||
});
|
||||
Object.defineProperty(
|
||||
proto, 'createdCallback', <any>{writable: false, configurable: false});
|
||||
|
||||
|
||||
it('should not throw if no options passed to registerElement', function() {
|
||||
expect(function() {
|
||||
(<any>document).registerElement('x-no-opts');
|
||||
(<any>document).registerElement('x-frozen-desc', {prototype: proto});
|
||||
}).not.toThrow();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
it('should check bind callback if not own property', function(done) {
|
||||
testZone.run(function() {
|
||||
const originalProto = {createdCallback: checkZone};
|
||||
|
||||
const secondaryProto = Object.create(originalProto);
|
||||
expect(secondaryProto.createdCallback).toBe(originalProto.createdCallback);
|
||||
|
||||
(<any>document).registerElement('x-inherited-callback', {prototype: secondaryProto});
|
||||
expect(secondaryProto.createdCallback).not.toBe(originalProto.createdCallback);
|
||||
|
||||
function checkZone() {
|
||||
expect(Zone.current).toBe(testZone);
|
||||
done();
|
||||
}
|
||||
|
||||
const elt = document.createElement('x-inherited-callback');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should not throw if no options passed to registerElement', function() {
|
||||
expect(function() {
|
||||
(<any>document).registerElement('x-no-opts');
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,53 +6,52 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ifEnvSupports} from '../test-util';
|
||||
declare const window: any;
|
||||
|
||||
describe('requestAnimationFrame', function() {
|
||||
const functions =
|
||||
['requestAnimationFrame', 'webkitRequestAnimationFrame', 'mozRequestAnimationFrame'];
|
||||
|
||||
functions.forEach(function(fnName) {
|
||||
describe(fnName, ifEnvSupports(fnName, function() {
|
||||
const originalTimeout: number = (<any>jasmine).DEFAULT_TIMEOUT_INTERVAL;
|
||||
beforeEach(() => {
|
||||
(<any>jasmine).DEFAULT_TIMEOUT_INTERVAL = 10000;
|
||||
});
|
||||
if ((global as any)[fnName] !== undefined) {
|
||||
describe(fnName, function() {
|
||||
const originalTimeout: number = (<any>jasmine).DEFAULT_TIMEOUT_INTERVAL;
|
||||
beforeEach(() => {
|
||||
(<any>jasmine).DEFAULT_TIMEOUT_INTERVAL = 10000;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(<any>jasmine).DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
});
|
||||
const rAF = window[fnName];
|
||||
afterEach(() => {
|
||||
(<any>jasmine).DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
});
|
||||
const requestAnimationFrameFn = (window as any)[fnName];
|
||||
|
||||
it('should be tolerant of invalid arguments', function() {
|
||||
// rAF throws an error on invalid arguments, so expect that.
|
||||
expect(function() {
|
||||
rAF(null);
|
||||
}).toThrow();
|
||||
});
|
||||
it('should be tolerant of invalid arguments', function() {
|
||||
// requestAnimationFrameFn throws an error on invalid arguments, so expect that.
|
||||
expect(function() {
|
||||
requestAnimationFrameFn(null);
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('should bind to same zone when called recursively', function(done) {
|
||||
Zone.current.fork({name: 'TestZone'}).run(() => {
|
||||
let frames = 0;
|
||||
let previousTimeStamp = 0;
|
||||
it('should bind to same zone when called recursively', function(done) {
|
||||
Zone.current.fork({name: 'TestZone'}).run(() => {
|
||||
let frames = 0;
|
||||
let previousTimeStamp = 0;
|
||||
|
||||
function frameCallback(timestamp: number) {
|
||||
expect(timestamp).toMatch(/^[\d.]+$/);
|
||||
// expect previous <= current
|
||||
expect(previousTimeStamp).not.toBeGreaterThan(timestamp);
|
||||
previousTimeStamp = timestamp;
|
||||
function frameCallback(timestamp: number) {
|
||||
expect(timestamp).toMatch(/^[\d.]+$/);
|
||||
// expect previous <= current
|
||||
expect(previousTimeStamp).not.toBeGreaterThan(timestamp);
|
||||
previousTimeStamp = timestamp;
|
||||
|
||||
if (frames++ > 15) {
|
||||
(<any>jasmine).DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
return done();
|
||||
}
|
||||
rAF(frameCallback);
|
||||
}
|
||||
if (frames++ > 15) {
|
||||
(<any>jasmine).DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
return done();
|
||||
}
|
||||
requestAnimationFrameFn(frameCallback);
|
||||
}
|
||||
|
||||
rAF(frameCallback);
|
||||
});
|
||||
});
|
||||
}));
|
||||
requestAnimationFrameFn(frameCallback);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,7 +38,7 @@ ifEnvSupports(supportJasmineSpec, () => {
|
||||
|
||||
it('should throw on async in describe', () => {
|
||||
expect(throwOnAsync).toBe(true);
|
||||
expect(syncZone.name).toEqual('syncTestZone for jasmine.describe');
|
||||
expect(syncZone.name).toEqual('syncTestZone for jasmine.describe#jasmine');
|
||||
itZone = Zone.current;
|
||||
});
|
||||
|
||||
|
||||
@@ -186,6 +186,44 @@ describe('AsyncTestZoneSpec', function() {
|
||||
}, 50);
|
||||
});
|
||||
|
||||
it('should not call done multiple times when proxy zone captures previously ' +
|
||||
'captured microtasks',
|
||||
(done) => {
|
||||
const ProxyZoneSpec = (Zone as any)['ProxyZoneSpec'];
|
||||
const proxyZoneSpec = new ProxyZoneSpec(null) as ProxyZoneSpec;
|
||||
const proxyZone = Zone.current.fork(proxyZoneSpec);
|
||||
|
||||
// This simulates a simple `beforeEach` patched, running in the proxy zone,
|
||||
// but not necessarily waiting for the promise to be resolved. This can
|
||||
// be the case e.g. in the AngularJS upgrade tests where the bootstrap is
|
||||
// performed in the before each, but the waiting is done in the actual `it` specs.
|
||||
proxyZone.run(() => {
|
||||
Promise.resolve().then(() => {});
|
||||
});
|
||||
|
||||
let doneCalledCount = 0;
|
||||
const testFn = () => {
|
||||
// When a test executes with `waitForAsync`, the proxy zone delegates to the async
|
||||
// test zone, potentially also capturing tasks leaking from `beforeEach`.
|
||||
proxyZoneSpec.setDelegate(testZoneSpec);
|
||||
};
|
||||
|
||||
const testZoneSpec = new AsyncTestZoneSpec(() => {
|
||||
// reset the proxy zone delegate after test completion.
|
||||
proxyZoneSpec.setDelegate(null);
|
||||
doneCalledCount++;
|
||||
}, () => done.fail('Error occurred in the async test zone.'), 'name');
|
||||
|
||||
const atz = Zone.current.fork(testZoneSpec);
|
||||
atz.run(testFn);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(doneCalledCount).toBe(1);
|
||||
done();
|
||||
}, 50);
|
||||
});
|
||||
|
||||
|
||||
describe('event tasks', ifEnvSupports('document', () => {
|
||||
let button: HTMLButtonElement;
|
||||
beforeEach(function() {
|
||||
@@ -359,7 +397,10 @@ describe('AsyncTestZoneSpec', function() {
|
||||
},
|
||||
(err: Error) => {
|
||||
expect(err.message).toEqual('Uncaught (in promise): my reason');
|
||||
done();
|
||||
// Without the `runInTestZone` function, the callback continues to execute
|
||||
// in the async test zone. We don't want to trigger new tasks upon
|
||||
// the failure callback already being invoked (`jasmine.done` schedules tasks)
|
||||
Zone.root.run(() => done());
|
||||
},
|
||||
'name');
|
||||
|
||||
|
||||
@@ -748,90 +748,105 @@ describe('FakeAsyncTestZoneSpec', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('requestAnimationFrame', () => {
|
||||
const functions =
|
||||
['requestAnimationFrame', 'webkitRequestAnimationFrame', 'mozRequestAnimationFrame'];
|
||||
functions.forEach((fnName) => {
|
||||
describe(fnName, ifEnvSupports(fnName, () => {
|
||||
it('should schedule a requestAnimationFrame with timeout of 16ms', () => {
|
||||
fakeAsyncTestZone.run(() => {
|
||||
let ran = false;
|
||||
requestAnimationFrame(() => {
|
||||
ran = true;
|
||||
});
|
||||
const animationFrameFns = [
|
||||
['requestAnimationFrame', 'cancelAnimationFrame'],
|
||||
['webkitRequestAnimationFrame', 'webkitCancelAnimationFrame'],
|
||||
['mozRequestAnimationFrame', 'mozCancelAnimationFrame'],
|
||||
];
|
||||
|
||||
testZoneSpec.tick(6);
|
||||
expect(ran).toEqual(false);
|
||||
const availableAnimationFrameFns =
|
||||
animationFrameFns
|
||||
.map(([fnName,
|
||||
cancelFnName]) => [fnName, (global as any)[fnName], (global as any)[cancelFnName]])
|
||||
.filter(
|
||||
([_, fn]) => fn !==
|
||||
undefined) as [string, typeof requestAnimationFrame, typeof cancelAnimationFrame][];
|
||||
|
||||
testZoneSpec.tick(10);
|
||||
expect(ran).toEqual(true);
|
||||
});
|
||||
});
|
||||
it('does not count as a pending timer', () => {
|
||||
fakeAsyncTestZone.run(() => {
|
||||
requestAnimationFrame(() => {});
|
||||
});
|
||||
expect(testZoneSpec.pendingTimers.length).toBe(0);
|
||||
expect(testZoneSpec.pendingPeriodicTimers.length).toBe(0);
|
||||
});
|
||||
it('should cancel a scheduled requestAnimatiomFrame', () => {
|
||||
fakeAsyncTestZone.run(() => {
|
||||
let ran = false;
|
||||
const id = requestAnimationFrame(() => {
|
||||
ran = true;
|
||||
});
|
||||
if (availableAnimationFrameFns.length > 0) {
|
||||
describe('requestAnimationFrame', () => {
|
||||
availableAnimationFrameFns.forEach(
|
||||
([name, requestAnimationFrameFn, cancelAnimationFrameFn]) => {
|
||||
describe(name, () => {
|
||||
it('should schedule a requestAnimationFrame with timeout of 16ms', () => {
|
||||
fakeAsyncTestZone.run(() => {
|
||||
let ran = false;
|
||||
requestAnimationFrameFn(() => {
|
||||
ran = true;
|
||||
});
|
||||
|
||||
testZoneSpec.tick(6);
|
||||
expect(ran).toEqual(false);
|
||||
testZoneSpec.tick(6);
|
||||
expect(ran).toEqual(false);
|
||||
|
||||
cancelAnimationFrame(id);
|
||||
testZoneSpec.tick(10);
|
||||
expect(ran).toEqual(true);
|
||||
});
|
||||
});
|
||||
it('does not count as a pending timer', () => {
|
||||
fakeAsyncTestZone.run(() => {
|
||||
requestAnimationFrameFn(() => {});
|
||||
});
|
||||
expect(testZoneSpec.pendingTimers.length).toBe(0);
|
||||
expect(testZoneSpec.pendingPeriodicTimers.length).toBe(0);
|
||||
});
|
||||
it('should cancel a scheduled requestAnimationFrame', () => {
|
||||
fakeAsyncTestZone.run(() => {
|
||||
let ran = false;
|
||||
const id = requestAnimationFrameFn(() => {
|
||||
ran = true;
|
||||
});
|
||||
|
||||
testZoneSpec.tick(10);
|
||||
expect(ran).toEqual(false);
|
||||
});
|
||||
});
|
||||
it('is not flushed when flushPeriodic is false', () => {
|
||||
let ran = false;
|
||||
fakeAsyncTestZone.run(() => {
|
||||
requestAnimationFrame(() => {
|
||||
ran = true;
|
||||
});
|
||||
testZoneSpec.flush(20);
|
||||
expect(ran).toEqual(false);
|
||||
});
|
||||
});
|
||||
it('is flushed when flushPeriodic is true', () => {
|
||||
let ran = false;
|
||||
fakeAsyncTestZone.run(() => {
|
||||
requestAnimationFrame(() => {
|
||||
ran = true;
|
||||
});
|
||||
const elapsed = testZoneSpec.flush(20, true);
|
||||
expect(elapsed).toEqual(16);
|
||||
expect(ran).toEqual(true);
|
||||
});
|
||||
});
|
||||
it('should pass timestamp as parameter', () => {
|
||||
let timestamp = 0;
|
||||
let timestamp1 = 0;
|
||||
fakeAsyncTestZone.run(() => {
|
||||
requestAnimationFrame((ts) => {
|
||||
timestamp = ts;
|
||||
requestAnimationFrame(ts1 => {
|
||||
timestamp1 = ts1;
|
||||
});
|
||||
});
|
||||
const elapsed = testZoneSpec.flush(20, true);
|
||||
const elapsed1 = testZoneSpec.flush(20, true);
|
||||
expect(elapsed).toEqual(16);
|
||||
expect(elapsed1).toEqual(16);
|
||||
expect(timestamp).toEqual(16);
|
||||
expect(timestamp1).toEqual(32);
|
||||
});
|
||||
});
|
||||
}, emptyRun));
|
||||
testZoneSpec.tick(6);
|
||||
expect(ran).toEqual(false);
|
||||
|
||||
cancelAnimationFrameFn(id);
|
||||
|
||||
testZoneSpec.tick(10);
|
||||
expect(ran).toEqual(false);
|
||||
});
|
||||
});
|
||||
it('is not flushed when flushPeriodic is false', () => {
|
||||
let ran = false;
|
||||
fakeAsyncTestZone.run(() => {
|
||||
requestAnimationFrameFn(() => {
|
||||
ran = true;
|
||||
});
|
||||
testZoneSpec.flush(20);
|
||||
expect(ran).toEqual(false);
|
||||
});
|
||||
});
|
||||
it('is flushed when flushPeriodic is true', () => {
|
||||
let ran = false;
|
||||
fakeAsyncTestZone.run(() => {
|
||||
requestAnimationFrameFn(() => {
|
||||
ran = true;
|
||||
});
|
||||
const elapsed = testZoneSpec.flush(20, true);
|
||||
expect(elapsed).toEqual(16);
|
||||
expect(ran).toEqual(true);
|
||||
});
|
||||
});
|
||||
it('should pass timestamp as parameter', () => {
|
||||
let timestamp = 0;
|
||||
let timestamp1 = 0;
|
||||
fakeAsyncTestZone.run(() => {
|
||||
requestAnimationFrameFn((ts) => {
|
||||
timestamp = ts;
|
||||
requestAnimationFrameFn(ts1 => {
|
||||
timestamp1 = ts1;
|
||||
});
|
||||
});
|
||||
const elapsed = testZoneSpec.flush(20, true);
|
||||
const elapsed1 = testZoneSpec.flush(20, true);
|
||||
expect(elapsed).toEqual(16);
|
||||
expect(elapsed1).toEqual(16);
|
||||
expect(timestamp).toEqual(16);
|
||||
expect(timestamp1).toEqual(32);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe(
|
||||
'XHRs', ifEnvSupports('XMLHttpRequest', () => {
|
||||
|
||||
@@ -22,7 +22,9 @@ describe('SyncTestZoneSpec', () => {
|
||||
syncTestZone.run(() => {
|
||||
expect(() => {
|
||||
Promise.resolve().then(function() {});
|
||||
}).toThrow(new Error('Cannot call Promise.then from within a sync test.'));
|
||||
})
|
||||
.toThrow(new Error(
|
||||
'Cannot call Promise.then from within a sync test (syncTestZone for name).'));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -30,7 +32,9 @@ describe('SyncTestZoneSpec', () => {
|
||||
syncTestZone.run(() => {
|
||||
expect(() => {
|
||||
setTimeout(() => {}, 100);
|
||||
}).toThrow(new Error('Cannot call setTimeout from within a sync test.'));
|
||||
})
|
||||
.toThrow(
|
||||
new Error('Cannot call setTimeout from within a sync test (syncTestZone for name).'));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -10,23 +10,23 @@
|
||||
"@jridgewell/gen-mapping" "^0.1.0"
|
||||
"@jridgewell/trace-mapping" "^0.3.9"
|
||||
|
||||
"@angular-devkit/architect@0.1401.0-next.4":
|
||||
version "0.1401.0-next.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1401.0-next.4.tgz#5e63f645f47c42f9bfe13296cb0c561cc182c284"
|
||||
integrity sha512-jPL5rlJX06J7g7FagL3FGnd/srKlg7TjwgU8EIXyIhZKVzT/jJEjY3esi6n0bBOXsErwU1SuF95s9x0W0vRvRg==
|
||||
"@angular-devkit/architect@0.1401.0-rc.3":
|
||||
version "0.1401.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1401.0-rc.3.tgz#55444857b8c041bfcfa57825047933588e917374"
|
||||
integrity sha512-ODAKO/cQNCpcTL+TIDxU6lPJB6kYT7raMg68JGc0bCiV82BEBZPE94xAIs/NA3B18WWEB6AxX6QgSVp1r963bg==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "14.1.0-next.4"
|
||||
"@angular-devkit/core" "14.1.0-rc.3"
|
||||
rxjs "6.6.7"
|
||||
|
||||
"@angular-devkit/build-angular@14.1.0-next.4":
|
||||
version "14.1.0-next.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-14.1.0-next.4.tgz#2c8ac035d757fc44af73943814bc05c7898a31be"
|
||||
integrity sha512-H5WOmUkQpk29GQF2YCfi72fZH1/XVjWRlgoxpV6cP+tmHk5XDH43uSk8lFVGfw+S2+2XguJORYsJgks4CvquIA==
|
||||
"@angular-devkit/build-angular@14.1.0-rc.3":
|
||||
version "14.1.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-14.1.0-rc.3.tgz#ce1ea6fd47e723b84ed33a373997dafa6bdc30f4"
|
||||
integrity sha512-O+68mRbaGdXXxsXxOboxwaKEtfx4++kmU+Wqz1DrYNBxNkBrS4/kVIO94YBvVI0zMRJ5wgnakqw/Be9MCIi2uQ==
|
||||
dependencies:
|
||||
"@ampproject/remapping" "2.2.0"
|
||||
"@angular-devkit/architect" "0.1401.0-next.4"
|
||||
"@angular-devkit/build-webpack" "0.1401.0-next.4"
|
||||
"@angular-devkit/core" "14.1.0-next.4"
|
||||
"@angular-devkit/architect" "0.1401.0-rc.3"
|
||||
"@angular-devkit/build-webpack" "0.1401.0-rc.3"
|
||||
"@angular-devkit/core" "14.1.0-rc.3"
|
||||
"@babel/core" "7.18.6"
|
||||
"@babel/generator" "7.18.7"
|
||||
"@babel/helper-annotate-as-pure" "7.18.6"
|
||||
@@ -37,7 +37,7 @@
|
||||
"@babel/runtime" "7.18.6"
|
||||
"@babel/template" "7.18.6"
|
||||
"@discoveryjs/json-ext" "0.5.7"
|
||||
"@ngtools/webpack" "14.1.0-next.4"
|
||||
"@ngtools/webpack" "14.1.0-rc.3"
|
||||
ansi-colors "4.1.3"
|
||||
babel-loader "8.2.5"
|
||||
babel-plugin-istanbul "6.1.1"
|
||||
@@ -107,18 +107,18 @@
|
||||
typescript "3.2.4"
|
||||
webpack-sources "1.3.0"
|
||||
|
||||
"@angular-devkit/build-webpack@0.1401.0-next.4":
|
||||
version "0.1401.0-next.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1401.0-next.4.tgz#4d893b086e76114f34ad77f8eb06c082bb924bd5"
|
||||
integrity sha512-/gOTPmc8OZgMP0n8ec/FMfhTveVHIJyPR68/5IARUnpbUHEmJPZL98zahDgcFST3B05Fg8Xq1rmKOq13oV5Khg==
|
||||
"@angular-devkit/build-webpack@0.1401.0-rc.3":
|
||||
version "0.1401.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1401.0-rc.3.tgz#8634d47e669b8a47ece8e12b71e2a2f5726a6a56"
|
||||
integrity sha512-he1xa9A9hEFjxZUoEEVfG7dOmNqsvuxJHPNCvQsM2vx7qzxW0/aZaNJEHi6bY9loDJjAftvd9zYprmeloxTdmQ==
|
||||
dependencies:
|
||||
"@angular-devkit/architect" "0.1401.0-next.4"
|
||||
"@angular-devkit/architect" "0.1401.0-rc.3"
|
||||
rxjs "6.6.7"
|
||||
|
||||
"@angular-devkit/core@14.1.0-next.4":
|
||||
version "14.1.0-next.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-14.1.0-next.4.tgz#af9885036517252380ff13817b9c2175f63c26cf"
|
||||
integrity sha512-RU6HIO47jnDr4LmVgwiwxtAh1ZftKKkLTh6JdF3O+TyxVIczXAG9fAv0fGEEocCWXjR3+CNPOizMtpPp9H7Seg==
|
||||
"@angular-devkit/core@14.1.0-rc.3":
|
||||
version "14.1.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-14.1.0-rc.3.tgz#b9d69c5216e7b7ac3f035a93b341d3480ff71e99"
|
||||
integrity sha512-KTrKoRJ12kuO1MVRJamaNc2v8avq0/roZ0dVlazXASs+BWlG/6x8fpBqF5ZodTF8M+rfYLkOSpJFlkWOBWkkyA==
|
||||
dependencies:
|
||||
ajv "8.11.0"
|
||||
ajv-formats "2.1.1"
|
||||
@@ -126,12 +126,12 @@
|
||||
rxjs "6.6.7"
|
||||
source-map "0.7.4"
|
||||
|
||||
"@angular-devkit/schematics@14.1.0-next.4":
|
||||
version "14.1.0-next.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-14.1.0-next.4.tgz#ff36f676a4dcfc27b51d54236c49161e465e3ffa"
|
||||
integrity sha512-YJBkX7lBpEf+POJ+ah2qrOopRLAeB3UfrkoIKs4rDS2Cbbu0tUhjYmvzSnBfFM1bqMxTW5+RYai+8exDqCeKag==
|
||||
"@angular-devkit/schematics@14.1.0-rc.3":
|
||||
version "14.1.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-14.1.0-rc.3.tgz#acdfb0bcf8ac3a1d6c575388463abb70473de160"
|
||||
integrity sha512-9cBhU0uuQ3h2pffsPIGN7u4BQew6RJwjoLC1Tmq2/OMt5J6yGySoDWJTmmfUoMfmst9hvCEmqhlmupTPuvsrrg==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "14.1.0-next.4"
|
||||
"@angular-devkit/core" "14.1.0-rc.3"
|
||||
jsonc-parser "3.0.0"
|
||||
magic-string "0.26.2"
|
||||
ora "5.4.1"
|
||||
@@ -152,24 +152,24 @@
|
||||
"@angular/core" "^13.0.0 || ^14.0.0-0"
|
||||
reflect-metadata "^0.1.13"
|
||||
|
||||
"@angular/cdk@14.1.0-next.3":
|
||||
version "14.1.0-next.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-14.1.0-next.3.tgz#6f46f37a58119a49a26143dbf80d65c55aa9144b"
|
||||
integrity sha512-1JYKi3d32RbtXRXg/xyVbbLWcDHp80Q7I7wNFSqkxxg/Wmx2z/Rpd+3KAPtw/z42dyZPC66kUsZAfGJ/J8vg5Q==
|
||||
"@angular/cdk@14.1.0-rc.0":
|
||||
version "14.1.0-rc.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-14.1.0-rc.0.tgz#ab41784f05848a4a9f72b398592c740c92e2f6c8"
|
||||
integrity sha512-4Xpx8bRGe1x8vIR16x5vmBzEVB8j3TgRlRSUHk33Zaqmj9juWDBwybgTVTUnfqvimDTo/JYFAyzXHcY5omy6HA==
|
||||
dependencies:
|
||||
tslib "^2.3.0"
|
||||
optionalDependencies:
|
||||
parse5 "^5.0.0"
|
||||
|
||||
"@angular/cli@14.1.0-next.4":
|
||||
version "14.1.0-next.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-14.1.0-next.4.tgz#df05563e432c235ebdbf987faae4769b613bbf8a"
|
||||
integrity sha512-qT3fuZP6IHHZRAfEh3fKjkxElOtovQ+K37EYmShTgksWDuKyZ2I3OFxoeoBGdbSfP2KJCRAmHds8pL5uZaLErQ==
|
||||
"@angular/cli@14.1.0-rc.3":
|
||||
version "14.1.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-14.1.0-rc.3.tgz#aa26f4cbea5fceeae640642ca1a2317f0eb48716"
|
||||
integrity sha512-NxOH0Gd8PPKnW/H+rcm1x15BQzhpL56pbl2yvY1WoorVAkMfQf0M4hCzb4gGjtI+cOh1DBnfMJDtU+Sx5R2gsg==
|
||||
dependencies:
|
||||
"@angular-devkit/architect" "0.1401.0-next.4"
|
||||
"@angular-devkit/core" "14.1.0-next.4"
|
||||
"@angular-devkit/schematics" "14.1.0-next.4"
|
||||
"@schematics/angular" "14.1.0-next.4"
|
||||
"@angular-devkit/architect" "0.1401.0-rc.3"
|
||||
"@angular-devkit/core" "14.1.0-rc.3"
|
||||
"@angular-devkit/schematics" "14.1.0-rc.3"
|
||||
"@schematics/angular" "14.1.0-rc.3"
|
||||
"@yarnpkg/lockfile" "1.1.0"
|
||||
ansi-colors "4.1.3"
|
||||
debug "4.3.4"
|
||||
@@ -208,12 +208,12 @@
|
||||
dependencies:
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@angular/dev-infra-private@https://github.com/angular/dev-infra-private-builds.git#294d737341a13e130fb7184268c0653c335ef388":
|
||||
version "0.0.0-2eca9e5ee9e09a0d8c25ff37d3f063982478b411"
|
||||
uid "294d737341a13e130fb7184268c0653c335ef388"
|
||||
resolved "https://github.com/angular/dev-infra-private-builds.git#294d737341a13e130fb7184268c0653c335ef388"
|
||||
"@angular/dev-infra-private@https://github.com/angular/dev-infra-private-builds.git#cdaaa155363dfe5a67493d71803a4b721f01cbad":
|
||||
version "0.0.0-4b8b706a0cd6ae9bc542fe74417040375239aeeb"
|
||||
uid cdaaa155363dfe5a67493d71803a4b721f01cbad
|
||||
resolved "https://github.com/angular/dev-infra-private-builds.git#cdaaa155363dfe5a67493d71803a4b721f01cbad"
|
||||
dependencies:
|
||||
"@angular-devkit/build-angular" "14.1.0-next.4"
|
||||
"@angular-devkit/build-angular" "14.1.0-rc.3"
|
||||
"@angular/benchpress" "0.3.0"
|
||||
"@babel/core" "^7.16.0"
|
||||
"@bazel/buildifier" "5.1.0"
|
||||
@@ -253,10 +253,10 @@
|
||||
dependencies:
|
||||
tslib "^2.2.0"
|
||||
|
||||
"@angular/material@14.1.0-next.3":
|
||||
version "14.1.0-next.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular/material/-/material-14.1.0-next.3.tgz#41645732a37e14f3f0cd47f13e1121366185ed81"
|
||||
integrity sha512-7Pp408tvXEYXjW9VDeBpZIMKzDol1LuwsMIWfRxiNJ9ALer/vJSNb0dz5Q91OWkAyDEutrwHfhcogNPIgudJLQ==
|
||||
"@angular/material@14.1.0-rc.0":
|
||||
version "14.1.0-rc.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/material/-/material-14.1.0-rc.0.tgz#16f9c013f1d937e68c34e331ce97c73edd5e0945"
|
||||
integrity sha512-1Cu1+tiVBO/c9Tm8LL68T/HajqLTMUMcmtPUw0yS1vJBtOfuLESm/9/p60bzrZLdMg8a5u3gMB+kaAj0jRzwJA==
|
||||
dependencies:
|
||||
tslib "^2.3.0"
|
||||
|
||||
@@ -305,10 +305,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@assemblyscript/loader/-/loader-0.10.1.tgz#70e45678f06c72fa2e350e8553ec4a4d72b92e06"
|
||||
integrity sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==
|
||||
|
||||
"@babel/cli@7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.18.6.tgz#b1228eb9196b34d608155a47508011d9e47ab1f2"
|
||||
integrity sha512-jXNHoYCbxZ8rKy+2lyy0VjcaGxS4NPbN0qc95DjIiGZQL/mTNx3o2/yI0TG+X0VrrTuwmO7zH52T9NcNdbF9Uw==
|
||||
"@babel/cli@7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.18.9.tgz#1fbc8424e5f74ae08bc61ec71609af29287d82d2"
|
||||
integrity sha512-e7TOtHVrAXBJGNgoROVxqx0mathd01oJGXIDekRfxdrISnRqfM795APwkDtse9GdyPYivjg3iXiko3sF3W7f5Q==
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping" "^0.3.8"
|
||||
commander "^4.0.1"
|
||||
@@ -328,7 +328,7 @@
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.18.6"
|
||||
|
||||
"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.18.6":
|
||||
"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.18.6", "@babel/compat-data@^7.18.8":
|
||||
version "7.18.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d"
|
||||
integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==
|
||||
@@ -354,6 +354,27 @@
|
||||
json5 "^2.2.1"
|
||||
semver "^6.3.0"
|
||||
|
||||
"@babel/core@7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.9.tgz#805461f967c77ff46c74ca0460ccf4fe933ddd59"
|
||||
integrity sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g==
|
||||
dependencies:
|
||||
"@ampproject/remapping" "^2.1.0"
|
||||
"@babel/code-frame" "^7.18.6"
|
||||
"@babel/generator" "^7.18.9"
|
||||
"@babel/helper-compilation-targets" "^7.18.9"
|
||||
"@babel/helper-module-transforms" "^7.18.9"
|
||||
"@babel/helpers" "^7.18.9"
|
||||
"@babel/parser" "^7.18.9"
|
||||
"@babel/template" "^7.18.6"
|
||||
"@babel/traverse" "^7.18.9"
|
||||
"@babel/types" "^7.18.9"
|
||||
convert-source-map "^1.7.0"
|
||||
debug "^4.1.0"
|
||||
gensync "^1.0.0-beta.2"
|
||||
json5 "^2.2.1"
|
||||
semver "^6.3.0"
|
||||
|
||||
"@babel/generator@7.18.7", "@babel/generator@^7.18.6", "@babel/generator@^7.18.7":
|
||||
version "7.18.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.7.tgz#2aa78da3c05aadfc82dbac16c99552fc802284bd"
|
||||
@@ -363,6 +384,15 @@
|
||||
"@jridgewell/gen-mapping" "^0.3.2"
|
||||
jsesc "^2.5.1"
|
||||
|
||||
"@babel/generator@7.18.9", "@babel/generator@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.9.tgz#68337e9ea8044d6ddc690fb29acae39359cca0a5"
|
||||
integrity sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug==
|
||||
dependencies:
|
||||
"@babel/types" "^7.18.9"
|
||||
"@jridgewell/gen-mapping" "^0.3.2"
|
||||
jsesc "^2.5.1"
|
||||
|
||||
"@babel/helper-annotate-as-pure@7.18.6", "@babel/helper-annotate-as-pure@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb"
|
||||
@@ -388,6 +418,16 @@
|
||||
browserslist "^4.20.2"
|
||||
semver "^6.3.0"
|
||||
|
||||
"@babel/helper-compilation-targets@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf"
|
||||
integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.18.8"
|
||||
"@babel/helper-validator-option" "^7.18.6"
|
||||
browserslist "^4.20.2"
|
||||
semver "^6.3.0"
|
||||
|
||||
"@babel/helper-create-class-features-plugin@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.6.tgz#6f15f8459f3b523b39e00a99982e2c040871ed72"
|
||||
@@ -428,6 +468,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.6.tgz#b7eee2b5b9d70602e59d1a6cad7dd24de7ca6cd7"
|
||||
integrity sha512-8n6gSfn2baOY+qlp+VSzsosjCVGFqWKmDF0cCWOybh52Dw3SEyoWR1KrhMJASjLwIEkkAufZ0xvr+SxLHSpy2Q==
|
||||
|
||||
"@babel/helper-environment-visitor@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
|
||||
integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
|
||||
|
||||
"@babel/helper-explode-assignable-expression@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096"
|
||||
@@ -443,6 +488,14 @@
|
||||
"@babel/template" "^7.18.6"
|
||||
"@babel/types" "^7.18.6"
|
||||
|
||||
"@babel/helper-function-name@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0"
|
||||
integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==
|
||||
dependencies:
|
||||
"@babel/template" "^7.18.6"
|
||||
"@babel/types" "^7.18.9"
|
||||
|
||||
"@babel/helper-hoist-variables@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
|
||||
@@ -457,6 +510,13 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.18.6"
|
||||
|
||||
"@babel/helper-member-expression-to-functions@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815"
|
||||
integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==
|
||||
dependencies:
|
||||
"@babel/types" "^7.18.9"
|
||||
|
||||
"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
|
||||
@@ -478,6 +538,20 @@
|
||||
"@babel/traverse" "^7.18.8"
|
||||
"@babel/types" "^7.18.8"
|
||||
|
||||
"@babel/helper-module-transforms@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712"
|
||||
integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==
|
||||
dependencies:
|
||||
"@babel/helper-environment-visitor" "^7.18.9"
|
||||
"@babel/helper-module-imports" "^7.18.6"
|
||||
"@babel/helper-simple-access" "^7.18.6"
|
||||
"@babel/helper-split-export-declaration" "^7.18.6"
|
||||
"@babel/helper-validator-identifier" "^7.18.6"
|
||||
"@babel/template" "^7.18.6"
|
||||
"@babel/traverse" "^7.18.9"
|
||||
"@babel/types" "^7.18.9"
|
||||
|
||||
"@babel/helper-optimise-call-expression@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe"
|
||||
@@ -490,6 +564,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.6.tgz#9448974dd4fb1d80fefe72e8a0af37809cd30d6d"
|
||||
integrity sha512-gvZnm1YAAxh13eJdkb9EWHBnF3eAub3XTLCZEehHT2kWxiKVRL64+ae5Y6Ivne0mVHmMYKT+xWgZO+gQhuLUBg==
|
||||
|
||||
"@babel/helper-plugin-utils@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f"
|
||||
integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==
|
||||
|
||||
"@babel/helper-remap-async-to-generator@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.6.tgz#fa1f81acd19daee9d73de297c0308783cd3cfc23"
|
||||
@@ -511,6 +590,17 @@
|
||||
"@babel/traverse" "^7.18.6"
|
||||
"@babel/types" "^7.18.6"
|
||||
|
||||
"@babel/helper-replace-supers@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz#1092e002feca980fbbb0bd4d51b74a65c6a500e6"
|
||||
integrity sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==
|
||||
dependencies:
|
||||
"@babel/helper-environment-visitor" "^7.18.9"
|
||||
"@babel/helper-member-expression-to-functions" "^7.18.9"
|
||||
"@babel/helper-optimise-call-expression" "^7.18.6"
|
||||
"@babel/traverse" "^7.18.9"
|
||||
"@babel/types" "^7.18.9"
|
||||
|
||||
"@babel/helper-simple-access@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea"
|
||||
@@ -525,6 +615,13 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.18.6"
|
||||
|
||||
"@babel/helper-skip-transparent-expression-wrappers@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818"
|
||||
integrity sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==
|
||||
dependencies:
|
||||
"@babel/types" "^7.18.9"
|
||||
|
||||
"@babel/helper-split-export-declaration@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075"
|
||||
@@ -561,6 +658,15 @@
|
||||
"@babel/traverse" "^7.18.6"
|
||||
"@babel/types" "^7.18.6"
|
||||
|
||||
"@babel/helpers@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9"
|
||||
integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==
|
||||
dependencies:
|
||||
"@babel/template" "^7.18.6"
|
||||
"@babel/traverse" "^7.18.9"
|
||||
"@babel/types" "^7.18.9"
|
||||
|
||||
"@babel/highlight@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
|
||||
@@ -570,7 +676,12 @@
|
||||
chalk "^2.0.0"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/parser@7.18.8", "@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.6", "@babel/parser@^7.18.8":
|
||||
"@babel/parser@7.18.9", "@babel/parser@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539"
|
||||
integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==
|
||||
|
||||
"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.6", "@babel/parser@^7.18.8":
|
||||
version "7.18.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.8.tgz#822146080ac9c62dac0823bb3489622e0bc1cbdf"
|
||||
integrity sha512-RSKRfYX20dyH+elbJK2uqAkVyucL+xXzhqlMD5/ZXx+dAAwpyB7HsvnHe/ZUGOF+xLr5Wx9/JoXVTj6BQE2/oA==
|
||||
@@ -591,6 +702,15 @@
|
||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.18.6"
|
||||
"@babel/plugin-proposal-optional-chaining" "^7.18.6"
|
||||
|
||||
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz#a11af19aa373d68d561f08e0a57242350ed0ec50"
|
||||
integrity sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.18.9"
|
||||
"@babel/plugin-proposal-optional-chaining" "^7.18.9"
|
||||
|
||||
"@babel/plugin-proposal-async-generator-functions@7.18.6", "@babel/plugin-proposal-async-generator-functions@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.6.tgz#aedac81e6fc12bb643374656dd5f2605bf743d17"
|
||||
@@ -634,6 +754,14 @@
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
"@babel/plugin-syntax-export-namespace-from" "^7.8.3"
|
||||
|
||||
"@babel/plugin-proposal-export-namespace-from@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203"
|
||||
integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
"@babel/plugin-syntax-export-namespace-from" "^7.8.3"
|
||||
|
||||
"@babel/plugin-proposal-json-strings@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b"
|
||||
@@ -650,6 +778,14 @@
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
"@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
|
||||
|
||||
"@babel/plugin-proposal-logical-assignment-operators@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz#8148cbb350483bf6220af06fa6db3690e14b2e23"
|
||||
integrity sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
"@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
|
||||
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1"
|
||||
@@ -677,6 +813,17 @@
|
||||
"@babel/plugin-syntax-object-rest-spread" "^7.8.3"
|
||||
"@babel/plugin-transform-parameters" "^7.18.6"
|
||||
|
||||
"@babel/plugin-proposal-object-rest-spread@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz#f9434f6beb2c8cae9dfcf97d2a5941bbbf9ad4e7"
|
||||
integrity sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.18.8"
|
||||
"@babel/helper-compilation-targets" "^7.18.9"
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
"@babel/plugin-syntax-object-rest-spread" "^7.8.3"
|
||||
"@babel/plugin-transform-parameters" "^7.18.8"
|
||||
|
||||
"@babel/plugin-proposal-optional-catch-binding@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb"
|
||||
@@ -694,6 +841,15 @@
|
||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.18.6"
|
||||
"@babel/plugin-syntax-optional-chaining" "^7.8.3"
|
||||
|
||||
"@babel/plugin-proposal-optional-chaining@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993"
|
||||
integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.18.9"
|
||||
"@babel/plugin-syntax-optional-chaining" "^7.8.3"
|
||||
|
||||
"@babel/plugin-proposal-private-methods@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea"
|
||||
@@ -855,6 +1011,13 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
|
||||
"@babel/plugin-transform-block-scoping@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz#f9b7e018ac3f373c81452d6ada8bd5a18928926d"
|
||||
integrity sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
|
||||
"@babel/plugin-transform-classes@^7.18.6":
|
||||
version "7.18.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.8.tgz#7e85777e622e979c85c701a095280360b818ce49"
|
||||
@@ -869,6 +1032,20 @@
|
||||
"@babel/helper-split-export-declaration" "^7.18.6"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/plugin-transform-classes@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz#90818efc5b9746879b869d5ce83eb2aa48bbc3da"
|
||||
integrity sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==
|
||||
dependencies:
|
||||
"@babel/helper-annotate-as-pure" "^7.18.6"
|
||||
"@babel/helper-environment-visitor" "^7.18.9"
|
||||
"@babel/helper-function-name" "^7.18.9"
|
||||
"@babel/helper-optimise-call-expression" "^7.18.6"
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
"@babel/helper-replace-supers" "^7.18.9"
|
||||
"@babel/helper-split-export-declaration" "^7.18.6"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/plugin-transform-computed-properties@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.6.tgz#5d15eb90e22e69604f3348344c91165c5395d032"
|
||||
@@ -876,6 +1053,13 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
|
||||
"@babel/plugin-transform-computed-properties@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e"
|
||||
integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
|
||||
"@babel/plugin-transform-destructuring@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.6.tgz#a98b0e42c7ffbf5eefcbcf33280430f230895c6f"
|
||||
@@ -883,6 +1067,13 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
|
||||
"@babel/plugin-transform-destructuring@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz#68906549c021cb231bee1db21d3b5b095f8ee292"
|
||||
integrity sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
|
||||
"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8"
|
||||
@@ -898,6 +1089,13 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
|
||||
"@babel/plugin-transform-duplicate-keys@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e"
|
||||
integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
|
||||
"@babel/plugin-transform-exponentiation-operator@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd"
|
||||
@@ -906,7 +1104,7 @@
|
||||
"@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6"
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
|
||||
"@babel/plugin-transform-for-of@^7.18.6":
|
||||
"@babel/plugin-transform-for-of@^7.18.6", "@babel/plugin-transform-for-of@^7.18.8":
|
||||
version "7.18.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1"
|
||||
integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==
|
||||
@@ -922,6 +1120,15 @@
|
||||
"@babel/helper-function-name" "^7.18.6"
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
|
||||
"@babel/plugin-transform-function-name@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0"
|
||||
integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==
|
||||
dependencies:
|
||||
"@babel/helper-compilation-targets" "^7.18.9"
|
||||
"@babel/helper-function-name" "^7.18.9"
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
|
||||
"@babel/plugin-transform-literals@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.6.tgz#9d6af353b5209df72960baf4492722d56f39a205"
|
||||
@@ -929,6 +1136,13 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
|
||||
"@babel/plugin-transform-literals@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc"
|
||||
integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
|
||||
"@babel/plugin-transform-member-expression-literals@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e"
|
||||
@@ -966,6 +1180,17 @@
|
||||
"@babel/helper-validator-identifier" "^7.18.6"
|
||||
babel-plugin-dynamic-import-node "^2.3.3"
|
||||
|
||||
"@babel/plugin-transform-modules-systemjs@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz#545df284a7ac6a05125e3e405e536c5853099a06"
|
||||
integrity sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==
|
||||
dependencies:
|
||||
"@babel/helper-hoist-variables" "^7.18.6"
|
||||
"@babel/helper-module-transforms" "^7.18.9"
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
"@babel/helper-validator-identifier" "^7.18.6"
|
||||
babel-plugin-dynamic-import-node "^2.3.3"
|
||||
|
||||
"@babel/plugin-transform-modules-umd@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9"
|
||||
@@ -997,7 +1222,7 @@
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
"@babel/helper-replace-supers" "^7.18.6"
|
||||
|
||||
"@babel/plugin-transform-parameters@^7.18.6":
|
||||
"@babel/plugin-transform-parameters@^7.18.6", "@babel/plugin-transform-parameters@^7.18.8":
|
||||
version "7.18.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz#ee9f1a0ce6d78af58d0956a9378ea3427cccb48a"
|
||||
integrity sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==
|
||||
@@ -1053,6 +1278,14 @@
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.18.6"
|
||||
|
||||
"@babel/plugin-transform-spread@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz#6ea7a6297740f381c540ac56caf75b05b74fb664"
|
||||
integrity sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.18.9"
|
||||
|
||||
"@babel/plugin-transform-sticky-regex@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc"
|
||||
@@ -1067,6 +1300,13 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
|
||||
"@babel/plugin-transform-template-literals@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e"
|
||||
integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
|
||||
"@babel/plugin-transform-typeof-symbol@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.6.tgz#486bb39d5a18047358e0d04dc0d2f322f0b92e92"
|
||||
@@ -1074,6 +1314,13 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
|
||||
"@babel/plugin-transform-typeof-symbol@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0"
|
||||
integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
|
||||
"@babel/plugin-transform-unicode-escapes@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.6.tgz#0d01fb7fb2243ae1c033f65f6e3b4be78db75f27"
|
||||
@@ -1170,6 +1417,87 @@
|
||||
core-js-compat "^3.22.1"
|
||||
semver "^6.3.0"
|
||||
|
||||
"@babel/preset-env@7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.9.tgz#9b3425140d724fbe590322017466580844c7eaff"
|
||||
integrity sha512-75pt/q95cMIHWssYtyfjVlvI+QEZQThQbKvR9xH+F/Agtw/s4Wfc2V9Bwd/P39VtixB7oWxGdH4GteTTwYJWMg==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.18.8"
|
||||
"@babel/helper-compilation-targets" "^7.18.9"
|
||||
"@babel/helper-plugin-utils" "^7.18.9"
|
||||
"@babel/helper-validator-option" "^7.18.6"
|
||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6"
|
||||
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9"
|
||||
"@babel/plugin-proposal-async-generator-functions" "^7.18.6"
|
||||
"@babel/plugin-proposal-class-properties" "^7.18.6"
|
||||
"@babel/plugin-proposal-class-static-block" "^7.18.6"
|
||||
"@babel/plugin-proposal-dynamic-import" "^7.18.6"
|
||||
"@babel/plugin-proposal-export-namespace-from" "^7.18.9"
|
||||
"@babel/plugin-proposal-json-strings" "^7.18.6"
|
||||
"@babel/plugin-proposal-logical-assignment-operators" "^7.18.9"
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6"
|
||||
"@babel/plugin-proposal-numeric-separator" "^7.18.6"
|
||||
"@babel/plugin-proposal-object-rest-spread" "^7.18.9"
|
||||
"@babel/plugin-proposal-optional-catch-binding" "^7.18.6"
|
||||
"@babel/plugin-proposal-optional-chaining" "^7.18.9"
|
||||
"@babel/plugin-proposal-private-methods" "^7.18.6"
|
||||
"@babel/plugin-proposal-private-property-in-object" "^7.18.6"
|
||||
"@babel/plugin-proposal-unicode-property-regex" "^7.18.6"
|
||||
"@babel/plugin-syntax-async-generators" "^7.8.4"
|
||||
"@babel/plugin-syntax-class-properties" "^7.12.13"
|
||||
"@babel/plugin-syntax-class-static-block" "^7.14.5"
|
||||
"@babel/plugin-syntax-dynamic-import" "^7.8.3"
|
||||
"@babel/plugin-syntax-export-namespace-from" "^7.8.3"
|
||||
"@babel/plugin-syntax-import-assertions" "^7.18.6"
|
||||
"@babel/plugin-syntax-json-strings" "^7.8.3"
|
||||
"@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
|
||||
"@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
|
||||
"@babel/plugin-syntax-numeric-separator" "^7.10.4"
|
||||
"@babel/plugin-syntax-object-rest-spread" "^7.8.3"
|
||||
"@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
|
||||
"@babel/plugin-syntax-optional-chaining" "^7.8.3"
|
||||
"@babel/plugin-syntax-private-property-in-object" "^7.14.5"
|
||||
"@babel/plugin-syntax-top-level-await" "^7.14.5"
|
||||
"@babel/plugin-transform-arrow-functions" "^7.18.6"
|
||||
"@babel/plugin-transform-async-to-generator" "^7.18.6"
|
||||
"@babel/plugin-transform-block-scoped-functions" "^7.18.6"
|
||||
"@babel/plugin-transform-block-scoping" "^7.18.9"
|
||||
"@babel/plugin-transform-classes" "^7.18.9"
|
||||
"@babel/plugin-transform-computed-properties" "^7.18.9"
|
||||
"@babel/plugin-transform-destructuring" "^7.18.9"
|
||||
"@babel/plugin-transform-dotall-regex" "^7.18.6"
|
||||
"@babel/plugin-transform-duplicate-keys" "^7.18.9"
|
||||
"@babel/plugin-transform-exponentiation-operator" "^7.18.6"
|
||||
"@babel/plugin-transform-for-of" "^7.18.8"
|
||||
"@babel/plugin-transform-function-name" "^7.18.9"
|
||||
"@babel/plugin-transform-literals" "^7.18.9"
|
||||
"@babel/plugin-transform-member-expression-literals" "^7.18.6"
|
||||
"@babel/plugin-transform-modules-amd" "^7.18.6"
|
||||
"@babel/plugin-transform-modules-commonjs" "^7.18.6"
|
||||
"@babel/plugin-transform-modules-systemjs" "^7.18.9"
|
||||
"@babel/plugin-transform-modules-umd" "^7.18.6"
|
||||
"@babel/plugin-transform-named-capturing-groups-regex" "^7.18.6"
|
||||
"@babel/plugin-transform-new-target" "^7.18.6"
|
||||
"@babel/plugin-transform-object-super" "^7.18.6"
|
||||
"@babel/plugin-transform-parameters" "^7.18.8"
|
||||
"@babel/plugin-transform-property-literals" "^7.18.6"
|
||||
"@babel/plugin-transform-regenerator" "^7.18.6"
|
||||
"@babel/plugin-transform-reserved-words" "^7.18.6"
|
||||
"@babel/plugin-transform-shorthand-properties" "^7.18.6"
|
||||
"@babel/plugin-transform-spread" "^7.18.9"
|
||||
"@babel/plugin-transform-sticky-regex" "^7.18.6"
|
||||
"@babel/plugin-transform-template-literals" "^7.18.9"
|
||||
"@babel/plugin-transform-typeof-symbol" "^7.18.9"
|
||||
"@babel/plugin-transform-unicode-escapes" "^7.18.6"
|
||||
"@babel/plugin-transform-unicode-regex" "^7.18.6"
|
||||
"@babel/preset-modules" "^0.1.5"
|
||||
"@babel/types" "^7.18.9"
|
||||
babel-plugin-polyfill-corejs2 "^0.3.1"
|
||||
babel-plugin-polyfill-corejs3 "^0.5.2"
|
||||
babel-plugin-polyfill-regenerator "^0.3.1"
|
||||
core-js-compat "^3.22.1"
|
||||
semver "^6.3.0"
|
||||
|
||||
"@babel/preset-modules@^0.1.5":
|
||||
version "0.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9"
|
||||
@@ -1197,7 +1525,23 @@
|
||||
"@babel/parser" "^7.18.6"
|
||||
"@babel/types" "^7.18.6"
|
||||
|
||||
"@babel/traverse@7.18.8", "@babel/traverse@^7.13.0", "@babel/traverse@^7.18.6", "@babel/traverse@^7.18.8":
|
||||
"@babel/traverse@7.18.9", "@babel/traverse@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.9.tgz#deeff3e8f1bad9786874cb2feda7a2d77a904f98"
|
||||
integrity sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.18.6"
|
||||
"@babel/generator" "^7.18.9"
|
||||
"@babel/helper-environment-visitor" "^7.18.9"
|
||||
"@babel/helper-function-name" "^7.18.9"
|
||||
"@babel/helper-hoist-variables" "^7.18.6"
|
||||
"@babel/helper-split-export-declaration" "^7.18.6"
|
||||
"@babel/parser" "^7.18.9"
|
||||
"@babel/types" "^7.18.9"
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/traverse@^7.13.0", "@babel/traverse@^7.18.6", "@babel/traverse@^7.18.8":
|
||||
version "7.18.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.8.tgz#f095e62ab46abf1da35e5a2011f43aee72d8d5b0"
|
||||
integrity sha512-UNg/AcSySJYR/+mIcJQDCv00T+AqRO7j/ZEJLzpaYtgM48rMg5MnkJgyNqkzo88+p4tfRvZJCEiwwfG6h4jkRg==
|
||||
@@ -1213,7 +1557,15 @@
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/types@7.18.8", "@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.7", "@babel/types@^7.18.8", "@babel/types@^7.3.0", "@babel/types@^7.4.4":
|
||||
"@babel/types@7.18.9", "@babel/types@^7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f"
|
||||
integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.18.6"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.7", "@babel/types@^7.18.8", "@babel/types@^7.3.0", "@babel/types@^7.4.4":
|
||||
version "7.18.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.8.tgz#c5af199951bf41ba4a6a9a6d0d8ad722b30cd42f"
|
||||
integrity sha512-qwpdsmraq0aJ3osLJRApsc2ouSJCdnMeZwB0DhbtHAtRpZNZCdlbRnHIgcRKzdE1g0iOGg644fzjOBcdOz9cPw==
|
||||
@@ -1629,10 +1981,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz#155ef21065427901994e765da8a0ba0eaae8b8bd"
|
||||
integrity sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw==
|
||||
|
||||
"@ngtools/webpack@14.1.0-next.4":
|
||||
version "14.1.0-next.4"
|
||||
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-14.1.0-next.4.tgz#94cebdff456ba4169d19a2741cc787748547b3eb"
|
||||
integrity sha512-ziW+ElIhgrwFLM7nBkcwtli7iA6RMzZnvBAgQAEkRhyi9l969m/LSidAC73U0l06JiBWOzZAiGBgR/kd3uFW+w==
|
||||
"@ngtools/webpack@14.1.0-rc.3":
|
||||
version "14.1.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-14.1.0-rc.3.tgz#3cca4d15f464c56d287788b0723040fa0284d4bb"
|
||||
integrity sha512-9zKcMBWQkeFA8Ws6bf9fcJHOMq2W+VojL+PDQO6IYDqUW9hIMcR57FfiUilM5eu+wn0VanIx887CehIwywCZbQ==
|
||||
|
||||
"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3":
|
||||
version "2.1.8-no-fsevents.3"
|
||||
@@ -1911,13 +2263,13 @@
|
||||
colors "~1.2.1"
|
||||
string-argv "~0.3.1"
|
||||
|
||||
"@schematics/angular@14.1.0-next.4":
|
||||
version "14.1.0-next.4"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-14.1.0-next.4.tgz#2e01051cebe30fb9d8046993daf3e8d5dc7f6e4a"
|
||||
integrity sha512-JFT0Qb/DphIBeX+ipRs15pjWnzYUhTgx4S3pw9/0c3VQDk52hhPgQVumhkLDhcFCVk7VTmclEAsqOV8uFnQnog==
|
||||
"@schematics/angular@14.1.0-rc.3":
|
||||
version "14.1.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-14.1.0-rc.3.tgz#5610d20eda3f0b9bd1989e8e5494f3a56bf2a5de"
|
||||
integrity sha512-BvsQio/SBlNeq4kUr9sKjC5FyzR17kLwh0tgAfIhZkCkHKHmak30d/vppOcl14bK6JSy7zjZUPXu0AEImfERFA==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "14.1.0-next.4"
|
||||
"@angular-devkit/schematics" "14.1.0-next.4"
|
||||
"@angular-devkit/core" "14.1.0-rc.3"
|
||||
"@angular-devkit/schematics" "14.1.0-rc.3"
|
||||
jsonc-parser "3.0.0"
|
||||
|
||||
"@sideway/address@^4.1.3":
|
||||
@@ -8917,12 +9269,7 @@ jasmine-ajax@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/jasmine-ajax/-/jasmine-ajax-4.0.0.tgz#7d8ba7e47e3f7e780e155fe9aa563faafa7e1a26"
|
||||
integrity sha512-htTxNw38BSHxxmd8RRMejocdPqLalGHU6n3HWFbzp/S8AuTQd1MYjkSH3dYDsbZ7EV1Xqx/b94m3tKaVSVBV2A==
|
||||
|
||||
jasmine-core@^3.6.0:
|
||||
version "3.99.1"
|
||||
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.99.1.tgz#5bfa4b2d76618868bfac4c8ff08bb26fffa4120d"
|
||||
integrity sha512-Hu1dmuoGcZ7AfyynN3LsfruwMbxMALMka+YtZeGoLuDEySVmVAPaonkNoBRIw/ectu8b9tVQCJNgp4a4knp+tg==
|
||||
|
||||
jasmine-core@^4.0.0, jasmine-core@^4.2.0:
|
||||
jasmine-core@^4.0.0, jasmine-core@^4.1.0, jasmine-core@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-4.2.0.tgz#0605bea284d6d78276f43c47de2532ecd4a73b00"
|
||||
integrity sha512-OcFpBrIhnbmb9wfI8cqPSJ50pv3Wg4/NSgoZIqHzIwO/2a9qivJWzv8hUvaREIMYYJBas6AvfXATFdVuzzCqVw==
|
||||
@@ -9254,12 +9601,12 @@ karma-firefox-launcher@^2.1.0:
|
||||
is-wsl "^2.2.0"
|
||||
which "^2.0.1"
|
||||
|
||||
karma-jasmine@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-4.0.2.tgz#386db2a3e1acc0af5265c711f673f78f1e4938de"
|
||||
integrity sha512-ggi84RMNQffSDmWSyyt4zxzh2CQGwsxvYYsprgyR1j8ikzIduEdOlcLvXjZGwXG/0j41KUXOWsUCBfbEHPWP9g==
|
||||
karma-jasmine@^5.0.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-5.1.0.tgz#3af4558a6502fa16856a0f346ec2193d4b884b2f"
|
||||
integrity sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==
|
||||
dependencies:
|
||||
jasmine-core "^3.6.0"
|
||||
jasmine-core "^4.1.0"
|
||||
|
||||
karma-requirejs@^1.1.0:
|
||||
version "1.1.0"
|
||||
@@ -12792,7 +13139,6 @@ sass@1.53.0:
|
||||
|
||||
"sauce-connect@https://saucelabs.com/downloads/sc-4.7.1-linux.tar.gz":
|
||||
version "0.0.0"
|
||||
uid e5d7f82ad98251a653d1b0537f1103e49eda5e11
|
||||
resolved "https://saucelabs.com/downloads/sc-4.7.1-linux.tar.gz#e5d7f82ad98251a653d1b0537f1103e49eda5e11"
|
||||
|
||||
saucelabs@7.2.0, saucelabs@^1.5.0, saucelabs@^4.6.3:
|
||||
|
||||
Reference in New Issue
Block a user