Compare commits

...

45 Commits

Author SHA1 Message Date
Andrew Kushnir 793326deef release: cut the v14.1.0 release 2022-07-20 10:44:59 -07:00
Doug Parker 10d46b4247 refactor(bazel): remove experimental_extended_template_diagnostics flag (#46898)
Extended diagnostics are enabled by default now and this flag doesn't do anything anymore but I missed it in a previous cleanup.

PR Close #46898
2022-07-20 08:50:45 -07:00
dario-piotrowicz b744bf6a08 refactor(compiler-cli): improve error for non-exported non-standalone (#46114)
improve the error message for non-standalone components which are not
exported from their module, and that are also imported directly as if
they were standalone

this change simply adds the suggestion to the developer to import the
ngModule instead

resolves #46004

PR Close #46114
2022-07-20 08:50:01 -07:00
Andrew Kushnir 2e5bd88ebf refactor(core): NgModuleRef should not implement EnvironmentInjector interface (#46896)
This commit refactors the `NgModuleRef` implementation to drop functions required by the `EnvironmentInjector` interface. Previously the idea was that the `NgModuleRef` can act as an Injector to facilitate easier transition to standalone. However, from the mental model perspective, the `NgModuleRef` has the `injector` field, which is the correct injector reference and can be used is needed as an `EnvironmentInjector`.

PR Close #46896
2022-07-20 08:49:15 -07:00
Andrew Scott 9b8ea9b5ca Revert "fix(core): Fix runInContext for NgModuleRef injector (#46877)" (#46896)
This reverts commit 14081dc48d.

PR Close #46896
2022-07-20 08:49:15 -07:00
Angular Robot eb91035102 build: update cross-repo angular dependencies to cdaaa15 (#46853)
See associated pull request for more information.

PR Close #46853
2022-07-20 08:48:45 -07:00
Bob Watson 17e2a2a9d1 docs: add info about request timeouts (#46882)
* Adds a short note about how service workers report request timeouts
* Updated heading levels to present a clearer hierarchy and cleaner local TOC
* Removed documentation lint errors
* Moves the service worker service timeout paragraph to a more logical topic in service-worker-devops
* Removes line error from the more logical topic.
* address feedback from PR review comments

fixes #46445

PR Close #46882
2022-07-20 08:48:00 -07:00
Paul Gschwendtner b431b5c6d6 test: convert source-map core tests to use async/await instead of fakeAsync (#46888)
The source map tests rely on asynchronous logic from the `source-map`
package. The tests itself are written using `fakeAsync` but this
unnecessarily complicates the interaction with the asynchronous
source-map helpers/package.

To fix this, we just make the tests use async/await as we don't intend
to test fakeAsync in this `describe` block..

PR Close #46888
2022-07-19 09:40:57 -07:00
Paul Gschwendtner bc371dac44 build: disable jasminewd2 types for sourcemap playground test (#46888)
The sourcemap test in the e2e playground is now using async/await code.
This results in errors now with the Bazel TS compilation because it
detects that `expect` is returning a promise and should be awaited.

This happens due to the jasminewd2 types. We should just use the actual
jasmine types and not rely on the deprecated selenium control flow,
using explicit async/await in the whole test. This also solves the issue
with the source-map types being async/await now.

PR Close #46888
2022-07-19 09:40:57 -07:00
Paul Gschwendtner 36361b0506 test: update source-map tests to account for source-map breaking change (#46888)
The source-map package now requires the
`SourceMapConsumer`/`SourceMapGenerator` classes to be instantiated
asynchronously. This commit updates our tests to account for that.

PR Close #46888
2022-07-19 09:40:57 -07:00
Paul Gschwendtner 38f53a55c3 build: remove source-map package from compiler-cli bundling external list (#46888)
The source-map package is no longer explicitly used in the
`compiler-cli` package and therefore can be removed from the ESBuild
bundling, denoting it as external. This should be a noop.

PR Close #46888
2022-07-19 09:40:56 -07:00
Paul Gschwendtner 8de3d099d2 build: update source-map package to latest version (#46888)
The source-map package is updated as part #46707, but the minor
version bump introduced breaking changes. This commit extracts the
update so that we can handle it separately accounting for the breaking
changes. Also removing the caret since this package apparently makes
breaking changes in minor segment bumps.

PR Close #46888
2022-07-19 09:40:56 -07:00
Andrew Scott 945a3ad359 fix(core): Fix runInContext for NgModuleRef injector (#46877)
The `runInContext` for `NgModuleRef` was previously an infinite loop.

PR Close #46877
2022-07-19 09:37:32 -07:00
Angular Robot 88003e27db build: update github/codeql-action action to v2.1.16 (#46885)
See associated pull request for more information.

PR Close #46885
2022-07-19 16:05:06 +00:00
Angular Robot a4cba79440 build: update eslint dependencies to v5.30.7 (#46884)
See associated pull request for more information.

PR Close #46884
2022-07-19 16:04:37 +00:00
Angular Robot 6ca7a79bfd build: update babel dependencies to v7.18.9 (#46883)
See associated pull request for more information.

PR Close #46883
2022-07-19 16:04:09 +00:00
Andrew Scott e95ee96562 refactor(router): Update TitleStrategy to useFactory (#46876)
The implementation of the `DefaultTitleStrategy` was modeled after the
existing strategy patterns in the Router. These patterns were developed
before the `providedIn` syntax for injectables. We can simplify the
model a lot by providing the default in the factory of the abstract
class.

Note that the other strategy patterns aren't touched in this PR due to
how long they've existed. Because they have been there for such a long
time, it's possible there will need to be some adjustments to code
if/when they are refactored to do the same.

PR Close #46876
2022-07-18 22:02:19 +00:00
dario-piotrowicz 536ded2280 fix(animations): make sure falsy values are added to _globalTimelineStyles (#46863)
style values get added to the `_globalTimelineStyles` map in order to keep
them so that they can be used across different timelines

`_globalTimelineStyles` was previously a plain object but has been
refactored to a map in #44482, as part of the update a check has been
changed from a ternary operation to an or (||), causing falsy values (as 0)
not to be added to the map anymore, apply the nullish coalescing operator (??)
instead to make sure only `undefined` and `null` are filtered out

also since this aspect was clearly not covered by tests, add a new test
to ensure that such regression doesn't happen in the future

resolves #46833

PR Close #46863
2022-07-18 21:46:13 +00:00
Cédric Exbrayat 6b8e60c06a fix(compiler-cli): improve the missingControlFlowDirective message (#46846)
The extended diagnostics about missing control flow directive was only mentioning that the `CommonModule` should be imported.
Now that the control flow directives are available as standalone, the message mentions that directive itself can be imported.

The message now also mentions which import should be used for the directive (as it can be tricky to figure out that `NgForOf` is the directive corresponding to `*ngFor`).

PR Close #46846
2022-07-18 19:56:08 +00:00
Paul Gschwendtner 5004b6ebf1 test: update tests to not run jasmine done function in sync-test zone from describe block
There are some ZoneJS tests that fork the zone from the `describe` block
for testing the zone patching. This does cause the Jasmine `done`
function later in `it` specs to be invoked in the sync-test zone from
the original `describe` block. The `done` implementation now has
changed with the Karma Jasmine update and breaks because it now causes
tasks to be scheduled.

It is conceptually incorrect/invalid to take the describe sync zone and
run test logic with that sync zone.

```
 An error was thrown in afterAll
  error properties: Object({ originalStack: 'Error: Cannot call jasmine.execute().forceTask from within a sync test (syncTestZone for jasmine.describe#FileReader).
      at new ZoneAwareError (packages/zone.js/test/browser_test_rollup.umd.js:98:37)
      at e.onScheduleTask (packages/zone.js/bundles/zone-testing-bundle.umd.min.js:158:196)
      at e.scheduleTask (packages/zone.js/bundles/zone-testing-bundle.umd.min.js:14:7529)
      at t.scheduleTask (packages/zone.js/bundles/zone-testing-bundle.umd.min.js:14:3539)
      at t.scheduleMicroTask (packages/zone.js/bundles/zone-testing-bundle.umd.min.js:14:3791)
      at r.execute (packages/zone.js/bundles/zone-testing-bundle.umd.min.js:166:4312)
      at queueRunnerFa ...
      at <Jasmine>
```
2022-07-18 17:19:30 +00:00
Paul Gschwendtner 6c08635662 feat(zone.js): include jasmine describe block name when raising unexpected task error
As mentioned in the previous commit that ensured that the Zone name is
included in errors raised by the `SyncTestZoneSpec`, we can now include
the Jasmine describe block descriptions in such errors.

Errors can often happen when users accidentally try to set up Angular
tests without an `it` block. Resulting in errors where it's not clear
at all which describe block (of potentially a large repository) is
involved:

```
 An error was thrown in afterAll
  error properties: Object({ originalStack: 'Error: Cannot call XX from within a sync test.
      at new ZoneAwareError (packages/zone.js/test/browser_test_rollup.umd.js:98:37)
      at e.onScheduleTask (packages/zone.js/bundles/zone-testing-bundle.umd.min.js:158:196)
      at e.scheduleTask (packages/zone.js/bundles/zone-testing-bundle.umd.min.js:14:7529)
      at t.scheduleTask (packages/zone.js/bundles/zone-testing-bundle.umd.min.js:14:3539)
      at t.scheduleMicroTask (packages/zone.js/bundles/zone-testing-bundle.umd.min.js:14:3791)
      at r.execute (packages/zone.js/bundles/zone-testing-bundle.umd.min.js:166:4372)
      at queueRunnerFa ...
      at <Jasmine>
```

We now include the describe block description in the error, so that it
is easier to figure out the location of the culprit code.
2022-07-18 17:19:30 +00:00
Paul Gschwendtner f0add81caf feat(zone.js): include zone name when sync-test zone reports tasks
The sync-test zone is used in e.g. `describe` to raise an error when
there is asynchronous code scheduled in describe blocks. This commit
includes the zone name in such thrown errors to allow for us to include
the jasmine describe name in the error. This will be wired up in the
jasmine zonejs patches separately.
2022-07-18 17:18:49 +00:00
Paul Gschwendtner 21e7584c8c test: fix fake-async-test spec to properly test prefixed animation frame functions
In the fake async test for `zone.js` there is some logic to test various
prefixes of the request animation frame functions. This logic does not
have any effect currently, except for generating the same test at
maximum three times.

This commit fixes the test to actually test what it meant to do
initially.
2022-07-18 17:18:49 +00:00
Paul Gschwendtner 92c9661165 test: update tests to account for karma-jasmine v5.0.0
Karma jasmine updated the `jasmine-core` dependency. Jasmine is now more
strict when:

* The done callback is invoked multiple times
* The done callback is used, while a promise is also returned
* The done callback is treated as error when e.g. a number is returned
  as first argument. This was the case with `requestAnimationFrame`.
2022-07-18 17:18:49 +00:00
Paul Gschwendtner a462f91d3e fix(core): do not invoke jasmine done callback multiple times with waitForAsync
Currently tests written using `waitForAsync` would be prone to Jasmine
warnings or errors (depending on the version) for tests incorrectly
invoking asynchronous jasmine `done` callbacks multiple times.

This can happen because the async test zone logic schedules the
`done` callback to be called using `setTimeout`, but this could
be invoked multiple times, causing multiple `done` invocations to
be scheduled. Most of the issues have been resolved with #45025,
but it does not solve the case of multiple tasks finished and callbacks
being scheduled.

Technically, the current logic is built in way that _should_ result in
`_finishCallbackIfDone` and eventually the `done` callback to be invoked
at maximium once. This is unfortunately not the case in some rather
advanced/unexpected scenarios (like our AngularJS upgrade tests) where
the scenario is the following (and microtasks from before the actual
`waitForAsync` spec are still completing -- which is valid):

```
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.
--> we execute the `done` callback twice because the async zone spec state is "stable"
```
2022-07-18 17:18:49 +00:00
Angular Robot aa8d3ed69b build: update dependency karma-jasmine to v5
| datasource | package       | from  | to    |
| ---------- | ------------- | ----- | ----- |
| npm        | karma-jasmine | 4.0.2 | 5.1.0 |
2022-07-18 17:18:49 +00:00
piyush132000 2ca404dbfd docs: fix code style (#46862)
PR Close #46862
2022-07-18 16:44:21 +00:00
Cédric Exbrayat f9edb776ca refactor(forms): simplify group builder function (#46844)
Applies the same logic that we have in the `control` function.

PR Close #46844
2022-07-15 22:02:20 +00:00
Andrew Kushnir c195c3f73c refactor(core): annotate ComponentMirror object fields as readonly
This commit updates the `inputs` and `outputs` objects of the `ComponentMirror` to mark their properties as readonly.
2022-07-15 14:58:58 -07:00
Andrew Kushnir 114011d2a0 refactor(core): move the code to avoid circular dependencies
This commit refactors the code to move some functions around to avoid circular dependencies in TS imports. The newly added functions are now located in the `packages/core/src/render3/component.ts` file (instead of `packages/core/src/render3/component_ref.ts`), which is a better place for them anyway.
2022-07-15 14:58:58 -07:00
Andrew Kushnir b5153814af feat(core): add reflectComponentType function
This commit introduces a new function that allows creating a object which exposes a number of getters to retrieve information about a given component.
2022-07-15 14:58:58 -07:00
Andrew Kushnir e8e8e5f171 feat(core): add createComponent function
This commit introduces a new function that allows creating a `ComponentRef` instance based on provided Component and a set of options. The function can be used to cover a number of use-cases where the `ComponentFactory` symbol was used previously.
2022-07-15 14:58:58 -07:00
Jessica Janiuk 08fb7bc4ca Revert "Revert "build: update cross-repo angular dependencies to 84dd092 (#46848)" (#46849)" (#46852)
This reverts commit b2a74469de.

PR Close #46852
2022-07-15 21:41:59 +00:00
Jessica Janiuk 6d709273eb Revert "build: update cross-repo angular dependencies to 84dd092 (#46848)" (#46849)
This reverts commit 510f45957a.

PR Close #46849
2022-07-15 17:52:46 +00:00
Angular Robot 571d7debb5 build: update cross-repo angular dependencies to 84dd092 (#46848)
See associated pull request for more information.

PR Close #46848
2022-07-15 16:00:34 +00:00
jnizet b1ba6429d4 docs(core): document that destroyAfterEach is true by default (#46845)
PR Close #46845
2022-07-15 15:50:36 +00:00
Jessica Janiuk 6c104a28e0 build: Add Jessica and Andrew K to docs-infra (#46843)
This adds two people to the docs-infra pullapprove group.

PR Close #46843
2022-07-15 15:46:06 +00:00
Angular Robot 5e09cd1f67 build: update cross-repo angular dependencies (#46834)
See associated pull request for more information.

PR Close #46834
2022-07-14 22:35:09 +00:00
Andrew Scott 6a7b818d94 fix(router): Ensure target RouterStateSnapshot is defined in NavigationError (#46842)
The Router transition observable pipe keeps an outer reference to a `t`
variable for use in the `catchError` operator. However, this variable is
not updated with intermediate state. This commit fixes that so the
`catchError` can access properties that get updated in intermediate
states. Specifically, `RouterStateSnapshot` in the `NavigationError` for
now but could be more in the future.

PR Close #46842
2022-07-14 22:19:27 +00:00
Bob Watson 9ecaa05c7d docs: correct debugging task step (#46832)
Also remove lint errors.

Fix: #42781

PR Close #46832
2022-07-13 22:55:21 +00:00
Andrew Kushnir d36a9e6bf3 refactor(core): improve an error message when ENVIRONMENT_INITIALIZER is not a multi provider (#46829)
Currently if the `ENVIRONMENT_INITIALIZER` token is not configured with `multi: true` flag, the code fails while trying to iterate over the value. This commit checks whether the `ENVIRONMENT_INITIALIZER` token value type is an array and throws a helpful error message.

PR Close #46829
2022-07-13 22:01:15 +00:00
George Kalpakas 4b452ffd3d docs: redirect /guide/ivy to the v12 guide (#46820)
Since Ivy is the default since v13, the Ivy guide (that used to live at
https://angular.io/guide/ivy) has been removed (see #43860). However,
there are certain error messages emitted by the CLI that still point to
it.

This commit address the problem by adding a redirect from
https://angular.io/guide/ivy to https://v12.angular.io/guide/ivy (which
is the last version that includes the guide).

Fixes #46717

PR Close #46820
2022-07-13 21:59:19 +00:00
Kristiyan Kostadinov 3f2d16c823 fix(compiler): inputs/outputs incorrectly parsed in jit mode (#46813)
The `Directive` and `Component` decorators support `inputs` and `outputs` fields which accept an array in the format of `"someInput"` or `"someInput: someAlias"`, however the parsing during JIT compilation was splitting on commas, not on colons, which resulted in incorrect parsing. E.g. `inputs: ["someInput: someAlias"]` was being parsed into `{"someInput: someAlias": "someInput: someAlias"}` instead of `{someInput: "someAlias"}`.

The feature was working by accident, because there's some logic further down in the compiler pipeline that was splitting the strings again.

PR Close #46813
2022-07-13 21:26:51 +00:00
acvi 4dca1842b3 docs: describe smallest typo, "an" -> "a" (#46812)
PR Close #46812
2022-07-13 20:06:30 +00:00
Jessica Janiuk 442b16c551 release: cut the v14.1.0-rc.0 release (#46830) 2022-07-13 12:37:14 -07:00
85 changed files with 2838 additions and 1814 deletions
+2 -2
View File
@@ -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 }}
+1 -1
View File
@@ -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 }}
+1 -1
View File
@@ -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 }}
+1 -1
View File
@@ -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
View File
@@ -1062,8 +1062,9 @@ groups:
])
reviewers:
users:
- alxhub
- AndrewKushnir
- gkalpak
- jessicajaniuk
- josephperrott
# =========================================================
+68 -131
View File
@@ -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() { }
}
@@ -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() {
}
}
+51 -41
View File
@@ -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&nbsp;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 &mdash;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">
+72 -57
View File
@@ -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 &mdash;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
+6 -6
View File
@@ -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">
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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":
+2
View File
@@ -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,
+27
View File
@@ -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;
+4
View File
@@ -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
+1 -2
View File
@@ -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)
+15 -8
View File
@@ -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
View File
@@ -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"],
-1
View File
@@ -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
@@ -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];
}
@@ -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');
}
/**
+4 -4
View File
@@ -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);
}
+3 -7
View File
@@ -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);
+1
View File
@@ -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) {
+8
View File
@@ -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();
}
+1
View File
@@ -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 ${
+164 -216
View File
@@ -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;
}
+231 -13
View File
@@ -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;
}
+12 -7
View File
@@ -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 {
+1 -1
View File
@@ -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';
+4 -14
View File
@@ -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 {
+315 -1
View File
@@ -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);
}
}
});
+6 -6
View File
@@ -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;
+1 -1
View File
@@ -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());
});
});
});
+1 -1
View File
@@ -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`. */
+9 -20
View File
@@ -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. */
+7
View File
@@ -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);
+1 -1
View File
@@ -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"
},
+2 -2
View File
@@ -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[],
+2 -1
View File
@@ -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
View File
@@ -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>;
}
+8 -8
View File
@@ -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);
+16 -15
View File
@@ -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');
})();
});
});
+29
View File
@@ -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)) {
+10 -6
View File
@@ -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.
+25 -4
View File
@@ -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);
}
+1 -1
View File
@@ -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);
+112 -117
View File
@@ -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);
});
});
});
}
});
});
+1 -1
View File
@@ -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).'));
});
});
+426 -80
View File
@@ -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: