mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| de1e35b944 | |||
| a38ae0aefa | |||
| fe3e4d6865 | |||
| 6cb7c3ebe3 | |||
| 966343bb92 | |||
| 7e93513425 | |||
| 7c7a1a1724 | |||
| 62217efb7e | |||
| 1fa14e0c84 | |||
| a71eb4c18e | |||
| ffd62b9f11 | |||
| e545dd8130 | |||
| 4883b7d9c7 | |||
| 8eb1f76341 | |||
| 37bbd97d6e | |||
| 9e9e82d7c7 | |||
| e12dfa535b | |||
| fa7c7afbe9 | |||
| a4aa9b3c28 | |||
| 40f27a31d2 | |||
| 94bfcdd9de | |||
| f0a8303abe | |||
| abd1bc8039 | |||
| 7316e72ec5 | |||
| ac2f262e9a | |||
| f2fa67eb0b | |||
| 9bee561956 | |||
| fce521e4d2 | |||
| ade305757b | |||
| ed67a074ce | |||
| 43eb934e6b | |||
| d2ae96f742 | |||
| 5626b34264 | |||
| 1671212a55 | |||
| 100091ebf0 | |||
| b7ff6f06de | |||
| 6b75263303 | |||
| caeb162513 |
@@ -1271,6 +1271,7 @@ groups:
|
||||
'docs/NAMING.md',
|
||||
'aio/content/guide/angular-package-format.md',
|
||||
'aio/content/errors/*.md',
|
||||
'aio/content/extended-diagnostics/*.md',
|
||||
'aio/content/guide/glossary.md',
|
||||
'aio/content/guide/styleguide.md',
|
||||
'aio/content/examples/errors/**',
|
||||
|
||||
+65
-61
@@ -1,3 +1,68 @@
|
||||
<a name="13.2.0"></a>
|
||||
# 13.2.0 (2022-01-26)
|
||||
## Deprecations
|
||||
###
|
||||
- The `CachedResourceLoader` and `RESOURCE_CACHE_PROVIDER` symbols were previously necessary in some cases to test AOT-compiled components with View Engine, but they are no longer needed since Ivy.
|
||||
|
||||
- The `ComponentFactory` and `ComponentFactoryResolver` classes are deprecated. Since Ivy, there is no need to resolve Component factories. Please use other APIs where you Component classes can be used directly (without resolving their factories).
|
||||
|
||||
- Since Ivy, the CompilerOptions.useJit and CompilerOptions.missingTranslation config options are unused, passing them has no effect.
|
||||
|
||||
- tap operator subscribe signature is deprecated
|
||||
|
||||
Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments
|
||||
###
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [9c11183e74](https://github.com/angular/angular/commit/9c11183e74980b12c3c5712df174e90af6f19027) | docs | deprecate `CachedResourceLoader` and `RESOURCE_CACHE_PROVIDER` symbols ([#44749](https://github.com/angular/angular/pull/44749)) |
|
||||
| [9f12e7fea4](https://github.com/angular/angular/commit/9f12e7fea434c6d33e1155994e2c228d51520744) | docs | deprecate `ComponentFactory` and `ComponentFactoryResolver` symbols ([#44749](https://github.com/angular/angular/pull/44749)) |
|
||||
| [4e95a316ce](https://github.com/angular/angular/commit/4e95a316cef35a771cd8168e3a744eb6bd7f1615) | docs | deprecate unused config options from the `CompilerOptions` interface ([#44749](https://github.com/angular/angular/pull/44749)) |
|
||||
| [34d2292323](https://github.com/angular/angular/commit/34d2292323a5b5c0751a5f2a3c85ccfe4427bfb5) | docs | update deprecated form of the tap operator in http example ([#44738](https://github.com/angular/angular/pull/44738)) |
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [ed21f5c753](https://github.com/angular/angular/commit/ed21f5c75378e1ce717ee3d76d28c8c994209de1) | feat | add injector input to ngTemplateOutlet ([#44761](https://github.com/angular/angular/pull/44761)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [a4ab6d6b72](https://github.com/angular/angular/commit/a4ab6d6b72fe891f232d2446f8b7c454a82a5ba0) | feat | add support for safe calls in templates ([#44580](https://github.com/angular/angular/pull/44580)) |
|
||||
| [abd1bc8039](https://github.com/angular/angular/commit/abd1bc8039ade4324f91fd616c82e52f05bdbb70) | fix | correct spans when parsing bindings with comments ([#44785](https://github.com/angular/angular/pull/44785)) |
|
||||
| [ed67a074ce](https://github.com/angular/angular/commit/ed67a074ceed6440bf7f638512a20bbd3be8cbd4) | fix | properly compile DI factories when coverage reporting is enabled ([#44732](https://github.com/angular/angular/pull/44732)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [fa835b5a29](https://github.com/angular/angular/commit/fa835b5a297087087eac2750b662096ed5492cb6) | feat | enable extended diagnostics by default ([#44712](https://github.com/angular/angular/pull/44712)) |
|
||||
| [73424def13](https://github.com/angular/angular/commit/73424def130ae1d72036142f6f2d7bce63858c9e) | feat | provide the animations for `DirectiveMeta` ([#44630](https://github.com/angular/angular/pull/44630)) |
|
||||
| [fe3e4d6865](https://github.com/angular/angular/commit/fe3e4d6865c1e760be0511cfd757b4574a67fa5b) | fix | Handle `ng-template` with structural directive in indexer ([#44788](https://github.com/angular/angular/pull/44788)) |
|
||||
| [7316e72ec5](https://github.com/angular/angular/commit/7316e72ec5ccc475e610bf26b5da451b8ffa50a2) | fix | properly index <svg> elements when on a template ([#44785](https://github.com/angular/angular/pull/44785)) |
|
||||
| [100091ebf0](https://github.com/angular/angular/commit/100091ebf0ad480e94b5db4f183f9bfcfe8b8274) | fix | remove leftover `_extendedTemplateDiagnostics` requirements ([#44777](https://github.com/angular/angular/pull/44777)) |
|
||||
| [d2ae96f742](https://github.com/angular/angular/commit/d2ae96f742c82daa6c4d4b8afd3b0959fb9b89d1) | fix | skip `ExtendedTemplateCheckerImpl` construction if there were configuration errors ([#44778](https://github.com/angular/angular/pull/44778)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [9921b2ca35](https://github.com/angular/angular/commit/9921b2ca35f774c2d80317aeeaabf02a6942daa6) | feat | Add a migration for typed forms. ([#44449](https://github.com/angular/angular/pull/44449)) |
|
||||
| [b49ffcd50e](https://github.com/angular/angular/commit/b49ffcd50e46ca7907ee2049f6cade8e080f5980) | feat | allow for injector to be specified when creating an embedded view ([#44666](https://github.com/angular/angular/pull/44666)) |
|
||||
| [5626b34264](https://github.com/angular/angular/commit/5626b34264d8893fece74d30db4920a685f3b17f) | fix | consistently use namespace short name rather than URI ([#44766](https://github.com/angular/angular/pull/44766)) |
|
||||
| [94bfcdd9de](https://github.com/angular/angular/commit/94bfcdd9de234f7189daaa06a6c179d9697bef8d) | fix | error if NgZone.isInAngularZone is called with a noop zone ([#44800](https://github.com/angular/angular/pull/44800)) |
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5ae1030462](https://github.com/angular/angular/commit/5ae1030462a97bda8357415cbfcd34301dfa6499) | feat | Add an unused symbol `AnyForUntypedForms`. ([#44467](https://github.com/angular/angular/pull/44467)) |
|
||||
| [72092ebd26](https://github.com/angular/angular/commit/72092ebd26d6670a7dd02f6db5515d2afd27c4d6) | feat | Allow a FormControl to use initial value as default. ([#44434](https://github.com/angular/angular/pull/44434)) |
|
||||
| [f7aa937cac](https://github.com/angular/angular/commit/f7aa937cac0ad315e1012770aa1da97476aced54) | fix | Make some minor fixups for forward-compatibility with typed forms. ([#44540](https://github.com/angular/angular/pull/44540)) |
|
||||
### language-service
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [af2a1317cb](https://github.com/angular/angular/commit/af2a1317cbfba985233f510bbe9e49016968307c) | feat | support completions for animation ([#44630](https://github.com/angular/angular/pull/44630)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5a4ddfd4f5](https://github.com/angular/angular/commit/5a4ddfd4f5e17bb4209528cefc3f0674298ab6e8) | feat | Allow symbol keys for `Route` `data` and `resolve` properties ([#44519](https://github.com/angular/angular/pull/44519)) |
|
||||
## Special Thanks
|
||||
Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Dario Piotrowicz, Derek Cormier, Doug Parker, Douglas Parker, Dylan Hunn, George Kalpakas, Jessica Janiuk, JoostK, Kristiyan Kostadinov, Martin Probst, Oleg Postoev, Stephanie Tuerk, Tim Bowersox, Wiley Marques, Yousaf Nawaz, dario-piotrowicz, iRealNirmal, ivanwonder and shejialuo
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.1.3"></a>
|
||||
# 13.1.3 (2022-01-19)
|
||||
### animations
|
||||
@@ -17,44 +82,6 @@ Alan Agius, Andrew Kushnir, AnkitSharma-007, Daniel Díaz, Dmytro Mezhenskyi, Je
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.2.0-next.2"></a>
|
||||
# 13.2.0-next.2 (2022-01-12)
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [8ba43a1a1e](https://github.com/angular/angular/commit/8ba43a1a1ea811ad25d6af4384e1cce5a53b0baf) | fix | retain triggers values for moved tracked list items ([#44578](https://github.com/angular/angular/pull/44578)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [a4ab6d6b72](https://github.com/angular/angular/commit/a4ab6d6b72fe891f232d2446f8b7c454a82a5ba0) | feat | add support for safe calls in templates ([#44580](https://github.com/angular/angular/pull/44580)) |
|
||||
| [b8ed03b5ad](https://github.com/angular/angular/commit/b8ed03b5ad91a2dd62d600240e9056a360b89b90) | fix | correct spans when parsing bindings with comments ([#44678](https://github.com/angular/angular/pull/44678)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [73424def13](https://github.com/angular/angular/commit/73424def130ae1d72036142f6f2d7bce63858c9e) | feat | provide the animations for `DirectiveMeta` ([#44630](https://github.com/angular/angular/pull/44630)) |
|
||||
| [1a9121826e](https://github.com/angular/angular/commit/1a9121826ea94d98d0603d65628d4b4b08f2f70b) | fix | enable narrowing of using type guard methods ([#44447](https://github.com/angular/angular/pull/44447)) |
|
||||
| [f8af49eb75](https://github.com/angular/angular/commit/f8af49eb751cabab76c6c7327f377a163f841dc4) | fix | fix crash during type-checking of library builds ([#44587](https://github.com/angular/angular/pull/44587)) |
|
||||
| [f83fb3a5f9](https://github.com/angular/angular/commit/f83fb3a5f9b146cb5025517950100e98895304ed) | fix | handle property reads of `ThisReceiver` in the indexer ([#44678](https://github.com/angular/angular/pull/44678)) |
|
||||
| [eeaabe7fbf](https://github.com/angular/angular/commit/eeaabe7fbf7a1cf057c6b740609886415e4037eb) | fix | incorrectly interpreting $any calls with a property read ([#44657](https://github.com/angular/angular/pull/44657)) |
|
||||
| [92b23f4851](https://github.com/angular/angular/commit/92b23f48519a1adb460a431c356bcd9917fb059d) | fix | properly index <svg> elements ([#44678](https://github.com/angular/angular/pull/44678)) |
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5ae1030462](https://github.com/angular/angular/commit/5ae1030462a97bda8357415cbfcd34301dfa6499) | feat | Add an unused symbol `AnyForUntypedForms`. ([#44467](https://github.com/angular/angular/pull/44467)) |
|
||||
| [f7aa937cac](https://github.com/angular/angular/commit/f7aa937cac0ad315e1012770aa1da97476aced54) | fix | Make some minor fixups for forward-compatibility with typed forms. ([#44540](https://github.com/angular/angular/pull/44540)) |
|
||||
### language-service
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [af2a1317cb](https://github.com/angular/angular/commit/af2a1317cbfba985233f510bbe9e49016968307c) | feat | support completions for animation ([#44630](https://github.com/angular/angular/pull/44630)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5a4ddfd4f5](https://github.com/angular/angular/commit/5a4ddfd4f5e17bb4209528cefc3f0674298ab6e8) | feat | Allow symbol keys for `Route` `data` and `resolve` properties ([#44519](https://github.com/angular/angular/pull/44519)) |
|
||||
## Special Thanks
|
||||
Abdurrahman Abu-Hijleh, Adam Plumer, Alex Rickabaugh, AlirezaEbrahimkhani, Andrew Kushnir, Andrew Scott, Borja Paz Rodríguez, Chihab Otmani, Chris Mancini, Dario Piotrowicz, Doug Parker, Dylan Hunn, George Kalpakas, Joey Perrott, JoostK, Kristiyan Kostadinov, Kyoz, Patrick Prakash, Paul Gschwendtner, Serhey Dolgushev, Yousaf Nawaz, Yuchao Wu, alkavats1, dario-piotrowicz, huangqing, ivanwonder, shejialuo, twerske, wszgrcy and zuckjet
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.1.2"></a>
|
||||
# 13.1.2 (2022-01-12)
|
||||
### animations
|
||||
@@ -82,22 +109,6 @@ Abdurrahman Abu-Hijleh, Adam Plumer, Alex Rickabaugh, AlirezaEbrahimkhani, Andre
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.2.0-next.1"></a>
|
||||
# 13.2.0-next.1 (2021-12-15)
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [38ddae1c2e](https://github.com/angular/angular/commit/38ddae1c2e0de87d5672fda30d4fbe567dab4123) | fix | don't consume instructions for animateChild ([#44357](https://github.com/angular/angular/pull/44357)) |
|
||||
| [bab7ed3541](https://github.com/angular/angular/commit/bab7ed35418173714509e06ba8b092aff28fff89) | fix | should not invoke disabled child animations ([#37724](https://github.com/angular/angular/pull/37724)) |
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [72092ebd26](https://github.com/angular/angular/commit/72092ebd26d6670a7dd02f6db5515d2afd27c4d6) | feat | Allow a FormControl to use initial value as default. ([#44434](https://github.com/angular/angular/pull/44434)) |
|
||||
## Special Thanks
|
||||
Alex Rickabaugh, Aristeidis Bampakos, Dylan Hunn, JoostK, Kristiyan Kostadinov, Paul Gschwendtner, Spej, Yousaf Nawaz, dario-piotrowicz, faso-dev, jaybell and zuckjet
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.1.1"></a>
|
||||
# 13.1.1 (2021-12-15)
|
||||
### animations
|
||||
@@ -115,13 +126,6 @@ Aristeidis Bampakos, Dylan Hunn, George Kalpakas, JoostK, Kristiyan Kostadinov,
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.2.0-next.0"></a>
|
||||
# 13.2.0-next.0 (2021-12-09)
|
||||
|
||||
There have been no changes since the previous `13.1.0` release.
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.1.0"></a>
|
||||
# 13.1.0 (2021-12-09)
|
||||
## Deprecations
|
||||
|
||||
+11
-4
@@ -19,16 +19,21 @@ export class HeroFormReactiveComponent implements OnInit {
|
||||
heroForm: FormGroup;
|
||||
|
||||
ngOnInit(): void {
|
||||
// #docregion async-validator-usage
|
||||
const alterEgoControl = new FormControl('', {
|
||||
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
|
||||
updateOn: 'blur'
|
||||
});
|
||||
// #enddocregion async-validator-usage
|
||||
alterEgoControl.setValue(this.hero.alterEgo);
|
||||
|
||||
this.heroForm = new FormGroup({
|
||||
name: new FormControl(this.hero.name, [
|
||||
Validators.required,
|
||||
Validators.minLength(4),
|
||||
forbiddenNameValidator(/bob/i)
|
||||
]),
|
||||
alterEgo: new FormControl(this.hero.alterEgo, {
|
||||
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
|
||||
updateOn: 'blur'
|
||||
}),
|
||||
alterEgo: alterEgoControl,
|
||||
power: new FormControl(this.hero.power, Validators.required)
|
||||
});
|
||||
}
|
||||
@@ -39,5 +44,7 @@ export class HeroFormReactiveComponent implements OnInit {
|
||||
|
||||
get alterEgo() { return this.heroForm.get('alterEgo'); }
|
||||
|
||||
// #docregion async-validator-inject
|
||||
constructor(private alterEgoValidator: UniqueAlterEgoValidator) {}
|
||||
// #enddocregion async-validator-inject
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ export class UniqueAlterEgoValidator implements AsyncValidator {
|
||||
constructor(private heroesService: HeroesService) {}
|
||||
|
||||
validate(
|
||||
ctrl: AbstractControl
|
||||
): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
|
||||
return this.heroesService.isAlterEgoTaken(ctrl.value).pipe(
|
||||
control: AbstractControl
|
||||
): Observable<ValidationErrors | null> {
|
||||
return this.heroesService.isAlterEgoTaken(control.value).pipe(
|
||||
map(isTaken => (isTaken ? { uniqueAlterEgo: true } : null)),
|
||||
catchError(() => of(null))
|
||||
);
|
||||
@@ -25,20 +25,24 @@ export class UniqueAlterEgoValidator implements AsyncValidator {
|
||||
}
|
||||
// #enddocregion async-validator
|
||||
|
||||
// #docregion async-validator-directive
|
||||
@Directive({
|
||||
selector: '[appUniqueAlterEgo]',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_ASYNC_VALIDATORS,
|
||||
useExisting: forwardRef(() => UniqueAlterEgoValidator),
|
||||
useExisting: forwardRef(() => UniqueAlterEgoValidatorDirective),
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
export class UniqueAlterEgoValidatorDirective {
|
||||
export class UniqueAlterEgoValidatorDirective implements AsyncValidator {
|
||||
constructor(private validator: UniqueAlterEgoValidator) {}
|
||||
|
||||
validate(control: AbstractControl) {
|
||||
this.validator.validate(control);
|
||||
validate(
|
||||
control: AbstractControl
|
||||
): Observable<ValidationErrors | null> {
|
||||
return this.validator.validate(control);
|
||||
}
|
||||
}
|
||||
// #enddocregion async-validator-directive
|
||||
|
||||
+3
-2
@@ -35,14 +35,15 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<input type="text"
|
||||
<!-- #docregion alterEgo-input -->
|
||||
<input type="text"
|
||||
id="alterEgo"
|
||||
name="alterEgo"
|
||||
#alterEgo="ngModel"
|
||||
[(ngModel)]="hero.alterEgo"
|
||||
[ngModelOptions]="{ updateOn: 'blur' }"
|
||||
appUniqueAlterEgo>
|
||||
|
||||
<!-- #enddocregion alterEgo-input -->
|
||||
<div *ngIf="alterEgo.pending">Validating...</div>
|
||||
<div *ngIf="alterEgo.invalid" class="alert alter-ego-errors">
|
||||
<div *ngIf="alterEgo.errors?.['uniqueAlterEgo']">
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
@name Invalid Banana-in-Box
|
||||
|
||||
@description
|
||||
|
||||
This diagnostic detects a backwards "banana-in-box" syntax for
|
||||
[two-way bindings](guide/two-way-binding).
|
||||
|
||||
```html
|
||||
<div ([var])="otherVar"></div>
|
||||
```
|
||||
|
||||
## What's wrong with that?
|
||||
|
||||
As it stands, `([var])` is actually an [event binding](guide/event-binding) with
|
||||
the name `[var]`. The author likely intended this as a two-way binding to a variable named
|
||||
`var` but as written this binding has no effect.
|
||||
|
||||
## What should I do instead?
|
||||
|
||||
Fix the typo. As the name suggests, the banana `(` should go *inside* the box `[]`. In this case:
|
||||
|
||||
```html
|
||||
<div [(var)]="otherVar"></div>
|
||||
```
|
||||
|
||||
## Configuration requirements
|
||||
|
||||
[`strictTemplates`](/guide/template-typecheck#strict-mode) must be enabled for any extended
|
||||
diagnostic to emit. `invalidBananaInBox` has no additional requirements beyond `strictTemplates`.
|
||||
|
||||
## What if I can't avoid this?
|
||||
|
||||
This diagnostic can be disabled by editing the project's `tsconfig.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"angularCompilerOptions": {
|
||||
"extendedDiagnostics": {
|
||||
"checks": {
|
||||
"invalidBananaInBox": "suppress"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See [extended diagnostic configuration](extended-diagnostics#configuration) for more info.
|
||||
@@ -0,0 +1,91 @@
|
||||
@name Nullish coalescing not nullable
|
||||
|
||||
@description
|
||||
|
||||
This diagnostic detects a useless nullish coalescing operator (`??`) in Angular templates.
|
||||
Specifically, it looks for operations where the input is not "nullable", meaning its type does not
|
||||
include `null` or `undefined`. For such values, the right side of the `??` will never be used.
|
||||
|
||||
```typescript
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
// Template displays `foo` if present, falls back to 'bar' if it is `null`
|
||||
// or `undefined`.
|
||||
template: `<div>{{ foo ?? 'bar' }}</div>`,
|
||||
// ...
|
||||
})
|
||||
class MyComponent {
|
||||
// `foo` is declared as a `string` which *cannot* be `null` or `undefined`.
|
||||
foo: string = 'test';
|
||||
}
|
||||
```
|
||||
|
||||
## What's wrong with that?
|
||||
|
||||
Using the nullish coalescing operator with a non-nullable input has no effect and is indicative of a
|
||||
discrepancy between the allowed type of a value and how it is presented in the template. A developer
|
||||
might reasonably assume that the right side of the nullish coalescing operator is displayed in some
|
||||
case, but it will never actually be displayed. This can lead to confusion about the expected output
|
||||
of the program.
|
||||
|
||||
## What should I do instead?
|
||||
|
||||
Update the template and declared type to be in sync. Double check the type of the input and confirm
|
||||
whether it is actually expected to be nullable.
|
||||
|
||||
If the input should be nullable, add `null` or `undefined` to its type to indicate this.
|
||||
|
||||
```typescript
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `<div>{{ foo ?? 'bar' }}</div>`,
|
||||
// ...
|
||||
})
|
||||
class MyComponent {
|
||||
// `foo` is now nullable. If it is ever set to `null`, 'bar' will be displayed.
|
||||
foo: string | null = 'test';
|
||||
}
|
||||
```
|
||||
|
||||
If the input should *not* be nullable, delete the `??` operator and its right operand, since the
|
||||
value is guaranteed by TypeScript to always be non-nullable.
|
||||
|
||||
```typescript
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
// Template always displays `foo`, which is guaranteed to never be `null` or
|
||||
// `undefined`.
|
||||
template: `<div>{{ foo }}</div>`,
|
||||
// ...
|
||||
})
|
||||
class MyComponent {
|
||||
foo: string = 'test';
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration requirements
|
||||
|
||||
[`strictTemplates`](/guide/template-typecheck#strict-mode) must be enabled for any extended
|
||||
diagnostic to emit. [`strictNullChecks`](/guide/template-typecheck#strict-null-checks) must also be
|
||||
enabled to emit any `nullishCoalescingNotNullable` diagnostics.
|
||||
|
||||
## What if I can't avoid this?
|
||||
|
||||
This diagnostic can be disabled by editing the project's `tsconfig.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"angularCompilerOptions": {
|
||||
"extendedDiagnostics": {
|
||||
"checks": {
|
||||
"nullishCoalescingNotNullable": "suppress"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See [extended diagnostic configuration](extended-diagnostics#configuration) for more info.
|
||||
@@ -0,0 +1,85 @@
|
||||
# Extended Diagnostics
|
||||
|
||||
There are many coding patterns that are technically valid to the compiler or runtime, but which may
|
||||
have complex nuances or caveats. These patterns may not have the intended effect expected by a
|
||||
developer, which often leads to bugs. The Angular compiler includes "extended diagnostics" which
|
||||
identify many of these patterns to warn developers about the potential issues and enforce common
|
||||
best practices within a codebase.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
Currently, Angular supports the following extended diagnostics:
|
||||
|
||||
* [NG8101 - `invalidBananaInBox`](extended-diagnostics/NG8101)
|
||||
* [NG8102 - `nullishCoalescingNotNullable`](extended-diagnostics/NG8102)
|
||||
|
||||
## Configuration
|
||||
|
||||
Extended diagnostics are warnings by default and do not block compilation. Each diagnostic can be
|
||||
configured as either:
|
||||
|
||||
* `warning` (default) - The compiler emits the diagnostic as a warning but does not block
|
||||
compilation. The compiler will still exit with status code 0, even if warnings are emitted.
|
||||
* `error` - The compiler emits the diagnostic as an error and fails the compilation. The compiler
|
||||
will exit with a non-zero status code if one or more errors are emitted.
|
||||
* `suppress` - The compiler does *not* emit the diagnostic at all.
|
||||
|
||||
Check severity can be configured in the project's `tsconfig.json` file:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"angularCompilerOptions": {
|
||||
"extendedDiagnostics": {
|
||||
// The categories to use for specific diagnostics.
|
||||
"checks": {
|
||||
// Maps check name to its category.
|
||||
"invalidBananaInBox": "suppress"
|
||||
},
|
||||
|
||||
// The category to use for any diagnostics not listed in `checks` above.
|
||||
"defaultCategory": "error"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `checks` field maps the name of individual diagnostics to their associated category. See
|
||||
[Diagnostics](#diagnostics) for a complete list of extended diagnostics and the name to use for
|
||||
configuring them.
|
||||
|
||||
The `defaultCategory` field is used for any diagnostics that are not explicitly listed under
|
||||
`checks`. If not set, such diagnostics will be treated as `warning`.
|
||||
|
||||
Extended diagnostics will emit when [`strictTemplates`](/guide/template-typecheck#strict-mode) is
|
||||
enabled. This is required to allow the compiler to better understand Angular template types and
|
||||
provide accurate and meaningful diagnostics.
|
||||
|
||||
## Semantic Versioning
|
||||
|
||||
The Angular team intends to add or enable new extended diagnostics in **minor** versions of Angular
|
||||
(see [semver](https://docs.npmjs.com/about-semantic-versioning)). This means that upgrading Angular
|
||||
may show new warnings in your existing codebase. This enables the team to deliver features more
|
||||
quickly and to make extended diagnostics more accessible to developers.
|
||||
|
||||
However, setting `"defaultCategory": "error"` will promote such warnings to hard errors. This can
|
||||
cause a minor version upgrade to introduce compilation errors, which may be seen as a semver
|
||||
non-compliant breaking change. Any new diagnostics can be suppressed or demoted to warnings via the
|
||||
above [configuration](#configuration), so the impact of a new diagnostic should be minimal to
|
||||
projects that treat extended diagnostics as errors by default. Defaulting to error is a very
|
||||
powerful tool; just be aware of this semver caveat when deciding if `error` is the right default for
|
||||
your project.
|
||||
|
||||
## New Diagnostics
|
||||
|
||||
The Angular team is always open to suggestions about new diagnostics that could be added. Extended
|
||||
diagnostics should generally:
|
||||
|
||||
* Detect a common, non-obvious developer mistake with Angular templates.
|
||||
* Clearly articulate why this pattern can lead to bugs or unintended behavior.
|
||||
* Suggest one or more clear solutions.
|
||||
* Have a low (preferrably zero) false-positive rate.
|
||||
* Apply to the vast majority of Angular applications (not specific to an unofficial library).
|
||||
* Improve program correctness or performance (not style, that responsibility falls to a linter).
|
||||
|
||||
If you have an idea for a compiler check which fits these criteria, consider filing a
|
||||
[feature request](https://github.com/angular/angular/issues/new?template=2-feature-request.yaml).
|
||||
@@ -304,7 +304,7 @@ In the following example, an async validator ensures that heroes pick an alter e
|
||||
New heroes are constantly enlisting and old heroes are leaving the service, so the list of available alter egos cannot be retrieved ahead of time.
|
||||
To validate the potential alter ego entry, the validator must initiate an asynchronous operation to consult a central database of all currently enlisted heroes.
|
||||
|
||||
The following code create the validator class, `UniqueAlterEgoValidator`, which implements the `AsyncValidator` interface.
|
||||
The following code creates the validator class, `UniqueAlterEgoValidator`, which implements the `AsyncValidator` interface.
|
||||
|
||||
<code-example path="form-validation/src/app/shared/alter-ego.directive.ts" region="async-validator"></code-example>
|
||||
|
||||
@@ -333,6 +333,30 @@ You could handle the error differently and return the `ValidationError` object i
|
||||
After some time passes, the observable chain completes and the asynchronous validation is done.
|
||||
The `pending` flag is set to `false`, and the form validity is updated.
|
||||
|
||||
### Adding async validators to reactive forms
|
||||
|
||||
To use an async validator in reactive forms, begin by injecting the validator into the constructor of the component class.
|
||||
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.2.ts" region="async-validator-inject"></code-example>
|
||||
|
||||
Then, pass the validator function directly to the `FormControl` to apply it.
|
||||
|
||||
In the following example, the `validate` function of `UniqueAlterEgoValidator` is applied to `alterEgoControl` by passing it to the control's `asyncValidators` option and binding it to the instance of `UniqueAlterEgoValidator` that was injected into `HeroFormReactiveComponent`. The value of `asyncValidators` can be either a single async validator function, or an array of functions. To learn more about `FormControl` options, see the [AbstractControlOptions](/api/forms/AbstractControlOptions) API reference.
|
||||
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.2.ts" region="async-validator-usage"></code-example>
|
||||
|
||||
### Adding async validators to template-driven forms
|
||||
|
||||
To use an async validator in template-driven forms, create a new directive and register the `NG_ASYNC_VALIDATORS` provider on it.
|
||||
|
||||
In the example below, the directive injects the `UniqueAlterEgoValidator` class that contains the actual validation logic and invokes it in the `validate` function, triggered by Angular when validation should happen.
|
||||
|
||||
<code-example path="form-validation/src/app/shared/alter-ego.directive.ts" region="async-validator-directive"></code-example>
|
||||
|
||||
Then, as with synchronous validators, add the directive's selector to an input to activate it.
|
||||
|
||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="alterEgo-input" header="template/hero-form-template.component.html (unique-alter-ego-input)"></code-example>
|
||||
|
||||
### Optimizing performance of async validators
|
||||
|
||||
By default, all validators run after every form value change. With synchronous validators, this does not normally have a noticeable impact on application performance.
|
||||
|
||||
@@ -46,7 +46,7 @@ In most cases, Angular sets the template variable's value to the element on whic
|
||||
In the previous example, `phone` refers to the phone number `<input>`.
|
||||
The button's click handler passes the `<input>` value to the component's `callPhone()` method.
|
||||
|
||||
The `NgForm` directive demonstrates getting a reference to a different value by reference a directive's `exportAs` name.
|
||||
The `NgForm` directive demonstrates getting a reference to a different value by referencing a directive's `exportAs` name.
|
||||
In the following example, the template variable, `itemForm`, appears three times separated by HTML.
|
||||
|
||||
<code-example path="template-reference-variables/src/app/app.component.html" region="ngForm" header="src/app/hero-form.component.html"></code-example>
|
||||
|
||||
@@ -1055,6 +1055,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Extended Diagnostic Reference",
|
||||
"tooltip": "Details of extended diagnostics that can be emitted by the Angular compiler",
|
||||
"children": [
|
||||
{
|
||||
"url": "extended-diagnostics",
|
||||
"title": "Extended Diagnostics Overview",
|
||||
"tooltip": "Overview of extended diagnostics, what they are, and how to configure them."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Example applications",
|
||||
"tooltip": "List of all of the example applications in the Angular documentation.",
|
||||
@@ -1247,4 +1258,4 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+18
-18
@@ -85,39 +85,39 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "13.1.2",
|
||||
"@angular/cdk": "13.1.2",
|
||||
"@angular/common": "13.1.2",
|
||||
"@angular/compiler": "13.1.2",
|
||||
"@angular/core": "13.1.2",
|
||||
"@angular/elements": "13.1.2",
|
||||
"@angular/forms": "13.1.2",
|
||||
"@angular/material": "13.1.2",
|
||||
"@angular/platform-browser": "13.1.2",
|
||||
"@angular/platform-browser-dynamic": "13.1.2",
|
||||
"@angular/router": "13.1.2",
|
||||
"@angular/service-worker": "13.1.2",
|
||||
"@angular/animations": "13.1.3",
|
||||
"@angular/cdk": "13.1.3",
|
||||
"@angular/common": "13.1.3",
|
||||
"@angular/compiler": "13.1.3",
|
||||
"@angular/core": "13.1.3",
|
||||
"@angular/elements": "13.1.3",
|
||||
"@angular/forms": "13.1.3",
|
||||
"@angular/material": "13.1.3",
|
||||
"@angular/platform-browser": "13.1.3",
|
||||
"@angular/platform-browser-dynamic": "13.1.3",
|
||||
"@angular/router": "13.1.3",
|
||||
"@angular/service-worker": "13.1.3",
|
||||
"rxjs": "~7.4.0",
|
||||
"safevalues": "^0.1.8",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "13.1.3",
|
||||
"@angular-devkit/build-angular": "13.1.4",
|
||||
"@angular-eslint/builder": "^13.0.0",
|
||||
"@angular-eslint/eslint-plugin": "^13.0.0",
|
||||
"@angular-eslint/eslint-plugin-template": "^13.0.0",
|
||||
"@angular-eslint/template-parser": "^13.0.0",
|
||||
"@angular/cli": "13.1.3",
|
||||
"@angular/compiler-cli": "13.1.2",
|
||||
"@angular/cli": "13.1.4",
|
||||
"@angular/compiler-cli": "13.1.3",
|
||||
"@types/jasmine": "~3.10.0",
|
||||
"@types/lunr": "^2.3.3",
|
||||
"@types/node": "^12.7.9",
|
||||
"@types/stemmer": "^1.0.2",
|
||||
"@types/trusted-types": "^2.0.2",
|
||||
"@types/xregexp": "^4.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.9.1",
|
||||
"@typescript-eslint/parser": "5.9.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.10.0",
|
||||
"@typescript-eslint/parser": "5.10.0",
|
||||
"@yarnpkg/lockfile": "^1.1.0",
|
||||
"archiver": "^5.3.0",
|
||||
"assert": "^2.0.0",
|
||||
@@ -128,7 +128,7 @@
|
||||
"cross-spawn": "^7.0.3",
|
||||
"css-selector-parser": "^1.4.1",
|
||||
"dgeni": "^0.4.14",
|
||||
"dgeni-packages": "^0.29.2",
|
||||
"dgeni-packages": "^0.29.3",
|
||||
"entities": "^3.0.0",
|
||||
"eslint": "^8.0.0",
|
||||
"eslint-plugin-import": "^2.23.4",
|
||||
|
||||
@@ -123,6 +123,7 @@
|
||||
'.folder-cli',
|
||||
'.folder-docs',
|
||||
'.folder-errors',
|
||||
'.folder-extended-diagnostics',
|
||||
'.folder-guide',
|
||||
'.folder-start',
|
||||
'.folder-tutorial',
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
const path = require('canonical-path');
|
||||
const Package = require('dgeni').Package;
|
||||
const basePackage = require('../angular-base-package');
|
||||
const {CONTENTS_PATH, TEMPLATES_PATH} = require('../config');
|
||||
const contentPackage = require('../content-package');
|
||||
|
||||
const extendedDiagnosticsPackage = new Package('angular-extended-diagnostics', [basePackage, contentPackage])
|
||||
|
||||
.factory(require('./readers/extended-diagnostic'))
|
||||
|
||||
.processor(require('./processors/processExtendedDiagnosticDocs'))
|
||||
|
||||
// Where do we find the extended-diagnostic documentation files?
|
||||
.config(function(extendedDiagnosticFileReader, readFilesProcessor) {
|
||||
readFilesProcessor.fileReaders.push(extendedDiagnosticFileReader);
|
||||
readFilesProcessor.sourceFiles = readFilesProcessor.sourceFiles.concat([
|
||||
{
|
||||
basePath: CONTENTS_PATH,
|
||||
include: `${CONTENTS_PATH}/extended-diagnostics/index.md`,
|
||||
fileReader: 'contentFileReader',
|
||||
},
|
||||
{
|
||||
basePath: CONTENTS_PATH,
|
||||
include: `${CONTENTS_PATH}/extended-diagnostics/**/*.md`,
|
||||
exclude: `${CONTENTS_PATH}/extended-diagnostics/index.md`,
|
||||
fileReader: 'extendedDiagnosticFileReader',
|
||||
},
|
||||
]);
|
||||
})
|
||||
|
||||
// Here we compute the `id`, `code`, `aliases`, `path` and `outputPath` for the
|
||||
// `extended-diagnostic` docs.
|
||||
// * The `id` is the same as the `path` (the source path with the `.md` stripped off).
|
||||
// * The `code` is the `id` without any containing paths (currently all extended diagnostics
|
||||
// must be on the top level).
|
||||
// * The `aliases` are used for automatic code linking and search terms.
|
||||
.config(function(computeIdsProcessor, computePathsProcessor) {
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
docTypes: ['extended-diagnostic'],
|
||||
getId: doc => doc.fileInfo.relativePath.replace(/\.\w*$/, ''), // Strip off the extension.
|
||||
getAliases: doc => {
|
||||
doc.code = path.basename(doc.id);
|
||||
return [doc.id, doc.code];
|
||||
},
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates = computePathsProcessor.pathTemplates.concat([
|
||||
{
|
||||
docTypes: ['extended-diagnostic'],
|
||||
getPath: doc => doc.id,
|
||||
outputPathTemplate: '${path}.json',
|
||||
},
|
||||
]);
|
||||
})
|
||||
|
||||
// The templates that define how the `extended-diagnostic` doc-type is rendered is found in the
|
||||
// `${TEMPLATES_PATH}/extended-diagnostic/` directory.
|
||||
.config(function(templateFinder) {
|
||||
templateFinder.templateFolders.unshift(path.resolve(TEMPLATES_PATH, 'extended-diagnostic'));
|
||||
})
|
||||
|
||||
// The AIO application expects content files to be provided as JSON files that it requests via
|
||||
// HTTP. So here we tell the `convertToJsonProcessor` to include docs of type
|
||||
// `extended-diagnostic` in those that it converts.
|
||||
.config(function(convertToJsonProcessor, postProcessHtml) {
|
||||
convertToJsonProcessor.docTypes.push('extended-diagnostic');
|
||||
postProcessHtml.docTypes.push('extended-diagnostic');
|
||||
});
|
||||
|
||||
module.exports = extendedDiagnosticsPackage;
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
module.exports = function processExtendedDiagnosticDocs(createDocMessage) {
|
||||
return {
|
||||
$runAfter: ['extra-docs-added'],
|
||||
$runBefore: ['rendering-docs'],
|
||||
$process(docs) {
|
||||
const navigationDoc = docs.find(doc => doc.docType === 'navigation-json');
|
||||
const extendedDiagnosticsNode =
|
||||
navigationDoc && findExtendedDiagnosticsNode(navigationDoc.data['SideNav']);
|
||||
|
||||
if (!extendedDiagnosticsNode) {
|
||||
throw new Error(createDocMessage(
|
||||
'Missing `extended-diagnostics` url - ' +
|
||||
'This node is needed as a place to insert the generated extended diagnostics docs.',
|
||||
navigationDoc));
|
||||
}
|
||||
|
||||
docs.forEach(doc => {
|
||||
if (doc.docType === 'extended-diagnostic') {
|
||||
// Add to navigation doc.
|
||||
const title = `${doc.code}: ${doc.name}`;
|
||||
extendedDiagnosticsNode.children.push({title, tooltip: doc.name, url: doc.path});
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Look for the `extended-diagnostics` navigation node. It is the node whose first child has
|
||||
* `url: 'extended-diagnostics'`.
|
||||
* (NOTE: Using the URL instead of the title, because it is more robust.)
|
||||
*
|
||||
* We will "recursively" check all navigation nodes and their children (in breadth-first order),
|
||||
* until we find the `extended-diagnostics` node. Keep a list of nodes lists to check.
|
||||
* (NOTE: Each item in the list is a LIST of nodes.)
|
||||
*/
|
||||
function findExtendedDiagnosticsNode(nodes) {
|
||||
const nodesList = [nodes];
|
||||
|
||||
while (nodesList.length > 0) {
|
||||
// Get the first item from the list of nodes lists.
|
||||
const currentNodes = nodesList.shift();
|
||||
const extendedDiagnosticsNode = currentNodes.find(isExtendedDiagnosticsNode);
|
||||
|
||||
// One of the nodes in `currentNodes` was the `extended-diagnostics` node. Return it.
|
||||
if (extendedDiagnosticsNode) return extendedDiagnosticsNode;
|
||||
|
||||
// The `extended-diagnostics` node is not in `currentNodes`. Check each node's children
|
||||
// (if any).
|
||||
currentNodes.forEach(node => node.children && nodesList.push(node.children));
|
||||
}
|
||||
|
||||
// We checked all navigation nodes and their children and did not find the `extended-diagnostics`
|
||||
// node.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isExtendedDiagnosticsNode(node) {
|
||||
return node.children && node.children.length && node.children[0].url === 'extended-diagnostics';
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
const Dgeni = require('dgeni');
|
||||
const testPackage = require('../../helpers/test-package');
|
||||
|
||||
describe('processExtendedDiagnosticDocs processor', () => {
|
||||
let dgeni, injector, processor, createDocMessage;
|
||||
|
||||
beforeEach(() => {
|
||||
dgeni = new Dgeni([testPackage('angular-extended-diagnostics-package')]);
|
||||
injector = dgeni.configureInjector();
|
||||
processor = injector.get('processExtendedDiagnosticDocs');
|
||||
createDocMessage = injector.get('createDocMessage');
|
||||
});
|
||||
|
||||
it('should be available on the injector', () => {
|
||||
expect(processor.$process).toBeDefined();
|
||||
});
|
||||
|
||||
it('should run after the correct processor', () => {
|
||||
expect(processor.$runAfter).toEqual(['extra-docs-added']);
|
||||
});
|
||||
|
||||
it('should run before the correct processor', () => {
|
||||
expect(processor.$runBefore).toEqual(['rendering-docs']);
|
||||
});
|
||||
|
||||
it(
|
||||
'should add the extended diagnostic to the `extended-diagnostics` node in the navigation doc ' +
|
||||
'if there is a top level node with an `extended-diagnostics` url',
|
||||
() => {
|
||||
const diagnosticDoc = {
|
||||
docType: 'extended-diagnostic',
|
||||
code: '1337',
|
||||
name: 'diagnostic1',
|
||||
path: 'extended-diagnostics/diagnostic1',
|
||||
};
|
||||
const navigation = {
|
||||
docType: 'navigation-json',
|
||||
data: {
|
||||
SideNav: [
|
||||
{url: 'some/page', title: 'Some Page'},
|
||||
{
|
||||
title: 'Extended diagnostics',
|
||||
children: [{title: 'Overview', url: 'extended-diagnostics'}],
|
||||
},
|
||||
{url: 'other/page', title: 'Other Page'},
|
||||
],
|
||||
},
|
||||
};
|
||||
processor.$process([diagnosticDoc, navigation]);
|
||||
expect(navigation.data.SideNav[1].title).toBe('Extended diagnostics');
|
||||
expect(navigation.data.SideNav[1].children).toEqual([
|
||||
{url: 'extended-diagnostics', title: 'Overview'},
|
||||
{
|
||||
url: 'extended-diagnostics/diagnostic1',
|
||||
title: '1337: diagnostic1',
|
||||
tooltip: 'diagnostic1',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should detect the `extended-diagnostics` node if it is nested in another node', () => {
|
||||
const diagnosticDoc = {
|
||||
docType: 'extended-diagnostic',
|
||||
code: '1337',
|
||||
name: 'diagnostic1',
|
||||
path: 'extended-diagnostics/diagnostic1',
|
||||
};
|
||||
const navigation = {
|
||||
docType: 'navigation-json',
|
||||
data: {
|
||||
SideNav: [
|
||||
{url: 'some/page', title: 'Some Page'},
|
||||
{
|
||||
title: 'Extended diagnostics grandparent',
|
||||
children: [
|
||||
{url: 'some/nested/page', title: 'Some Nested Page'},
|
||||
{
|
||||
title: 'Extended diagnostics parent',
|
||||
children: [
|
||||
{url: 'some/more/nested/page', title: 'Some More Nested Page'},
|
||||
{
|
||||
title: 'Extended diagnostics',
|
||||
children: [{title: 'Overview', url: 'extended-diagnostics'}],
|
||||
},
|
||||
{url: 'other/more/nested/page', title: 'Other More Nested Page'},
|
||||
],
|
||||
},
|
||||
{url: 'other/nested/page', title: 'Other Nested Page'},
|
||||
],
|
||||
},
|
||||
{url: 'other/page', title: 'Other Page'},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
processor.$process([diagnosticDoc, navigation]);
|
||||
|
||||
const diagnosticsContainerNode = navigation.data.SideNav[1].children[1].children[1];
|
||||
expect(diagnosticsContainerNode.title).toBe('Extended diagnostics');
|
||||
expect(diagnosticsContainerNode.children).toEqual([
|
||||
{url: 'extended-diagnostics', title: 'Overview'},
|
||||
{url: 'extended-diagnostics/diagnostic1', title: '1337: diagnostic1', tooltip: 'diagnostic1'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should complain if there is no child with `extended-diagnostics` url', () => {
|
||||
const diagnosticDoc = {
|
||||
docType: 'extended-diagnostic',
|
||||
code: '1337',
|
||||
name: 'diagnostic1',
|
||||
path: 'extended-diagnostics/diagnostic1',
|
||||
};
|
||||
const navigation = {
|
||||
docType: 'navigation-json',
|
||||
data: {
|
||||
SideNav: [
|
||||
{url: 'some/page', title: 'Some Page'}, {
|
||||
title: 'Extended diagnostics',
|
||||
tooltip: 'Angular extended diagnostic reference',
|
||||
children: [{title: 'Overview', url: 'not-extended-diagnostics'}],
|
||||
},
|
||||
{url: 'other/page', title: 'Other Page'},
|
||||
]
|
||||
}
|
||||
};
|
||||
expect(() => processor.$process([diagnosticDoc, navigation]))
|
||||
.toThrowError(createDocMessage(
|
||||
'Missing `extended-diagnostics` url - This node is needed as a place to insert the ' +
|
||||
'generated extended diagnostics docs.',
|
||||
navigation));
|
||||
});
|
||||
});
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @dgService
|
||||
* @description
|
||||
* This file reader will pull the contents from an extended diagnostic text file (by default `.md`).
|
||||
*
|
||||
* The doc will initially have the form:
|
||||
* ```
|
||||
* {
|
||||
* docType: 'extended-diagnostic',
|
||||
* content: 'the content of the file',
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
module.exports = function extendedDiagnosticFileReader() {
|
||||
return {
|
||||
name: 'extendedDiagnosticFileReader',
|
||||
defaultPattern: /\.md$/,
|
||||
getDocs: fileInfo => {
|
||||
// We return a single element array because extended-diagnostic files only contain one
|
||||
// document.
|
||||
return [{docType: 'extended-diagnostic', content: fileInfo.content}];
|
||||
},
|
||||
};
|
||||
};
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
const path = require('canonical-path');
|
||||
const Dgeni = require('dgeni');
|
||||
const testPackage = require('../../helpers/test-package');
|
||||
|
||||
describe('extendedDiagnosticFileReader', () => {
|
||||
let dgeni, injector, fileReader;
|
||||
|
||||
beforeEach(() => {
|
||||
dgeni = new Dgeni([testPackage('angular-extended-diagnostics-package', false)]);
|
||||
injector = dgeni.configureInjector();
|
||||
fileReader = injector.get('extendedDiagnosticFileReader');
|
||||
});
|
||||
|
||||
function createFileInfo(file, content, basePath) {
|
||||
return {
|
||||
fileReader: fileReader.name,
|
||||
filePath: file,
|
||||
baseName: path.basename(file, path.extname(file)),
|
||||
extension: path.extname(file).replace(/^\./, ''),
|
||||
basePath: basePath,
|
||||
relativePath: path.relative(basePath, file),
|
||||
content: content,
|
||||
};
|
||||
}
|
||||
|
||||
describe('defaultPattern', () => {
|
||||
it('should match `.md` files', () => {
|
||||
expect(fileReader.defaultPattern.test('abc.md')).toBeTrue();
|
||||
expect(fileReader.defaultPattern.test('abc.js')).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDocs', () => {
|
||||
it('should return an object containing info about the file and its contents', () => {
|
||||
const fileInfo = createFileInfo(
|
||||
'project/path/modules/someModule/foo/docs/subfolder/bar.md', 'A load of content.',
|
||||
'project/path');
|
||||
expect(fileReader.getDocs(fileInfo)).toEqual([
|
||||
{docType: 'extended-diagnostic', content: 'A load of content.'},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -10,9 +10,12 @@ const gitPackage = require('dgeni-packages/git');
|
||||
const apiPackage = require('../angular-api-package');
|
||||
const contentPackage = require('../angular-content-package');
|
||||
const errorsPackage = require('../angular-errors-package');
|
||||
const extendedDiagnosticsPackage = require('../angular-extended-diagnostics-package');
|
||||
const cliDocsPackage = require('../cli-docs-package');
|
||||
|
||||
module.exports = new Package('angular.io', [gitPackage, apiPackage, contentPackage, cliDocsPackage, errorsPackage])
|
||||
module.exports = new Package(
|
||||
'angular.io',
|
||||
[gitPackage, apiPackage, contentPackage, cliDocsPackage, errorsPackage, extendedDiagnosticsPackage])
|
||||
|
||||
// This processor relies upon the versionInfo. See below...
|
||||
.processor(require('./processors/processNavigationMap'))
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
const Package = require('dgeni').Package;
|
||||
const errorsPackage = require('../angular-errors-package');
|
||||
const {CONTENTS_PATH} = require('../config');
|
||||
const baseAuthoringPackage = require('./base-authoring-package');
|
||||
|
||||
function createPackage() {
|
||||
return new Package('author-errors', [baseAuthoringPackage, errorsPackage])
|
||||
.config(function(readFilesProcessor) {
|
||||
readFilesProcessor.sourceFiles = [
|
||||
{
|
||||
basePath: CONTENTS_PATH,
|
||||
include: `${CONTENTS_PATH}/errors/index.md`,
|
||||
fileReader: 'contentFileReader',
|
||||
},
|
||||
{
|
||||
basePath: CONTENTS_PATH,
|
||||
include: `${CONTENTS_PATH}/errors/**/*.md`,
|
||||
exclude: `${CONTENTS_PATH}/errors/index.md`,
|
||||
fileReader: 'errorFileReader',
|
||||
},
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {createPackage};
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
const Package = require('dgeni').Package;
|
||||
const extendedDiagnosticsPackage = require('../angular-extended-diagnostics-package');
|
||||
const {CONTENTS_PATH} = require('../config');
|
||||
const baseAuthoringPackage = require('./base-authoring-package');
|
||||
|
||||
function createPackage() {
|
||||
return new Package('author-extended-diagnostics', [baseAuthoringPackage, extendedDiagnosticsPackage])
|
||||
.config(function(readFilesProcessor) {
|
||||
readFilesProcessor.sourceFiles = [
|
||||
{
|
||||
basePath: CONTENTS_PATH,
|
||||
include: `${CONTENTS_PATH}/extended-diagnostics/index.md`,
|
||||
fileReader: 'contentFileReader',
|
||||
},
|
||||
{
|
||||
basePath: CONTENTS_PATH,
|
||||
include: `${CONTENTS_PATH}/extended-diagnostics/**/*.md`,
|
||||
exclude: `${CONTENTS_PATH}/extended-diagnostics/index.md`,
|
||||
fileReader: 'extendedDiagnosticFileReader',
|
||||
},
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {createPackage};
|
||||
@@ -45,6 +45,18 @@ function createPackage(changedFile) {
|
||||
console.log('Building API docs for', packageName);
|
||||
return require('./api-package').createPackage(packageName);
|
||||
}
|
||||
|
||||
const errorsMatch = /^aio\/content\/error\/([^.]+)\.md/.exec(changedFile);
|
||||
if (errorsMatch) {
|
||||
console.log('Building errors docs');
|
||||
return require('./errors-package').createPackage();
|
||||
}
|
||||
|
||||
const diagnosticsMatch = /^aio\/content\/extended-diagnostics\/([^.]+)\.md/.exec(changedFile);
|
||||
if (diagnosticsMatch) {
|
||||
console.log('Building extended diagnostics docs');
|
||||
return require('./extended-diagnostics-package').createPackage();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{% import "lib/githubLinks.html" as github -%}
|
||||
|
||||
<h1>{$ doc.code $}: {$ doc.name $}</h1>
|
||||
<div class="github-links">
|
||||
{$ github.githubEditLink(doc, versionInfo) $}
|
||||
</div>
|
||||
{% block content %}
|
||||
<div class="content">
|
||||
<h2>Description</h2>
|
||||
{$ doc.description | marked $}
|
||||
</div>
|
||||
{% endblock %}
|
||||
+408
-484
File diff suppressed because it is too large
Load Diff
@@ -581,9 +581,8 @@ export class NgTemplateOutlet implements OnChanges {
|
||||
ngOnChanges(changes: SimpleChanges): void;
|
||||
ngTemplateOutlet: TemplateRef<any> | null;
|
||||
ngTemplateOutletContext: Object | null;
|
||||
ngTemplateOutletInjector: Injector | null;
|
||||
// (undocumented)
|
||||
static ɵdir: i0.ɵɵDirectiveDeclaration<NgTemplateOutlet, "[ngTemplateOutlet]", never, { "ngTemplateOutletContext": "ngTemplateOutletContext"; "ngTemplateOutlet": "ngTemplateOutlet"; "ngTemplateOutletInjector": "ngTemplateOutletInjector"; }, {}, never>;
|
||||
static ɵdir: i0.ɵɵDirectiveDeclaration<NgTemplateOutlet, "[ngTemplateOutlet]", never, { "ngTemplateOutletContext": "ngTemplateOutletContext"; "ngTemplateOutlet": "ngTemplateOutlet"; }, {}, never>;
|
||||
// (undocumented)
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<NgTemplateOutlet, never>;
|
||||
}
|
||||
|
||||
@@ -1238,7 +1238,7 @@ export type StaticProvider = ValueProvider | ExistingProvider | StaticClassProvi
|
||||
|
||||
// @public
|
||||
export abstract class TemplateRef<C> {
|
||||
abstract createEmbeddedView(context: C, injector?: Injector): EmbeddedViewRef<C>;
|
||||
abstract createEmbeddedView(context: C): EmbeddedViewRef<C>;
|
||||
abstract readonly elementRef: ElementRef;
|
||||
}
|
||||
|
||||
@@ -1384,10 +1384,6 @@ export abstract class ViewContainerRef {
|
||||
}): ComponentRef<C>;
|
||||
// @deprecated
|
||||
abstract createComponent<C>(componentFactory: ComponentFactory<C>, index?: number, injector?: Injector, projectableNodes?: any[][], ngModuleRef?: NgModuleRef<any>): ComponentRef<C>;
|
||||
abstract createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, options?: {
|
||||
index?: number;
|
||||
injector?: Injector;
|
||||
}): EmbeddedViewRef<C>;
|
||||
abstract createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number): EmbeddedViewRef<C>;
|
||||
abstract detach(index?: number): ViewRef | null;
|
||||
abstract get element(): ElementRef;
|
||||
|
||||
@@ -167,7 +167,6 @@ export class CheckboxControlValueAccessor extends BuiltInControlValueAccessor im
|
||||
|
||||
// @public
|
||||
export class CheckboxRequiredValidator extends RequiredValidator {
|
||||
validate(control: AbstractControl): ValidationErrors | null;
|
||||
// (undocumented)
|
||||
static ɵdir: i0.ɵɵDirectiveDeclaration<CheckboxRequiredValidator, "input[type=checkbox][required][formControlName],input[type=checkbox][required][formControl],input[type=checkbox][required][ngModel]", never, {}, {}, never>;
|
||||
// (undocumented)
|
||||
@@ -694,11 +693,10 @@ export class ReactiveFormsModule {
|
||||
}
|
||||
|
||||
// @public
|
||||
export class RequiredValidator implements Validator {
|
||||
registerOnValidatorChange(fn: () => void): void;
|
||||
get required(): boolean | string;
|
||||
set required(value: boolean | string);
|
||||
validate(control: AbstractControl): ValidationErrors | null;
|
||||
export class RequiredValidator extends AbstractValidatorDirective {
|
||||
// (undocumented)
|
||||
enabled(input: boolean): boolean;
|
||||
required: boolean | string;
|
||||
// (undocumented)
|
||||
static ɵdir: i0.ɵɵDirectiveDeclaration<RequiredValidator, ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", never, { "required": "required"; }, {}, never>;
|
||||
// (undocumented)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"runtime": 4330,
|
||||
"main": 458531,
|
||||
"polyfills": 37123,
|
||||
"styles": 69863,
|
||||
"styles": 70394,
|
||||
"light-theme": 77232,
|
||||
"dark-theme": 77356
|
||||
}
|
||||
@@ -15,9 +15,9 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime": 4330,
|
||||
"main": 457719,
|
||||
"main": 459048,
|
||||
"polyfills": 37271,
|
||||
"styles": 69863,
|
||||
"styles": 70394,
|
||||
"light-theme": 77232,
|
||||
"dark-theme": 77356
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime": 1063,
|
||||
"main": 162121,
|
||||
"polyfills": 37206
|
||||
"main": 163160,
|
||||
"polyfills": 36975
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+8
-8
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "13.2.0-next.2",
|
||||
"version": "13.2.0",
|
||||
"private": true,
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
"homepage": "https://github.com/angular/angular",
|
||||
@@ -38,12 +38,12 @@
|
||||
},
|
||||
"// 1": "dependencies are used locally and by bazel",
|
||||
"dependencies": {
|
||||
"@angular-devkit/build-angular": "13.1.3",
|
||||
"@angular-devkit/build-optimizer": "0.1301.3",
|
||||
"@angular-devkit/core": "13.1.3",
|
||||
"@angular-devkit/schematics": "13.1.3",
|
||||
"@angular-devkit/build-angular": "13.1.4",
|
||||
"@angular-devkit/build-optimizer": "0.1301.4",
|
||||
"@angular-devkit/core": "13.1.4",
|
||||
"@angular-devkit/schematics": "13.1.4",
|
||||
"@angular/animations-12": "npm:@angular/animations@12.2.13",
|
||||
"@angular/cli": "13.1.3",
|
||||
"@angular/cli": "13.1.4",
|
||||
"@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",
|
||||
@@ -70,7 +70,7 @@
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-commonjs": "^21.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.0.4",
|
||||
"@schematics/angular": "13.1.3",
|
||||
"@schematics/angular": "13.1.4",
|
||||
"@types/angular": "^1.6.47",
|
||||
"@types/babel__core": "7.1.6",
|
||||
"@types/babel__generator": "7.6.1",
|
||||
@@ -137,7 +137,7 @@
|
||||
"puppeteer": "10.2.0",
|
||||
"reflect-metadata": "^0.1.3",
|
||||
"requirejs": "^2.3.6",
|
||||
"rollup": "~2.63.0",
|
||||
"rollup": "~2.65.0",
|
||||
"rollup-plugin-preserve-shebang": "^1.0.1",
|
||||
"rollup-plugin-sourcemaps": "^0.6.3",
|
||||
"rxjs": "^6.6.7",
|
||||
|
||||
@@ -410,7 +410,7 @@ export function compile({
|
||||
const hasError = diagnostics.some((diag) => diag.category === ts.DiagnosticCategory.Error);
|
||||
if (!hasError) {
|
||||
if (bazelOpts.tsickleGenerateExterns) {
|
||||
externs += tsickle.getGeneratedExterns(tsickleEmitResult.externs);
|
||||
externs += tsickle.getGeneratedExterns(tsickleEmitResult.externs, compilerOpts.rootDir!);
|
||||
}
|
||||
if (bazelOpts.manifest) {
|
||||
const manifest = constructManifest(tsickleEmitResult.modulesManifest, bazelHost);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, EmbeddedViewRef, Injector, Input, OnChanges, SimpleChanges, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChange, SimpleChanges, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
@@ -49,9 +49,6 @@ export class NgTemplateOutlet implements OnChanges {
|
||||
*/
|
||||
@Input() public ngTemplateOutlet: TemplateRef<any>|null = null;
|
||||
|
||||
/** Injector to be used within the embedded view. */
|
||||
@Input() public ngTemplateOutletInjector: Injector|null = null;
|
||||
|
||||
constructor(private _viewContainerRef: ViewContainerRef) {}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
@@ -62,17 +59,9 @@ export class NgTemplateOutlet implements OnChanges {
|
||||
viewContainerRef.remove(viewContainerRef.indexOf(this._viewRef));
|
||||
}
|
||||
|
||||
if (this.ngTemplateOutlet) {
|
||||
const {
|
||||
ngTemplateOutlet: template,
|
||||
ngTemplateOutletContext: context,
|
||||
ngTemplateOutletInjector: injector
|
||||
} = this;
|
||||
this._viewRef = viewContainerRef.createEmbeddedView(
|
||||
template, context, injector ? {injector} : undefined);
|
||||
} else {
|
||||
this._viewRef = null;
|
||||
}
|
||||
this._viewRef = this.ngTemplateOutlet ?
|
||||
viewContainerRef.createEmbeddedView(this.ngTemplateOutlet, this.ngTemplateOutletContext) :
|
||||
null;
|
||||
} else if (
|
||||
this._viewRef && changes['ngTemplateOutletContext'] && this.ngTemplateOutletContext) {
|
||||
this._viewRef.context = this.ngTemplateOutletContext;
|
||||
|
||||
@@ -127,7 +127,11 @@ export class BrowserViewportScroller implements ViewportScroller {
|
||||
this.scrollToElement(elSelected);
|
||||
// After scrolling to the element, the spec dictates that we follow the focus steps for the
|
||||
// target. Rather than following the robust steps, simply attempt focus.
|
||||
this.attemptFocus(elSelected);
|
||||
//
|
||||
// @see https://html.spec.whatwg.org/#get-the-focusable-area
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/focus
|
||||
// @see https://html.spec.whatwg.org/#focusable-area
|
||||
elSelected.focus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,21 +161,6 @@ export class BrowserViewportScroller implements ViewportScroller {
|
||||
this.window.scrollTo(left - offset[0], top - offset[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls `focus` on the `focusTarget` and returns `true` if the element was focused successfully.
|
||||
*
|
||||
* If `false`, further steps may be necessary to determine a valid substitute to be focused
|
||||
* instead.
|
||||
*
|
||||
* @see https://html.spec.whatwg.org/#get-the-focusable-area
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/focus
|
||||
* @see https://html.spec.whatwg.org/#focusable-area
|
||||
*/
|
||||
private attemptFocus(focusTarget: HTMLElement): boolean {
|
||||
focusTarget.focus();
|
||||
return this.document.activeElement === focusTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* We only support scroll restoration when we can get a hold of window.
|
||||
* This means that we do not support this behavior when running in a web worker.
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component, ContentChildren, Directive, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, OnDestroy, QueryList, TemplateRef} from '@angular/core';
|
||||
import {Component, ContentChildren, Directive, Injectable, NO_ERRORS_SCHEMA, OnDestroy, QueryList, TemplateRef} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
|
||||
@@ -29,13 +29,7 @@ describe('NgTemplateOutlet', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
TestComponent,
|
||||
CaptureTplRefs,
|
||||
DestroyableCmpt,
|
||||
MultiContextComponent,
|
||||
InjectValueComponent,
|
||||
],
|
||||
declarations: [TestComponent, CaptureTplRefs, DestroyableCmpt, MultiContextComponent],
|
||||
imports: [CommonModule],
|
||||
providers: [DestroyedSpyService]
|
||||
});
|
||||
@@ -268,19 +262,8 @@ describe('NgTemplateOutlet', () => {
|
||||
expect(componentInstance.context1).toEqual({name: 'two'});
|
||||
expect(componentInstance.context2).toEqual({name: 'one'});
|
||||
});
|
||||
|
||||
it('should be able to specify an injector', waitForAsync(() => {
|
||||
const template = `<ng-template #tpl><inject-value></inject-value></ng-template>` +
|
||||
`<ng-container *ngTemplateOutlet="tpl; injector: injector"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
fixture.componentInstance.injector =
|
||||
Injector.create({providers: [{provide: templateToken, useValue: 'world'}]});
|
||||
detectChangesAndExpectText('Hello world');
|
||||
}));
|
||||
});
|
||||
|
||||
const templateToken = new InjectionToken<string>('templateToken');
|
||||
|
||||
@Injectable()
|
||||
class DestroyedSpyService {
|
||||
destroyed = false;
|
||||
@@ -307,15 +290,6 @@ class TestComponent {
|
||||
currentTplRef!: TemplateRef<any>;
|
||||
context: any = {foo: 'bar'};
|
||||
value = 'bar';
|
||||
injector: Injector|null = null;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'inject-value',
|
||||
template: 'Hello {{tokenValue}}',
|
||||
})
|
||||
class InjectValueComponent {
|
||||
constructor(@Inject(templateToken) public tokenValue: string) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -51,7 +51,7 @@ interface LazyCompilationState {
|
||||
refEmitter: ReferenceEmitter;
|
||||
templateTypeChecker: TemplateTypeChecker;
|
||||
resourceRegistry: ResourceRegistry;
|
||||
extendedTemplateChecker: ExtendedTemplateChecker;
|
||||
extendedTemplateChecker: ExtendedTemplateChecker|null;
|
||||
}
|
||||
|
||||
|
||||
@@ -446,7 +446,7 @@ export class NgCompiler {
|
||||
diagnostics.push(
|
||||
...this.getNonTemplateDiagnostics().filter(diag => diag.file === file),
|
||||
...this.getTemplateDiagnosticsForFile(file, optimizeFor));
|
||||
if (this.options._extendedTemplateDiagnostics) {
|
||||
if (this.options.strictTemplates) {
|
||||
diagnostics.push(...this.getExtendedTemplateDiagnostics(file));
|
||||
}
|
||||
return this.addMessageTextDetails(diagnostics);
|
||||
@@ -460,8 +460,9 @@ export class NgCompiler {
|
||||
const ttc = compilation.templateTypeChecker;
|
||||
const diagnostics: ts.Diagnostic[] = [];
|
||||
diagnostics.push(...ttc.getDiagnosticsForComponent(component));
|
||||
if (this.options._extendedTemplateDiagnostics) {
|
||||
const extendedTemplateChecker = compilation.extendedTemplateChecker;
|
||||
|
||||
const extendedTemplateChecker = compilation.extendedTemplateChecker;
|
||||
if (this.options.strictTemplates && extendedTemplateChecker) {
|
||||
diagnostics.push(...extendedTemplateChecker.getDiagnosticsForComponent(component));
|
||||
}
|
||||
return this.addMessageTextDetails(diagnostics);
|
||||
@@ -886,6 +887,10 @@ export class NgCompiler {
|
||||
const diagnostics: ts.Diagnostic[] = [];
|
||||
const compilation = this.ensureAnalyzed();
|
||||
const extendedTemplateChecker = compilation.extendedTemplateChecker;
|
||||
if (!extendedTemplateChecker) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (sf !== undefined) {
|
||||
return compilation.traitCompiler.extendedTemplateCheck(sf, extendedTemplateChecker);
|
||||
}
|
||||
@@ -1062,8 +1067,11 @@ export class NgCompiler {
|
||||
reflector, this.adapter, this.incrementalCompilation, scopeRegistry, typeCheckScopeRegistry,
|
||||
this.delegatingPerfRecorder);
|
||||
|
||||
const extendedTemplateChecker = new ExtendedTemplateCheckerImpl(
|
||||
templateTypeChecker, checker, ALL_DIAGNOSTIC_FACTORIES, this.options);
|
||||
// Only construct the extended template checker if the configuration is valid and usable.
|
||||
const extendedTemplateChecker = this.constructionDiagnostics.length === 0 ?
|
||||
new ExtendedTemplateCheckerImpl(
|
||||
templateTypeChecker, checker, ALL_DIAGNOSTIC_FACTORIES, this.options) :
|
||||
null;
|
||||
|
||||
return {
|
||||
isCore,
|
||||
|
||||
@@ -229,20 +229,19 @@ class TemplateVisitor extends TmplAstRecursiveVisitor {
|
||||
let name: string;
|
||||
let kind: IdentifierKind.Element|IdentifierKind.Template;
|
||||
if (node instanceof TmplAstTemplate) {
|
||||
name = node.tagName;
|
||||
name = node.tagName ?? 'ng-template';
|
||||
kind = IdentifierKind.Template;
|
||||
} else {
|
||||
// Namespaced elements have a particular format for `node.name` that needs to be handled.
|
||||
// For example, an `<svg>` element has a `node.name` of `':svg:svg'`.
|
||||
|
||||
// TODO(alxhub): properly handle namespaced elements
|
||||
if (node.name.startsWith(':')) {
|
||||
name = node.name.split(':').pop()!;
|
||||
} else {
|
||||
name = node.name;
|
||||
}
|
||||
name = node.name;
|
||||
kind = IdentifierKind.Element;
|
||||
}
|
||||
// Namespaced elements have a particular format for `node.name` that needs to be handled.
|
||||
// For example, an `<svg>` element has a `node.name` of `':svg:svg'`.
|
||||
// TODO(alxhub): properly handle namespaced elements
|
||||
if (name.startsWith(':')) {
|
||||
name = name.split(':').pop()!;
|
||||
}
|
||||
|
||||
const sourceSpan = node.startSourceSpan;
|
||||
// An element's or template's source span can be of the form `<element>`, `<element />`, or
|
||||
// `<element></element>`. Only the selector is interesting to the indexer, so the source is
|
||||
|
||||
@@ -35,6 +35,20 @@ runInEachFileSystem(() => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle svg elements on templates', () => {
|
||||
const template = '<svg *ngIf="true"></svg>';
|
||||
const refs = getTemplateIdentifiers(bind(template));
|
||||
|
||||
const [ref] = Array.from(refs);
|
||||
expect(ref).toEqual({
|
||||
kind: IdentifierKind.Template,
|
||||
name: 'svg',
|
||||
span: new AbsoluteSourceSpan(1, 4),
|
||||
usedDirectives: new Set(),
|
||||
attributes: new Set(),
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle comments in interpolations', () => {
|
||||
const template = '{{foo // comment}}';
|
||||
const refs = getTemplateIdentifiers(bind(template));
|
||||
@@ -48,6 +62,33 @@ runInEachFileSystem(() => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle whitespace and comments in interpolations', () => {
|
||||
const template = '{{ foo // comment }}';
|
||||
const refs = getTemplateIdentifiers(bind(template));
|
||||
|
||||
const [ref] = Array.from(refs);
|
||||
expect(ref).toEqual({
|
||||
name: 'foo',
|
||||
kind: IdentifierKind.Property,
|
||||
span: new AbsoluteSourceSpan(5, 8),
|
||||
target: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('works when structural directives are on templates', () => {
|
||||
const template = '<ng-template *ngIf="true">';
|
||||
const refs = getTemplateIdentifiers(bind(template));
|
||||
|
||||
const [ref] = Array.from(refs);
|
||||
expect(ref).toEqual({
|
||||
kind: IdentifierKind.Template,
|
||||
name: 'ng-template',
|
||||
span: new AbsoluteSourceSpan(1, 12),
|
||||
usedDirectives: new Set(),
|
||||
attributes: new Set(),
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate nothing in empty template', () => {
|
||||
const template = '';
|
||||
const refs = getTemplateIdentifiers(bind(template));
|
||||
|
||||
@@ -86,7 +86,7 @@ export class ExtendedTemplateCheckerImpl implements ExtendedTemplateChecker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a `DiagnosticCategoryLabel` to its equivalent `ts.DiagnosticCategory` or `undefined` if
|
||||
* Converts a `DiagnosticCategoryLabel` to its equivalent `ts.DiagnosticCategory` or `null` if
|
||||
* the label is `DiagnosticCategoryLabel.Suppress`.
|
||||
*/
|
||||
function diagnosticLabelToCategory(label: DiagnosticCategoryLabel): ts.DiagnosticCategory|null {
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ consts: function() {
|
||||
},
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtemplate(0, MyComponent_0_Template, 2, 1, undefined, 0);
|
||||
$r3$.ɵɵtemplate(0, MyComponent_0_Template, 2, 1, null, 0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵproperty("ngIf", true);
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ consts: function() {
|
||||
},
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtemplate(0, MyComponent_0_Template, 1, 0, undefined, 0);
|
||||
$r3$.ɵɵtemplate(0, MyComponent_0_Template, 1, 0, null, 0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵproperty("ngIf", ctx.visible);
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ consts: function() {
|
||||
},
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtemplate(0, MyComponent_0_Template, 1, 0, undefined, 0);
|
||||
$r3$.ɵɵtemplate(0, MyComponent_0_Template, 1, 0, null, 0);
|
||||
$r3$.ɵɵtemplate(1, MyComponent_ng_container_1_Template, 2, 0, "ng-container", 0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
consts: [[4, "ngIf"], [3, "dir"]],
|
||||
template: function TestComp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$i0$.ɵɵtemplate(0, $TestComp_0_Template$, 1, 1, undefined, 0);
|
||||
$i0$.ɵɵtemplate(0, $TestComp_0_Template$, 1, 1, null, 0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵɵproperty("ngIf", true);
|
||||
|
||||
@@ -233,6 +233,24 @@ runInEachFileSystem(() => {
|
||||
const diagnostics = env.driveDiagnostics(0 /* expectedExitCode */);
|
||||
expect(diagnostics).toEqual([]);
|
||||
});
|
||||
|
||||
it('by throwing an error when given a bad category', () => {
|
||||
env.tsconfig({
|
||||
strictTemplates: true,
|
||||
extendedDiagnostics: {
|
||||
defaultCategory: 'not-a-category',
|
||||
},
|
||||
});
|
||||
|
||||
env.write('test.ts', warningComponent);
|
||||
|
||||
const diagnostics = env.driveDiagnostics(1 /* expectedExitCode */);
|
||||
expect(diagnostics.length).toBe(1);
|
||||
expect(diagnostics[0]).toEqual(jasmine.objectContaining({
|
||||
code: ngErrorCode(ErrorCode.CONFIG_EXTENDED_DIAGNOSTICS_UNKNOWN_CATEGORY_LABEL),
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,9 +41,7 @@ export class Parser {
|
||||
const sourceToLex = this._stripComments(input);
|
||||
const tokens = this._lexer.tokenize(sourceToLex);
|
||||
const ast =
|
||||
new _ParseAST(
|
||||
input, location, absoluteOffset, tokens, sourceToLex.length, true, this.errors, 0)
|
||||
.parseChain();
|
||||
new _ParseAST(input, location, absoluteOffset, tokens, true, this.errors, 0).parseChain();
|
||||
return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
|
||||
}
|
||||
|
||||
@@ -90,8 +88,7 @@ export class Parser {
|
||||
this._checkNoInterpolation(input, location, interpolationConfig);
|
||||
const sourceToLex = this._stripComments(input);
|
||||
const tokens = this._lexer.tokenize(sourceToLex);
|
||||
return new _ParseAST(
|
||||
input, location, absoluteOffset, tokens, sourceToLex.length, false, this.errors, 0)
|
||||
return new _ParseAST(input, location, absoluteOffset, tokens, false, this.errors, 0)
|
||||
.parseChain();
|
||||
}
|
||||
|
||||
@@ -138,8 +135,8 @@ export class Parser {
|
||||
absoluteValueOffset: number): TemplateBindingParseResult {
|
||||
const tokens = this._lexer.tokenize(templateValue);
|
||||
const parser = new _ParseAST(
|
||||
templateValue, templateUrl, absoluteValueOffset, tokens, templateValue.length,
|
||||
false /* parseAction */, this.errors, 0 /* relative offset */);
|
||||
templateValue, templateUrl, absoluteValueOffset, tokens, false /* parseAction */,
|
||||
this.errors, 0 /* relative offset */);
|
||||
return parser.parseTemplateBindings({
|
||||
source: templateKey,
|
||||
span: new AbsoluteSourceSpan(absoluteKeyOffset, absoluteKeyOffset + templateKey.length),
|
||||
@@ -159,10 +156,9 @@ export class Parser {
|
||||
const expressionText = expressions[i].text;
|
||||
const sourceToLex = this._stripComments(expressionText);
|
||||
const tokens = this._lexer.tokenize(sourceToLex);
|
||||
const ast = new _ParseAST(
|
||||
input, location, absoluteOffset, tokens, sourceToLex.length, false,
|
||||
this.errors, offsets[i])
|
||||
.parseChain();
|
||||
const ast =
|
||||
new _ParseAST(input, location, absoluteOffset, tokens, false, this.errors, offsets[i])
|
||||
.parseChain();
|
||||
expressionNodes.push(ast);
|
||||
}
|
||||
|
||||
@@ -180,7 +176,7 @@ export class Parser {
|
||||
const sourceToLex = this._stripComments(expression);
|
||||
const tokens = this._lexer.tokenize(sourceToLex);
|
||||
const ast = new _ParseAST(
|
||||
expression, location, absoluteOffset, tokens, sourceToLex.length,
|
||||
expression, location, absoluteOffset, tokens,
|
||||
/* parseAction */ false, this.errors, 0)
|
||||
.parseChain();
|
||||
const strings = ['', '']; // The prefix and suffix strings are both empty
|
||||
@@ -275,7 +271,7 @@ export class Parser {
|
||||
|
||||
private _stripComments(input: string): string {
|
||||
const i = this._commentStart(input);
|
||||
return i != null ? input.substring(0, i).trim() : input;
|
||||
return i != null ? input.substring(0, i) : input;
|
||||
}
|
||||
|
||||
private _commentStart(input: string): number|null {
|
||||
@@ -392,8 +388,8 @@ export class _ParseAST {
|
||||
|
||||
constructor(
|
||||
public input: string, public location: string, public absoluteOffset: number,
|
||||
public tokens: Token[], public inputLength: number, public parseAction: boolean,
|
||||
private errors: ParserError[], private offset: number) {}
|
||||
public tokens: Token[], public parseAction: boolean, private errors: ParserError[],
|
||||
private offset: number) {}
|
||||
|
||||
peek(offset: number): Token {
|
||||
const i = this.index + offset;
|
||||
@@ -429,7 +425,7 @@ export class _ParseAST {
|
||||
// No tokens have been processed yet; return the next token's start or the length of the input
|
||||
// if there is no token.
|
||||
if (this.tokens.length === 0) {
|
||||
return this.inputLength + this.offset;
|
||||
return this.input.length + this.offset;
|
||||
}
|
||||
return this.next.index + this.offset;
|
||||
}
|
||||
@@ -587,7 +583,7 @@ export class _ParseAST {
|
||||
if (exprs.length == 0) {
|
||||
// We have no expressions so create an empty expression that spans the entire input length
|
||||
const artificialStart = this.offset;
|
||||
const artificialEnd = this.offset + this.inputLength;
|
||||
const artificialEnd = this.offset + this.input.length;
|
||||
return new EmptyExpr(
|
||||
this.span(artificialStart, artificialEnd),
|
||||
this.sourceSpan(artificialStart, artificialEnd));
|
||||
@@ -623,7 +619,7 @@ export class _ParseAST {
|
||||
//
|
||||
// Therefore, we push the end of the `ParseSpan` for this pipe all the way up to the
|
||||
// beginning of the next token, or until the end of input if the next token is EOF.
|
||||
fullSpanEnd = this.next.index !== -1 ? this.next.index : this.inputLength + this.offset;
|
||||
fullSpanEnd = this.next.index !== -1 ? this.next.index : this.input.length + this.offset;
|
||||
|
||||
// The `nameSpan` for an empty pipe name is zero-length at the end of any whitespace
|
||||
// beyond the pipe character.
|
||||
|
||||
@@ -119,11 +119,23 @@ export class Element implements Node {
|
||||
|
||||
export class Template implements Node {
|
||||
constructor(
|
||||
public tagName: string, public attributes: TextAttribute[], public inputs: BoundAttribute[],
|
||||
public outputs: BoundEvent[], public templateAttrs: (BoundAttribute|TextAttribute)[],
|
||||
public children: Node[], public references: Reference[], public variables: Variable[],
|
||||
public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan,
|
||||
public endSourceSpan: ParseSourceSpan|null, public i18n?: I18nMeta) {}
|
||||
// tagName is the name of the container element, if applicable.
|
||||
// `null` is a special case for when there is a structural directive on an `ng-template` so
|
||||
// the renderer can differentiate between the synthetic template and the one written in the
|
||||
// file.
|
||||
public tagName: string|null,
|
||||
public attributes: TextAttribute[],
|
||||
public inputs: BoundAttribute[],
|
||||
public outputs: BoundEvent[],
|
||||
public templateAttrs: (BoundAttribute|TextAttribute)[],
|
||||
public children: Node[],
|
||||
public references: Reference[],
|
||||
public variables: Variable[],
|
||||
public sourceSpan: ParseSourceSpan,
|
||||
public startSourceSpan: ParseSourceSpan,
|
||||
public endSourceSpan: ParseSourceSpan|null,
|
||||
public i18n?: I18nMeta,
|
||||
) {}
|
||||
visit<Result>(visitor: Visitor<Result>): Result {
|
||||
return visitor.visitTemplate(this);
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ class HtmlAstToIvyAst implements html.Visitor {
|
||||
const children: t.Node[] =
|
||||
html.visitAll(preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children);
|
||||
|
||||
let parsedElement: t.Node|undefined;
|
||||
let parsedElement: t.Content|t.Template|t.Element|undefined;
|
||||
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
|
||||
// `<ng-content>`
|
||||
if (element.children &&
|
||||
@@ -235,13 +235,12 @@ class HtmlAstToIvyAst implements html.Visitor {
|
||||
// the wrapping template to prevent unnecessary i18n instructions from being generated. The
|
||||
// necessary i18n meta information will be extracted from child elements.
|
||||
const i18n = isTemplateElement && isI18nRootElement ? undefined : element.i18n;
|
||||
const name = parsedElement instanceof t.Template ? null : parsedElement.name;
|
||||
|
||||
// TODO(pk): test for this case
|
||||
parsedElement = new t.Template(
|
||||
(parsedElement as t.Element | t.Content).name, hoistedAttrs.attributes,
|
||||
hoistedAttrs.inputs, hoistedAttrs.outputs, templateAttrs, [parsedElement],
|
||||
[/* no references */], templateVariables, element.sourceSpan, element.startSourceSpan,
|
||||
element.endSourceSpan, i18n);
|
||||
name, hoistedAttrs.attributes, hoistedAttrs.inputs, hoistedAttrs.outputs, templateAttrs,
|
||||
[parsedElement], [/* no references */], templateVariables, element.sourceSpan,
|
||||
element.startSourceSpan, element.endSourceSpan, i18n);
|
||||
}
|
||||
if (isI18nRootElement) {
|
||||
this.inI18nBlock = false;
|
||||
|
||||
@@ -17,6 +17,17 @@ describe('expression AST absolute source spans', () => {
|
||||
.toContain(['foo', new AbsoluteSourceSpan(2, 5)]);
|
||||
});
|
||||
|
||||
it('should handle whitespace in interpolation', () => {
|
||||
expect(humanizeExpressionSource(parse('{{ foo }}', {preserveWhitespaces: true}).nodes))
|
||||
.toContain(['foo', new AbsoluteSourceSpan(4, 7)]);
|
||||
});
|
||||
|
||||
it('should handle whitespace and comment in interpolation', () => {
|
||||
expect(humanizeExpressionSource(
|
||||
parse('{{ foo // comment }}', {preserveWhitespaces: true}).nodes))
|
||||
.toContain(['foo', new AbsoluteSourceSpan(4, 7)]);
|
||||
});
|
||||
|
||||
it('should handle comment in an action binding', () => {
|
||||
expect(humanizeExpressionSource(parse('<button (click)="foo = true // comment">Save</button>', {
|
||||
preserveWhitespaces: true
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import {BindingType} from '../../src/expression_parser/ast';
|
||||
import * as t from '../../src/render3/r3_ast';
|
||||
import {unparse} from '../expression_parser/utils/unparser';
|
||||
|
||||
import {parseR3 as parse} from './view/util';
|
||||
|
||||
|
||||
@@ -272,6 +273,18 @@ describe('R3 template transform', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should support <ng-template> with structural directive', () => {
|
||||
expectFromHtml('<ng-template *ngIf="true"></ng-template>').toEqual([
|
||||
['Template'],
|
||||
['BoundAttribute', 0, 'ngIf', 'true'],
|
||||
['Template'],
|
||||
]);
|
||||
const res = parse('<ng-template *ngIf="true"></ng-template>', {ignoreError: false});
|
||||
expect((res.nodes[0] as t.Template).tagName).toEqual(null);
|
||||
expect(((res.nodes[0] as t.Template).children[0] as t.Template).tagName)
|
||||
.toEqual('ng-template');
|
||||
});
|
||||
|
||||
it('should support reference via #...', () => {
|
||||
expectFromHtml('<ng-template #a></ng-template>').toEqual([
|
||||
['Template'],
|
||||
|
||||
@@ -19,11 +19,6 @@
|
||||
"version": "14.0.0-beta",
|
||||
"description": "As of Angular version 13, `entryComponents` are no longer necessary.",
|
||||
"factory": "./migrations/entry-components/index"
|
||||
},
|
||||
"migration-v14-typed-forms": {
|
||||
"version": "9999.0.0",
|
||||
"description": "Experimental migration that adds <any>s for Typed Forms.",
|
||||
"factory": "./migrations/typed-forms/index"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,208 +80,214 @@ describe('Typed Forms migration', () => {
|
||||
shx.rm('-r', tmpDirPath);
|
||||
});
|
||||
|
||||
describe(`should add ${anySymbolName} to constructors`, () => {
|
||||
it('for FormControl', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { FormControl } from '@angular/forms';
|
||||
@Component({template: ''})
|
||||
export class MyComp {
|
||||
const fc1 = new FormControl();
|
||||
new FormControl(42);
|
||||
constructor() {}
|
||||
}
|
||||
`);
|
||||
await runMigration();
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`const fc1 = new FormControl<${anySymbolName}>();`);
|
||||
expect(tree.readContent('/index.ts')).toContain(`new FormControl<${anySymbolName}>(42);`);
|
||||
});
|
||||
// These tests are disabled because the migration is removed from migration.json, and cannot be
|
||||
// run.
|
||||
|
||||
it('for FormGroup', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { FormGroup } from '@angular/forms';
|
||||
@Component({template: ''})
|
||||
export class MyComp {
|
||||
const fg = new FormGroup({foo: 3});
|
||||
constructor() {}
|
||||
}
|
||||
`);
|
||||
await runMigration();
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`const fg = new FormGroup<${anySymbolName}>({foo: 3});`);
|
||||
});
|
||||
// describe(`should add ${anySymbolName} to constructors`, () => {
|
||||
// it('for FormControl', async () => {
|
||||
// writeFile('/index.ts', `
|
||||
// import { FormControl } from '@angular/forms';
|
||||
// @Component({template: ''})
|
||||
// export class MyComp {
|
||||
// const fc1 = new FormControl();
|
||||
// new FormControl(42);
|
||||
// constructor() {}
|
||||
// }
|
||||
// `);
|
||||
// await runMigration();
|
||||
// expect(tree.readContent('/index.ts'))
|
||||
// .toContain(`const fc1 = new FormControl<${anySymbolName}>();`);
|
||||
// expect(tree.readContent('/index.ts')).toContain(`new FormControl<${anySymbolName}>(42);`);
|
||||
// });
|
||||
|
||||
it('for FormArray', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { FormArray } from '@angular/forms';
|
||||
@Component({template: ''})
|
||||
export class MyComp {
|
||||
const fa = new FormArray([null]);
|
||||
constructor() {}
|
||||
}
|
||||
`);
|
||||
await runMigration();
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`const fa = new FormArray<${anySymbolName}[]>([null]);`);
|
||||
});
|
||||
// it('for FormGroup', async () => {
|
||||
// writeFile('/index.ts', `
|
||||
// import { FormGroup } from '@angular/forms';
|
||||
// @Component({template: ''})
|
||||
// export class MyComp {
|
||||
// const fg = new FormGroup({foo: 3});
|
||||
// constructor() {}
|
||||
// }
|
||||
// `);
|
||||
// await runMigration();
|
||||
// expect(tree.readContent('/index.ts'))
|
||||
// .toContain(`const fg = new FormGroup<${anySymbolName}>({foo: 3});`);
|
||||
// });
|
||||
|
||||
it('for FormControl with a qualified import', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { FormControl as FC } from '@angular/forms';
|
||||
@Component({template: ''})
|
||||
export class MyComp {
|
||||
const fc = new FC({foo: 3});
|
||||
constructor() {}
|
||||
}
|
||||
`);
|
||||
await runMigration();
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`const fc = new FC<${anySymbolName}>({foo: 3});`);
|
||||
});
|
||||
// it('for FormArray', async () => {
|
||||
// writeFile('/index.ts', `
|
||||
// import { FormArray } from '@angular/forms';
|
||||
// @Component({template: ''})
|
||||
// export class MyComp {
|
||||
// const fa = new FormArray([null]);
|
||||
// constructor() {}
|
||||
// }
|
||||
// `);
|
||||
// await runMigration();
|
||||
// expect(tree.readContent('/index.ts'))
|
||||
// .toContain(`const fa = new FormArray<${anySymbolName}[]>([null]);`);
|
||||
// });
|
||||
|
||||
it('for FormArray with a qualified import', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { FormArray as FA } from '@angular/forms';
|
||||
@Component({template: ''})
|
||||
export class MyComp {
|
||||
const fa = new FA([null]);
|
||||
constructor() {}
|
||||
}
|
||||
`);
|
||||
await runMigration();
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`const fa = new FA<${anySymbolName}[]>([null]);`);
|
||||
});
|
||||
// it('for FormControl with a qualified import', async () => {
|
||||
// writeFile('/index.ts', `
|
||||
// import { FormControl as FC } from '@angular/forms';
|
||||
// @Component({template: ''})
|
||||
// export class MyComp {
|
||||
// const fc = new FC({foo: 3});
|
||||
// constructor() {}
|
||||
// }
|
||||
// `);
|
||||
// await runMigration();
|
||||
// expect(tree.readContent('/index.ts'))
|
||||
// .toContain(`const fc = new FC<${anySymbolName}>({foo: 3});`);
|
||||
// });
|
||||
|
||||
it('but not for controls that already have type arguments', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { FormControl } from '@angular/forms';
|
||||
@Component({template: ''})
|
||||
export class MyComp {
|
||||
const fc1 = new FormControl<${anySymbolName}>();
|
||||
constructor() {}
|
||||
}
|
||||
`);
|
||||
await runMigration();
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`const fc1 = new FormControl<${anySymbolName}>();`);
|
||||
});
|
||||
});
|
||||
// it('for FormArray with a qualified import', async () => {
|
||||
// writeFile('/index.ts', `
|
||||
// import { FormArray as FA } from '@angular/forms';
|
||||
// @Component({template: ''})
|
||||
// export class MyComp {
|
||||
// const fa = new FA([null]);
|
||||
// constructor() {}
|
||||
// }
|
||||
// `);
|
||||
// await runMigration();
|
||||
// expect(tree.readContent('/index.ts'))
|
||||
// .toContain(`const fa = new FA<${anySymbolName}[]>([null]);`);
|
||||
// });
|
||||
|
||||
describe(`should add ${anySymbolName} to FormBuilder method`, () => {
|
||||
it('control', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
@Component({template: ''})
|
||||
export class MyComp {
|
||||
constructor() {
|
||||
const fb = new FormBuilder();
|
||||
const fc = fb.control(43);
|
||||
const fd = new FormBuilder().control(42);
|
||||
}
|
||||
}
|
||||
`);
|
||||
await runMigration();
|
||||
expect(tree.readContent('/index.ts')).toContain(`.control<${anySymbolName}>(43);`);
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`const fd = new FormBuilder().control<${anySymbolName}>(42)`);
|
||||
});
|
||||
// it('but not for controls that already have type arguments', async () => {
|
||||
// writeFile('/index.ts', `
|
||||
// import { FormControl } from '@angular/forms';
|
||||
// @Component({template: ''})
|
||||
// export class MyComp {
|
||||
// const fc1 = new FormControl<${anySymbolName}>();
|
||||
// constructor() {}
|
||||
// }
|
||||
// `);
|
||||
// await runMigration();
|
||||
// expect(tree.readContent('/index.ts'))
|
||||
// .toContain(`const fc1 = new FormControl<${anySymbolName}>();`);
|
||||
// });
|
||||
// });
|
||||
|
||||
it('group', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
@Component({template: ''})
|
||||
export class MyComp {
|
||||
constructor() {
|
||||
const fb = new FormBuilder();
|
||||
const fc = fb.group({});
|
||||
const fd = new FormBuilder().group({});
|
||||
}
|
||||
}
|
||||
`);
|
||||
await runMigration();
|
||||
expect(tree.readContent('/index.ts')).toContain(`fb.group<${anySymbolName}>({});`);
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`const fd = new FormBuilder().group<${anySymbolName}>({})`);
|
||||
});
|
||||
// describe(`should add ${anySymbolName} to FormBuilder method`, () => {
|
||||
// it('control', async () => {
|
||||
// writeFile('/index.ts', `
|
||||
// import { FormBuilder } from '@angular/forms';
|
||||
// @Component({template: ''})
|
||||
// export class MyComp {
|
||||
// constructor() {
|
||||
// const fb = new FormBuilder();
|
||||
// const fc = fb.control(43);
|
||||
// const fd = new FormBuilder().control(42);
|
||||
// }
|
||||
// }
|
||||
// `);
|
||||
// await runMigration();
|
||||
// expect(tree.readContent('/index.ts')).toContain(`.control<${anySymbolName}>(43);`);
|
||||
// expect(tree.readContent('/index.ts'))
|
||||
// .toContain(`const fd = new FormBuilder().control<${anySymbolName}>(42)`);
|
||||
// });
|
||||
|
||||
it('array', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
@Component({template: ''})
|
||||
export class MyComp {
|
||||
constructor() {
|
||||
const fb = new FormBuilder();
|
||||
const fc = fb.array([0]);
|
||||
const fd = new FormBuilder().array([0]);
|
||||
}
|
||||
}
|
||||
`);
|
||||
await runMigration();
|
||||
expect(tree.readContent('/index.ts')).toContain(`fb.array<${anySymbolName}[]>([0]);`);
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`const fd = new FormBuilder().array<${anySymbolName}[]>([0])`);
|
||||
});
|
||||
});
|
||||
// it('group', async () => {
|
||||
// writeFile('/index.ts', `
|
||||
// import { FormBuilder } from '@angular/forms';
|
||||
// @Component({template: ''})
|
||||
// export class MyComp {
|
||||
// constructor() {
|
||||
// const fb = new FormBuilder();
|
||||
// const fc = fb.group({});
|
||||
// const fd = new FormBuilder().group({});
|
||||
// }
|
||||
// }
|
||||
// `);
|
||||
// await runMigration();
|
||||
// expect(tree.readContent('/index.ts')).toContain(`fb.group<${anySymbolName}>({});`);
|
||||
// expect(tree.readContent('/index.ts'))
|
||||
// .toContain(`const fd = new FormBuilder().group<${anySymbolName}>({})`);
|
||||
// });
|
||||
|
||||
describe('should add import', () => {
|
||||
it('any when not already imported', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
@Component({template: ''})
|
||||
export class MyComp { }
|
||||
`);
|
||||
await runMigration();
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`import { ${anySymbolName}, FormBuilder } from '@angular/forms';`);
|
||||
});
|
||||
// it('array', async () => {
|
||||
// writeFile('/index.ts', `
|
||||
// import { FormBuilder } from '@angular/forms';
|
||||
// @Component({template: ''})
|
||||
// export class MyComp {
|
||||
// constructor() {
|
||||
// const fb = new FormBuilder();
|
||||
// const fc = fb.array([0]);
|
||||
// const fd = new FormBuilder().array([0]);
|
||||
// }
|
||||
// }
|
||||
// `);
|
||||
// await runMigration();
|
||||
// expect(tree.readContent('/index.ts')).toContain(`fb.array<${anySymbolName}[]>([0]);`);
|
||||
// expect(tree.readContent('/index.ts'))
|
||||
// .toContain(`const fd = new FormBuilder().array<${anySymbolName}[]>([0])`);
|
||||
// });
|
||||
// });
|
||||
|
||||
it('exclusively when not already imported', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { ${anySymbolName}, FormBuilder } from '@angular/forms';
|
||||
@Component({template: ''})
|
||||
export class MyComp { }
|
||||
`);
|
||||
await runMigration();
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`import { ${anySymbolName}, FormBuilder } from '@angular/forms';`);
|
||||
});
|
||||
});
|
||||
// describe('should add import', () => {
|
||||
// it('any when not already imported', async () => {
|
||||
// writeFile('/index.ts', `
|
||||
// import { FormBuilder } from '@angular/forms';
|
||||
// @Component({template: ''})
|
||||
// export class MyComp { }
|
||||
// `);
|
||||
// await runMigration();
|
||||
// expect(tree.readContent('/index.ts'))
|
||||
// .toContain(`import { ${anySymbolName}, FormBuilder } from '@angular/forms';`);
|
||||
// });
|
||||
|
||||
describe('should handle', () => {
|
||||
it('an integrated example', async () => {
|
||||
writeFile('/index.ts', `
|
||||
import { Component } from '@angular/core';
|
||||
import { AbstractControl, FormArray, FormBuilder, FormControl as FC, FormGroup } from '@angular/forms';
|
||||
// it('exclusively when not already imported', async () => {
|
||||
// writeFile('/index.ts', `
|
||||
// import { ${anySymbolName}, FormBuilder } from '@angular/forms';
|
||||
// @Component({template: ''})
|
||||
// export class MyComp { }
|
||||
// `);
|
||||
// await runMigration();
|
||||
// expect(tree.readContent('/index.ts'))
|
||||
// .toContain(`import { ${anySymbolName}, FormBuilder } from '@angular/forms';`);
|
||||
// });
|
||||
// });
|
||||
|
||||
@Component({template: ''})
|
||||
export class MyComponent {
|
||||
private _control = new FC(42);
|
||||
private _group = new FormGroup({});
|
||||
private _array = new FormArray([]);
|
||||
// describe('should handle', () => {
|
||||
// it('an integrated example', async () => {
|
||||
// writeFile('/index.ts', `
|
||||
// import { Component } from '@angular/core';
|
||||
// import { AbstractControl, FormArray, FormBuilder, FormControl as FC, FormGroup } from
|
||||
// '@angular/forms';
|
||||
|
||||
private fb = new FormBuilder();
|
||||
// @Component({template: ''})
|
||||
// export class MyComponent {
|
||||
// private _control = new FC(42);
|
||||
// private _group = new FormGroup({});
|
||||
// private _array = new FormArray([]);
|
||||
|
||||
build() {
|
||||
const c = this.fb.control(42);
|
||||
const g = this.fb.group({one: this.fb.control('')});
|
||||
const a = this.fb.array([42]);
|
||||
const fc2 = new FC(0);
|
||||
}
|
||||
}
|
||||
`);
|
||||
await runMigration();
|
||||
[`import { ${
|
||||
anySymbolName}, AbstractControl, FormArray, FormBuilder, FormControl as FC, FormGroup } from '@angular/forms';`,
|
||||
`private _control = new FC<${anySymbolName}>(42)`,
|
||||
`private _group = new FormGroup<${anySymbolName}>({})`,
|
||||
`private _array = new FormArray<${anySymbolName}[]>([])`,
|
||||
`const fc2 = new FC<${anySymbolName}>(0)`, `const c = this.fb.control<${anySymbolName}>(42)`,
|
||||
`const g = this.fb.group<${anySymbolName}>({one: this.fb.control<${anySymbolName}>('')})`,
|
||||
`const a = this.fb.array<${anySymbolName}[]>([42])`]
|
||||
.forEach(t => expect(tree.readContent('/index.ts')).toContain(t));
|
||||
});
|
||||
});
|
||||
// private fb = new FormBuilder();
|
||||
|
||||
// build() {
|
||||
// const c = this.fb.control(42);
|
||||
// const g = this.fb.group({one: this.fb.control('')});
|
||||
// const a = this.fb.array([42]);
|
||||
// const fc2 = new FC(0);
|
||||
// }
|
||||
// }
|
||||
// `);
|
||||
// await runMigration();
|
||||
// [`import { ${
|
||||
// anySymbolName}, AbstractControl, FormArray, FormBuilder, FormControl as FC, FormGroup
|
||||
// } from '@angular/forms';`,
|
||||
// `private _control = new FC<${anySymbolName}>(42)`,
|
||||
// `private _group = new FormGroup<${anySymbolName}>({})`,
|
||||
// `private _array = new FormArray<${anySymbolName}[]>([])`,
|
||||
// `const fc2 = new FC<${anySymbolName}>(0)`, `const c =
|
||||
// this.fb.control<${anySymbolName}>(42)`, `const g = this.fb.group<${anySymbolName}>({one:
|
||||
// this.fb.control<${anySymbolName}>('')})`, `const a =
|
||||
// this.fb.array<${anySymbolName}[]>([42])`]
|
||||
// .forEach(t => expect(tree.readContent('/index.ts')).toContain(t));
|
||||
// });
|
||||
// });
|
||||
|
||||
function writeFile(filePath: string, contents: string) {
|
||||
host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents));
|
||||
|
||||
@@ -6,12 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injector} from '../di/injector';
|
||||
import {assertLContainer} from '../render3/assert';
|
||||
import {ChainedInjector} from '../render3/chained_injector';
|
||||
import {createLView, renderView} from '../render3/instructions/shared';
|
||||
import {TContainerNode, TNode, TNodeType} from '../render3/interfaces/node';
|
||||
import {DECLARATION_LCONTAINER, INJECTOR, LView, LViewFlags, QUERIES, TView} from '../render3/interfaces/view';
|
||||
import {DECLARATION_LCONTAINER, LView, LViewFlags, QUERIES, TView} from '../render3/interfaces/view';
|
||||
import {getCurrentTNode, getLView} from '../render3/state';
|
||||
import {ViewRef as R3_ViewRef} from '../render3/view_ref';
|
||||
import {assertDefined} from '../util/assert';
|
||||
@@ -57,10 +55,9 @@ export abstract class TemplateRef<C> {
|
||||
* and attaches it to the view container.
|
||||
* @param context The data-binding context of the embedded view, as declared
|
||||
* in the `<ng-template>` usage.
|
||||
* @param injector Injector to be used within the embedded view.
|
||||
* @returns The new embedded view object.
|
||||
*/
|
||||
abstract createEmbeddedView(context: C, injector?: Injector): EmbeddedViewRef<C>;
|
||||
abstract createEmbeddedView(context: C): EmbeddedViewRef<C>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@@ -80,12 +77,11 @@ const R3TemplateRef = class TemplateRef<T> extends ViewEngineTemplateRef<T> {
|
||||
super();
|
||||
}
|
||||
|
||||
override createEmbeddedView(context: T, injector?: Injector): EmbeddedViewRef<T> {
|
||||
override createEmbeddedView(context: T): EmbeddedViewRef<T> {
|
||||
const embeddedTView = this._declarationTContainer.tViews as TView;
|
||||
const embeddedLView = createLView(
|
||||
this._declarationLView, embeddedTView, context, LViewFlags.CheckAlways, null,
|
||||
embeddedTView.declTNode, null, null, null,
|
||||
createEmbeddedViewInjector(injector, this._declarationLView[INJECTOR]));
|
||||
embeddedTView.declTNode, null, null, null, null);
|
||||
|
||||
const declarationLContainer = this._declarationLView[this._declarationTContainer.index];
|
||||
ngDevMode && assertLContainer(declarationLContainer);
|
||||
@@ -102,18 +98,6 @@ const R3TemplateRef = class TemplateRef<T> extends ViewEngineTemplateRef<T> {
|
||||
}
|
||||
};
|
||||
|
||||
function createEmbeddedViewInjector(
|
||||
embeddedViewInjector: Injector|undefined, declarationViewInjector: Injector|null): Injector|
|
||||
null {
|
||||
if (!embeddedViewInjector) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return declarationViewInjector ?
|
||||
new ChainedInjector(embeddedViewInjector, declarationViewInjector) :
|
||||
embeddedViewInjector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a TemplateRef given a node.
|
||||
*
|
||||
|
||||
@@ -90,24 +90,6 @@ export abstract class ViewContainerRef {
|
||||
*/
|
||||
abstract get length(): number;
|
||||
|
||||
/**
|
||||
* Instantiates an embedded view and inserts it
|
||||
* into this container.
|
||||
* @param templateRef The HTML template that defines the view.
|
||||
* @param context The data-binding context of the embedded view, as declared
|
||||
* in the `<ng-template>` usage.
|
||||
* @param options Extra configuration for the created view. Includes:
|
||||
* * index: The 0-based index at which to insert the new view into this container.
|
||||
* If not specified, appends the new view as the last entry.
|
||||
* * injector: Injector to be used within the embedded view.
|
||||
*
|
||||
* @returns The `ViewRef` instance for the newly created view.
|
||||
*/
|
||||
abstract createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, options?: {
|
||||
index?: number,
|
||||
injector?: Injector
|
||||
}): EmbeddedViewRef<C>;
|
||||
|
||||
/**
|
||||
* Instantiates an embedded view and inserts it
|
||||
* into this container.
|
||||
@@ -276,27 +258,9 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
|
||||
return this._lContainer.length - CONTAINER_HEADER_OFFSET;
|
||||
}
|
||||
|
||||
override createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, options?: {
|
||||
index?: number,
|
||||
injector?: Injector
|
||||
}): EmbeddedViewRef<C>;
|
||||
override createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number):
|
||||
EmbeddedViewRef<C>;
|
||||
override createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, indexOrOptions?: number|{
|
||||
index?: number,
|
||||
injector?: Injector
|
||||
}): EmbeddedViewRef<C> {
|
||||
let index: number|undefined;
|
||||
let injector: Injector|undefined;
|
||||
|
||||
if (typeof indexOrOptions === 'number') {
|
||||
index = indexOrOptions;
|
||||
} else if (indexOrOptions != null) {
|
||||
index = indexOrOptions.index;
|
||||
injector = indexOrOptions.injector;
|
||||
}
|
||||
|
||||
const viewRef = templateRef.createEmbeddedView(context || <any>{}, injector);
|
||||
EmbeddedViewRef<C> {
|
||||
const viewRef = templateRef.createEmbeddedView(context || <any>{});
|
||||
this.insert(viewRef, index);
|
||||
return viewRef;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ export const ES2015_INHERITED_CLASS_WITH_CTOR =
|
||||
* and inherit a constructor.
|
||||
*/
|
||||
export const ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR =
|
||||
/^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(\)\s*{\s*super\(\.\.\.arguments\)/;
|
||||
/^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(\)\s*{[^}]*super\(\.\.\.arguments\)/;
|
||||
|
||||
/**
|
||||
* Determine whether a stringified type is a class which delegates its constructor
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injector} from '../di/injector';
|
||||
import {InjectFlags} from '../di/interface/injector';
|
||||
import {ProviderToken} from '../di/provider_token';
|
||||
import {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from '../view/provider_flags';
|
||||
|
||||
/**
|
||||
* Injector that looks up a value using a specific injector, before falling back to the module
|
||||
* injector. Used primarily when creating components or embedded views dynamically.
|
||||
*/
|
||||
export class ChainedInjector implements Injector {
|
||||
constructor(private injector: Injector, private parentInjector: Injector) {}
|
||||
|
||||
get<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T {
|
||||
const value = this.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as T, flags);
|
||||
|
||||
if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR ||
|
||||
notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) {
|
||||
// Return the value from the root element injector when
|
||||
// - it provides it
|
||||
// (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
|
||||
// - the module injector should not be checked
|
||||
// (notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.parentInjector.get(token, notFoundValue, flags);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
import {ChangeDetectorRef as ViewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref';
|
||||
import {InjectionToken} from '../di/injection_token';
|
||||
import {Injector} from '../di/injector';
|
||||
import {InjectFlags} from '../di/interface/injector';
|
||||
import {ProviderToken} from '../di/provider_token';
|
||||
import {Type} from '../interface/type';
|
||||
import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
|
||||
import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver';
|
||||
@@ -17,9 +19,9 @@ import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory
|
||||
import {RendererFactory2} from '../render/api';
|
||||
import {Sanitizer} from '../sanitization/sanitizer';
|
||||
import {VERSION} from '../version';
|
||||
import {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from '../view/provider_flags';
|
||||
|
||||
import {assertComponentType} from './assert';
|
||||
import {ChainedInjector} from './chained_injector';
|
||||
import {createRootComponent, createRootComponentView, createRootContext, LifecycleHooksFeature} from './component';
|
||||
import {getComponentDef} from './definition';
|
||||
import {NodeInjector} from './di';
|
||||
@@ -78,6 +80,26 @@ export const SCHEDULER = new InjectionToken<((fn: () => void) => void)>('SCHEDUL
|
||||
factory: () => defaultScheduler,
|
||||
});
|
||||
|
||||
function createChainedInjector(rootViewInjector: Injector, moduleInjector: Injector): Injector {
|
||||
return {
|
||||
get: <T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T => {
|
||||
const value = rootViewInjector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as T, flags);
|
||||
|
||||
if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR ||
|
||||
notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) {
|
||||
// Return the value from the root element injector when
|
||||
// - it provides it
|
||||
// (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
|
||||
// - the module injector should not be checked
|
||||
// (notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
|
||||
return value;
|
||||
}
|
||||
|
||||
return moduleInjector.get(token, notFoundValue, flags);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Render3 implementation of {@link viewEngine_ComponentFactory}.
|
||||
*/
|
||||
@@ -114,11 +136,11 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
||||
ngModule?: viewEngine_NgModuleRef<any>|undefined): viewEngine_ComponentRef<T> {
|
||||
ngModule = ngModule || this.ngModule;
|
||||
|
||||
const rootViewInjector = ngModule ? new ChainedInjector(injector, ngModule.injector) : injector;
|
||||
const rootViewInjector =
|
||||
ngModule ? createChainedInjector(injector, ngModule.injector) : injector;
|
||||
|
||||
const rendererFactory =
|
||||
rootViewInjector.get(RendererFactory2, domRendererFactory3 as RendererFactory2) as
|
||||
RendererFactory3;
|
||||
rootViewInjector.get(RendererFactory2, domRendererFactory3) as RendererFactory3;
|
||||
const sanitizer = rootViewInjector.get(Sanitizer, null);
|
||||
|
||||
const hostRenderer = rendererFactory.createRenderer(null, this.componentDef);
|
||||
|
||||
@@ -48,7 +48,7 @@ import {LView, TData} from './view';
|
||||
* index + 7: cumulative bloom filter
|
||||
* index + 8: cumulative bloom filter
|
||||
* index + TNODE: TNode associated with this `NodeInjector`
|
||||
* `const tNode = tView.data[index + NodeInjectorOffset.TNODE]`
|
||||
* `canst tNode = tView.data[index + NodeInjectorOffset.TNODE]`
|
||||
* ```
|
||||
*/
|
||||
export const enum NodeInjectorOffset {
|
||||
|
||||
@@ -6,5 +6,13 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
||||
export const MATH_ML_NAMESPACE = 'http://www.w3.org/1998/MathML/';
|
||||
export const SVG_NAMESPACE = 'svg';
|
||||
export const SVG_NAMESPACE_URI = 'http://www.w3.org/2000/svg';
|
||||
export const MATH_ML_NAMESPACE = 'math';
|
||||
export const MATH_ML_NAMESPACE_URI = 'http://www.w3.org/1998/MathML/';
|
||||
|
||||
export function getNamespaceUri(namespace: string): string|null {
|
||||
const name = namespace.toLowerCase();
|
||||
return name === SVG_NAMESPACE ? SVG_NAMESPACE_URI :
|
||||
(name === MATH_ML_NAMESPACE ? MATH_ML_NAMESPACE_URI : null);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import {isProceduralRenderer, ProceduralRenderer3, Renderer3, unusedValueExportT
|
||||
import {RComment, RElement, RNode, RText} from './interfaces/renderer_dom';
|
||||
import {isLContainer, isLView} from './interfaces/type_checks';
|
||||
import {CHILD_HEAD, CLEANUP, DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, DestroyHookData, FLAGS, HookData, HookFn, HOST, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, T_HOST, TVIEW, TView, TViewType, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
||||
import {getNamespaceUri} from './namespaces';
|
||||
import {assertTNodeType} from './node_assert';
|
||||
import {profiler, ProfilerEvent} from './profiler';
|
||||
import {getLViewParent} from './util/view_traversal_utils';
|
||||
@@ -132,8 +133,9 @@ export function createElementNode(
|
||||
if (isProceduralRenderer(renderer)) {
|
||||
return renderer.createElement(name, namespace);
|
||||
} else {
|
||||
return namespace === null ? renderer.createElement(name) :
|
||||
renderer.createElementNS(namespace, name);
|
||||
const namespaceUri = namespace !== null ? getNamespaceUri(namespace) : null;
|
||||
return namespaceUri === null ? renderer.createElement(name) :
|
||||
renderer.createElementNS(namespaceUri, name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -153,7 +153,8 @@ export class NgZone {
|
||||
}
|
||||
|
||||
static isInAngularZone(): boolean {
|
||||
return Zone.current.get('isAngularZone') === true;
|
||||
// Zone needs to be checked, because this method might be called even when NoopNgZone is used.
|
||||
return typeof Zone !== 'undefined' && Zone.current.get('isAngularZone') === true;
|
||||
}
|
||||
|
||||
static assertInAngularZone(): void {
|
||||
|
||||
@@ -3546,603 +3546,4 @@ describe('di', () => {
|
||||
TestBed.configureTestingModule({declarations: [App]});
|
||||
expect(() => TestBed.createComponent(App)).toThrowError(/NullInjectorError/);
|
||||
});
|
||||
|
||||
describe('injector when creating embedded view', () => {
|
||||
const token = new InjectionToken<string>('greeting');
|
||||
|
||||
@Directive({selector: 'menu-trigger'})
|
||||
class MenuTrigger {
|
||||
@Input('triggerFor') menu!: TemplateRef<unknown>;
|
||||
|
||||
constructor(private viewContainerRef: ViewContainerRef) {}
|
||||
|
||||
open(injector: Injector|undefined) {
|
||||
this.viewContainerRef.createEmbeddedView(this.menu, undefined, {injector});
|
||||
}
|
||||
}
|
||||
|
||||
it('should be able to provide an injection token through a custom injector', () => {
|
||||
@Directive({selector: 'menu'})
|
||||
class Menu {
|
||||
constructor(@Inject(token) public tokenValue: string) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<menu-trigger [triggerFor]="menuTemplate"></menu-trigger>
|
||||
<ng-template #menuTemplate>
|
||||
<menu></menu>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(MenuTrigger) trigger!: MenuTrigger;
|
||||
@ViewChild(Menu) menu!: Menu;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, MenuTrigger, Menu]});
|
||||
const injector = Injector.create({providers: [{provide: token, useValue: 'hello'}]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.trigger.open(injector);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.menu.tokenValue).toBe('hello');
|
||||
});
|
||||
|
||||
it('should be able to provide an injection token to a nested template through a custom injector',
|
||||
() => {
|
||||
@Directive({selector: 'menu'})
|
||||
class Menu {
|
||||
constructor(@Inject(token) public tokenValue: string) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<menu-trigger #outerTrigger [triggerFor]="outerTemplate"></menu-trigger>
|
||||
<ng-template #outerTemplate>
|
||||
<menu></menu>
|
||||
|
||||
<menu-trigger #innerTrigger [triggerFor]="innerTemplate"></menu-trigger>
|
||||
<ng-template #innerTemplate>
|
||||
<menu #innerMenu></menu>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild('outerTrigger', {read: MenuTrigger}) outerTrigger!: MenuTrigger;
|
||||
@ViewChild('innerTrigger', {read: MenuTrigger}) innerTrigger!: MenuTrigger;
|
||||
@ViewChild('innerMenu', {read: Menu}) innerMenu!: Menu;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, MenuTrigger, Menu]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.outerTrigger.open(
|
||||
Injector.create({providers: [{provide: token, useValue: 'hello'}]}));
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.innerTrigger.open(undefined);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.innerMenu.tokenValue).toBe('hello');
|
||||
});
|
||||
|
||||
it('should be able to resolve a token from a custom grandparent injector if the token is not provided in the parent',
|
||||
() => {
|
||||
@Directive({selector: 'menu'})
|
||||
class Menu {
|
||||
constructor(@Inject(token) public tokenValue: string) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<menu-trigger #grandparentTrigger [triggerFor]="grandparentTemplate"></menu-trigger>
|
||||
<ng-template #grandparentTemplate>
|
||||
<menu></menu>
|
||||
|
||||
<menu-trigger #parentTrigger [triggerFor]="parentTemplate"></menu-trigger>
|
||||
<ng-template #parentTemplate>
|
||||
<menu></menu>
|
||||
|
||||
<menu-trigger #childTrigger [triggerFor]="childTemplate"></menu-trigger>
|
||||
<ng-template #childTemplate>
|
||||
<menu #childMenu></menu>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild('grandparentTrigger', {read: MenuTrigger}) grandparentTrigger!: MenuTrigger;
|
||||
@ViewChild('parentTrigger', {read: MenuTrigger}) parentTrigger!: MenuTrigger;
|
||||
@ViewChild('childTrigger', {read: MenuTrigger}) childTrigger!: MenuTrigger;
|
||||
@ViewChild('childMenu', {read: Menu}) childMenu!: Menu;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, MenuTrigger, Menu]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.grandparentTrigger.open(
|
||||
Injector.create({providers: [{provide: token, useValue: 'hello'}]}));
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.parentTrigger.open(Injector.create({providers: []}));
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.childTrigger.open(undefined);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.childMenu.tokenValue).toBe('hello');
|
||||
});
|
||||
|
||||
it('should resolve value from node injector if it is lower than embedded view injector', () => {
|
||||
@Directive({selector: 'menu'})
|
||||
class Menu {
|
||||
constructor(@Inject(token) public tokenValue: string) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'wrapper',
|
||||
providers: [{provide: token, useValue: 'hello from wrapper'}],
|
||||
template: `
|
||||
<menu-trigger [triggerFor]="menuTemplate"></menu-trigger>
|
||||
<ng-template #menuTemplate>
|
||||
<menu></menu>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
class Wrapper {
|
||||
@ViewChild(MenuTrigger) trigger!: MenuTrigger;
|
||||
@ViewChild(Menu) menu!: Menu;
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<menu-trigger [triggerFor]="menuTemplate"></menu-trigger>
|
||||
<ng-template #menuTemplate>
|
||||
<wrapper></wrapper>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(MenuTrigger) trigger!: MenuTrigger;
|
||||
@ViewChild(Wrapper) wrapper!: Wrapper;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, MenuTrigger, Menu, Wrapper]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.trigger.open(
|
||||
Injector.create({providers: [{provide: token, useValue: 'hello from injector'}]}));
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.wrapper.trigger.open(undefined);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.wrapper.menu.tokenValue).toBe('hello from wrapper');
|
||||
});
|
||||
|
||||
it('should be able to inject a value provided at the module level', () => {
|
||||
@Directive({selector: 'menu'})
|
||||
class Menu {
|
||||
constructor(@Inject(token) public tokenValue: string) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<menu-trigger [triggerFor]="menuTemplate"></menu-trigger>
|
||||
<ng-template #menuTemplate>
|
||||
<menu></menu>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(MenuTrigger) trigger!: MenuTrigger;
|
||||
@ViewChild(Menu) menu!: Menu;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [App, MenuTrigger, Menu],
|
||||
exports: [App, MenuTrigger, Menu],
|
||||
providers: [{provide: token, useValue: 'hello'}]
|
||||
})
|
||||
class Module {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({imports: [Module]});
|
||||
const injector = Injector.create({providers: []});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.trigger.open(injector);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.menu.tokenValue).toBe('hello');
|
||||
});
|
||||
|
||||
it('should have value from custom injector take precedence over module injector', () => {
|
||||
@Directive({selector: 'menu'})
|
||||
class Menu {
|
||||
constructor(@Inject(token) public tokenValue: string) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<menu-trigger [triggerFor]="menuTemplate"></menu-trigger>
|
||||
<ng-template #menuTemplate>
|
||||
<menu></menu>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(MenuTrigger) trigger!: MenuTrigger;
|
||||
@ViewChild(Menu) menu!: Menu;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [App, MenuTrigger, Menu],
|
||||
exports: [App, MenuTrigger, Menu],
|
||||
providers: [{provide: token, useValue: 'hello from module'}]
|
||||
})
|
||||
class Module {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({imports: [Module]});
|
||||
const injector =
|
||||
Injector.create({providers: [{provide: token, useValue: 'hello from injector'}]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.trigger.open(injector);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.menu.tokenValue).toBe('hello from injector');
|
||||
});
|
||||
|
||||
it('should be able to inject built-in tokens when a custom injector is provided', () => {
|
||||
@Directive({selector: 'menu'})
|
||||
class Menu {
|
||||
constructor(public elementRef: ElementRef, public changeDetectorRef: ChangeDetectorRef) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<menu-trigger [triggerFor]="menuTemplate"></menu-trigger>
|
||||
<ng-template #menuTemplate>
|
||||
<menu></menu>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(MenuTrigger) trigger!: MenuTrigger;
|
||||
@ViewChild(Menu) menu!: Menu;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, MenuTrigger, Menu]});
|
||||
const injector = Injector.create({providers: []});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.trigger.open(injector);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.menu.elementRef.nativeElement)
|
||||
.toBe(fixture.nativeElement.querySelector('menu'));
|
||||
expect(fixture.componentInstance.menu.changeDetectorRef).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have value from parent component injector take precedence over module injector',
|
||||
() => {
|
||||
@Directive({selector: 'menu'})
|
||||
class Menu {
|
||||
constructor(@Inject(token) public tokenValue: string) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<menu-trigger [triggerFor]="menuTemplate"></menu-trigger>
|
||||
<ng-template #menuTemplate>
|
||||
<menu></menu>
|
||||
</ng-template>
|
||||
`,
|
||||
providers: [{provide: token, useValue: 'hello from parent'}]
|
||||
})
|
||||
class App {
|
||||
@ViewChild(MenuTrigger) trigger!: MenuTrigger;
|
||||
@ViewChild(Menu) menu!: Menu;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [App, MenuTrigger, Menu],
|
||||
exports: [App, MenuTrigger, Menu],
|
||||
providers: [{provide: token, useValue: 'hello from module'}]
|
||||
})
|
||||
class Module {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({imports: [Module]});
|
||||
const injector = Injector.create({providers: []});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.trigger.open(injector);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.menu.tokenValue).toBe('hello from parent');
|
||||
});
|
||||
|
||||
it('should be able to inject an injectable with dependencies', () => {
|
||||
@Injectable()
|
||||
class Greeter {
|
||||
constructor(@Inject(token) private tokenValue: string) {}
|
||||
|
||||
greet() {
|
||||
return `hello from ${this.tokenValue}`;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: 'menu'})
|
||||
class Menu {
|
||||
constructor(public greeter: Greeter) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<menu-trigger [triggerFor]="menuTemplate"></menu-trigger>
|
||||
<ng-template #menuTemplate>
|
||||
<menu></menu>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(MenuTrigger) trigger!: MenuTrigger;
|
||||
@ViewChild(Menu) menu!: Menu;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [App, MenuTrigger, Menu],
|
||||
exports: [App, MenuTrigger, Menu],
|
||||
providers: [{provide: token, useValue: 'module'}]
|
||||
})
|
||||
class Module {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({imports: [Module]});
|
||||
const injector = Injector.create({
|
||||
providers: [
|
||||
{provide: Greeter, useClass: Greeter},
|
||||
{provide: token, useValue: 'injector'},
|
||||
]
|
||||
});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.trigger.open(injector);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.menu.greeter.greet()).toBe('hello from injector');
|
||||
});
|
||||
|
||||
it('should be able to inject a value from a grandparent component when a custom injector is provided',
|
||||
() => {
|
||||
@Directive({selector: 'menu'})
|
||||
class Menu {
|
||||
constructor(@Inject(token) public tokenValue: string) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'parent',
|
||||
template: `
|
||||
<menu-trigger [triggerFor]="menuTemplate"></menu-trigger>
|
||||
<ng-template #menuTemplate>
|
||||
<menu></menu>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
class Parent {
|
||||
@ViewChild(MenuTrigger) trigger!: MenuTrigger;
|
||||
@ViewChild(Menu) menu!: Menu;
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: '<parent></parent>',
|
||||
providers: [{provide: token, useValue: 'hello from grandparent'}]
|
||||
})
|
||||
class GrandParent {
|
||||
@ViewChild(Parent) parent!: Parent;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [GrandParent, Parent, MenuTrigger, Menu]});
|
||||
const injector = Injector.create({providers: []});
|
||||
const fixture = TestBed.createComponent(GrandParent);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.parent.trigger.open(injector);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.parent.menu.tokenValue).toBe('hello from grandparent');
|
||||
});
|
||||
|
||||
it('should be able to use a custom injector when created through TemplateRef', () => {
|
||||
let injectedValue: string|undefined;
|
||||
|
||||
@Directive({selector: 'menu'})
|
||||
class Menu {
|
||||
constructor(@Inject(token) tokenValue: string) {
|
||||
injectedValue = tokenValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ng-template>
|
||||
<menu></menu>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(TemplateRef) template!: TemplateRef<unknown>;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [App, Menu],
|
||||
exports: [App, Menu],
|
||||
providers: [{provide: token, useValue: 'hello from module'}]
|
||||
})
|
||||
class Module {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({imports: [Module]});
|
||||
const injector =
|
||||
Injector.create({providers: [{provide: token, useValue: 'hello from injector'}]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.template.createEmbeddedView({}, injector);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(injectedValue).toBe('hello from injector');
|
||||
});
|
||||
|
||||
it('should use a custom injector when the view is created outside of the declaration view',
|
||||
() => {
|
||||
const declarerToken = new InjectionToken<string>('declarerToken');
|
||||
const creatorToken = new InjectionToken<string>('creatorToken');
|
||||
|
||||
@Directive({selector: 'menu'})
|
||||
class Menu {
|
||||
constructor(
|
||||
@Inject(token) public tokenValue: string,
|
||||
@Optional() @Inject(declarerToken) public declarerTokenValue: string,
|
||||
@Optional() @Inject(creatorToken) public creatorTokenValue: string) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'declarer',
|
||||
template: '<ng-template><menu></menu></ng-template>',
|
||||
providers: [{provide: declarerToken, useValue: 'hello from declarer'}]
|
||||
})
|
||||
class Declarer {
|
||||
@ViewChild(Menu) menu!: Menu;
|
||||
@ViewChild(TemplateRef) template!: TemplateRef<unknown>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'creator',
|
||||
template: '<menu-trigger></menu-trigger>',
|
||||
providers: [{provide: creatorToken, useValue: 'hello from creator'}]
|
||||
})
|
||||
class Creator {
|
||||
@ViewChild(MenuTrigger) trigger!: MenuTrigger;
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<declarer></declarer>
|
||||
<creator></creator>
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(Declarer) declarer!: Declarer;
|
||||
@ViewChild(Creator) creator!: Creator;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [App, MenuTrigger, Menu, Declarer, Creator]});
|
||||
const injector = Injector.create({providers: [{provide: token, useValue: 'hello'}]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
const {declarer, creator} = fixture.componentInstance;
|
||||
|
||||
creator.trigger.menu = declarer.template;
|
||||
creator.trigger.open(injector);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(declarer.menu.tokenValue).toBe('hello');
|
||||
expect(declarer.menu.declarerTokenValue).toBe('hello from declarer');
|
||||
expect(declarer.menu.creatorTokenValue).toBeNull();
|
||||
});
|
||||
|
||||
it('should behave consistently with `createComponent` when token is shadowed in node injector',
|
||||
() => {
|
||||
@Directive({selector: 'trigger'})
|
||||
class Trigger {
|
||||
constructor(public viewContainerRef: ViewContainerRef) {}
|
||||
}
|
||||
|
||||
@Directive({selector: 'overlay'})
|
||||
class Overlay {
|
||||
constructor(@Inject(token) public tokenValue: string) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'overlay-host',
|
||||
template: '<overlay></overlay>',
|
||||
providers: [{provide: token, useValue: 'hello from parent'}],
|
||||
})
|
||||
class OverlayHost {
|
||||
@ViewChild(Overlay) overlay!: Overlay;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'wrapper',
|
||||
template: '<ng-content></ng-content>',
|
||||
providers: [{provide: token, useValue: 'hello from parent'}],
|
||||
})
|
||||
class Wrapper {
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<trigger></trigger>
|
||||
|
||||
<wrapper>
|
||||
<ng-template #template>
|
||||
<overlay></overlay>
|
||||
</ng-template>
|
||||
</wrapper>
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(Trigger) trigger!: Trigger;
|
||||
@ViewChild('template', {read: TemplateRef}) template!: TemplateRef<any>;
|
||||
@ViewChild(Overlay) overlayInTemplate!: Overlay;
|
||||
|
||||
openFromTemplate(injector: Injector) {
|
||||
this.trigger.viewContainerRef.createEmbeddedView(this.template, null, {injector});
|
||||
}
|
||||
|
||||
openFromComponent(injector: Injector) {
|
||||
return this.trigger.viewContainerRef.createComponent(OverlayHost, {injector});
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [App, Trigger, Overlay, OverlayHost, Wrapper]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
const providers = [{provide: token, useValue: 'hello from custom injector'}];
|
||||
|
||||
fixture.componentInstance.openFromTemplate(Injector.create({providers}));
|
||||
fixture.detectChanges();
|
||||
|
||||
const componentRef =
|
||||
fixture.componentInstance.openFromComponent(Injector.create({providers}));
|
||||
fixture.detectChanges();
|
||||
|
||||
// The node injector is expected to take precedence over the provided injector, despite
|
||||
// technically being higher in the tree, because the custom one is provided as a module
|
||||
// injector. This is consistent with how `createComponent` has always worked and avoids
|
||||
// ambiguity as to whether the provided injector should be in the declaration or insertion
|
||||
// node injector tree.
|
||||
expect(fixture.componentInstance.overlayInTemplate.tokenValue).toBe('hello from parent');
|
||||
expect(componentRef.instance.overlay.tokenValue).toBe('hello from parent');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -542,10 +542,6 @@ describe('runtime i18n', () => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{provide: DOCUMENT, useFactory: _document, deps: []},
|
||||
// TODO(FW-811): switch back to default server renderer (i.e. remove the line
|
||||
// below) once it starts to support Ivy namespace format (URIs) correctly. For
|
||||
// now, use `DomRenderer` that supports Ivy namespace format.
|
||||
{provide: RendererFactory2, useClass: DomRendererFactory2}
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -197,10 +197,6 @@ describe('ViewContainerRef', () => {
|
||||
declarations: [TestComp, SvgComp, MathMLComp],
|
||||
providers: [
|
||||
{provide: DOCUMENT, useFactory: _document, deps: []},
|
||||
// TODO(FW-811): switch back to default server renderer (i.e. remove the line below)
|
||||
// once it starts to support Ivy namespace format (URIs) correctly. For now, use
|
||||
// `DomRenderer` that supports Ivy namespace format.
|
||||
{provide: RendererFactory2, useClass: DomRendererFactory2}
|
||||
],
|
||||
});
|
||||
const fixture = TestBed.createComponent(TestComp);
|
||||
|
||||
@@ -12,6 +12,7 @@ yarn bazel run //packages/core/test/bundling/forms:symbol_test.accept
|
||||
yarn bazel run //packages/core/test/bundling/hello_world:symbol_test.accept
|
||||
yarn bazel run //packages/core/test/bundling/router:symbol_test.accept
|
||||
yarn bazel run //packages/core/test/bundling/todo:symbol_test.accept
|
||||
yarn bazel run //packages/core/test/bundling/animations:symbol_test.accept
|
||||
```
|
||||
|
||||
## Running all symbol tests
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
load("//tools:defaults.bzl", "app_bundle", "jasmine_node_test", "ng_module", "ts_library")
|
||||
load("//tools/symbol-extractor:index.bzl", "js_expected_symbol_test")
|
||||
load("@npm//http-server:index.bzl", "http_server")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
ng_module(
|
||||
name = "animations",
|
||||
srcs = ["index.ts"],
|
||||
deps = [
|
||||
"//packages/animations",
|
||||
"//packages/core",
|
||||
"//packages/platform-browser",
|
||||
"//packages/platform-browser/animations",
|
||||
],
|
||||
)
|
||||
|
||||
app_bundle(
|
||||
name = "bundle",
|
||||
entry_point = ":index.ts",
|
||||
deps = [
|
||||
":animations",
|
||||
"@npm//rxjs",
|
||||
],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "test_lib",
|
||||
testonly = True,
|
||||
srcs = glob(["*_spec.ts"]),
|
||||
deps = [
|
||||
"//packages:types",
|
||||
"//packages/compiler",
|
||||
"//packages/core",
|
||||
"//packages/core/testing",
|
||||
"//packages/private/testing",
|
||||
],
|
||||
)
|
||||
|
||||
jasmine_node_test(
|
||||
name = "test",
|
||||
data = [
|
||||
":bundle.debug.min.js",
|
||||
":bundle.js",
|
||||
":bundle.min.js",
|
||||
":bundle.min.js.br",
|
||||
],
|
||||
deps = [":test_lib"],
|
||||
)
|
||||
|
||||
js_expected_symbol_test(
|
||||
name = "symbol_test",
|
||||
src = ":bundle.debug.min.js",
|
||||
golden = ":bundle.golden_symbols.json",
|
||||
)
|
||||
|
||||
http_server(
|
||||
name = "prodserver",
|
||||
data = [
|
||||
"index.html",
|
||||
":bundle.debug.min.js",
|
||||
":bundle.min.js",
|
||||
],
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Angular Animations Example</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- The Angular application will be bootstrapped into this element. -->
|
||||
|
||||
<app-root></app-root>
|
||||
|
||||
<!--
|
||||
Script tag which bootstraps the application. Use `?debug` in URL to select
|
||||
the debug version of the script.
|
||||
|
||||
There are two scripts sources: `bundle.min.js` and `bundle.debug.min.js` You can
|
||||
switch between which bundle the browser loads to experiment with the application.
|
||||
|
||||
- `bundle.min.js`: Is what the site would serve to their users. It has gone
|
||||
through rollup, build-optimizer, and uglify with tree shaking.
|
||||
- `bundle.debug.min.js`: Is what the developer would like to see when debugging
|
||||
the application. It has also gone through full pipeline of esbuild, babel optimization,
|
||||
plugins from the devkit and terser, however mangling is disabled and the minified
|
||||
file is formatted using prettier (to ease debugging).
|
||||
-->
|
||||
<script>
|
||||
document.write('<script src="' +
|
||||
(document.location.search.endsWith('debug') ? '/bundle.debug.min.js' : '/bundle.min.js') +
|
||||
'"></' + 'script>');
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {animate, style, transition, trigger} from '@angular/animations';
|
||||
import {Component, NgModule, ɵNgModuleFactory as NgModuleFactory} from '@angular/core';
|
||||
import {BrowserModule, platformBrowser} from '@angular/platform-browser';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
|
||||
@Component({
|
||||
selector: 'app-animations',
|
||||
template: `
|
||||
<div [@myAnimation]="exp"></div>
|
||||
`,
|
||||
animations:
|
||||
[trigger('myAnimation', [transition('* => on', [animate(1000, style({opacity: 1}))])])]
|
||||
})
|
||||
class AnimationsComponent {
|
||||
exp: any = false;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
template: `
|
||||
<app-animations></app-animations>
|
||||
`
|
||||
})
|
||||
class RootComponent {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [RootComponent, AnimationsComponent],
|
||||
imports: [BrowserModule, BrowserAnimationsModule],
|
||||
})
|
||||
class AnimationsExampleModule {
|
||||
ngDoBootstrap(app: any) {
|
||||
app.bootstrap(RootComponent);
|
||||
}
|
||||
}
|
||||
|
||||
(window as any).waitForApp =
|
||||
platformBrowser().bootstrapModule(AnimationsExampleModule, {ngZone: 'noop'});
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import '@angular/compiler';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
const UTF8 = {
|
||||
encoding: 'utf-8'
|
||||
};
|
||||
const PACKAGE = 'angular/packages/core/test/bundling/animations';
|
||||
|
||||
describe('treeshaking with uglify', () => {
|
||||
let content: string;
|
||||
// We use the debug version as otherwise symbols/identifiers would be mangled (and the test would
|
||||
// always pass)
|
||||
const contentPath = require.resolve(path.join(PACKAGE, 'bundle.debug.min.js'));
|
||||
beforeAll(() => {
|
||||
content = fs.readFileSync(contentPath, UTF8);
|
||||
});
|
||||
|
||||
it('should drop unused TypeScript helpers', () => {
|
||||
expect(content).not.toContain('__asyncGenerator');
|
||||
});
|
||||
|
||||
it('should not contain rxjs from commonjs distro', () => {
|
||||
expect(content).not.toContain('commonjsGlobal');
|
||||
expect(content).not.toContain('createCommonjsModule');
|
||||
});
|
||||
});
|
||||
@@ -77,9 +77,6 @@
|
||||
{
|
||||
"name": "COMPOSITION_BUFFER_MODE"
|
||||
},
|
||||
{
|
||||
"name": "ChainedInjector"
|
||||
},
|
||||
{
|
||||
"name": "ChangeDetectionStrategy"
|
||||
},
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
{
|
||||
"name": "AbstractFormGroupDirective"
|
||||
},
|
||||
{
|
||||
"name": "AbstractValidatorDirective"
|
||||
},
|
||||
{
|
||||
"name": "AnonymousSubject"
|
||||
},
|
||||
@@ -77,9 +80,6 @@
|
||||
{
|
||||
"name": "COMPOSITION_BUFFER_MODE"
|
||||
},
|
||||
{
|
||||
"name": "ChainedInjector"
|
||||
},
|
||||
{
|
||||
"name": "ChangeDetectionStrategy"
|
||||
},
|
||||
@@ -1310,6 +1310,9 @@
|
||||
{
|
||||
"name": "notFoundValueOrThrow"
|
||||
},
|
||||
{
|
||||
"name": "nullValidator"
|
||||
},
|
||||
{
|
||||
"name": "observable"
|
||||
},
|
||||
@@ -1400,6 +1403,9 @@
|
||||
{
|
||||
"name": "renderView"
|
||||
},
|
||||
{
|
||||
"name": "requiredValidator"
|
||||
},
|
||||
{
|
||||
"name": "resetPreOrderHookFlags"
|
||||
},
|
||||
|
||||
@@ -92,9 +92,6 @@
|
||||
{
|
||||
"name": "CatchSubscriber"
|
||||
},
|
||||
{
|
||||
"name": "ChainedInjector"
|
||||
},
|
||||
{
|
||||
"name": "ChangeDetectionStrategy"
|
||||
},
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
{
|
||||
"name": "CLEAN_PROMISE"
|
||||
},
|
||||
{
|
||||
"name": "ChainedInjector"
|
||||
},
|
||||
{
|
||||
"name": "ChangeDetectionStrategy"
|
||||
},
|
||||
|
||||
@@ -292,6 +292,51 @@ class TestObj {
|
||||
expect(isDelegateCtor(ChildWithCtorComplexBase)).toBe(false);
|
||||
});
|
||||
|
||||
it('should deal with coverage instrumentation statements', () => {
|
||||
// The Istanbul coverage tool augments code with additional statements to capture coverage
|
||||
// information, and this test verifies that those do not interfere with our ability to
|
||||
// detect delegate constructors.
|
||||
const Es2015ClassNoCtor = `class TestService extends Parent {
|
||||
constructor() {
|
||||
cov_8nt6qq5zt().f[2]++;
|
||||
cov_8nt6qq5zt().s[6]++;
|
||||
super(...arguments);
|
||||
cov_8nt6qq5zt().s[7]++;
|
||||
this.foo = 'bar';
|
||||
}
|
||||
}`;
|
||||
const Es2015ClassWithCtor = `class TestService extends Parent {
|
||||
constructor() {
|
||||
cov_8nt6qq5zt().f[2]++;
|
||||
cov_8nt6qq5zt().s[6]++;
|
||||
super();
|
||||
cov_8nt6qq5zt().s[7]++;
|
||||
this.foo = 'bar';
|
||||
}
|
||||
}`;
|
||||
const Es5ClassNoCtor = `function TestService() {
|
||||
cov_8nt6qq5zt().f[4]++;
|
||||
var _this = (cov_8nt6qq5zt().s[8]++, (cov_8nt6qq5zt().b[0][0]++, _super !== null) && (cov_8nt6qq5zt().b[0][1]++, _super.apply(this, arguments)) || (cov_8nt6qq5zt().b[0][2]++, this));
|
||||
cov_8nt6qq5zt().s[9]++;
|
||||
_this.foo = 'bar';
|
||||
cov_8nt6qq5zt().s[10]++;
|
||||
return _this;
|
||||
}`;
|
||||
const Es5ClassWithCtor = `function TestService() {
|
||||
cov_8nt6qq5zt().f[4]++;
|
||||
var _this = _super.call();
|
||||
cov_8nt6qq5zt().s[9]++;
|
||||
_this.foo = 'bar';
|
||||
cov_8nt6qq5zt().s[10]++;
|
||||
return _this;
|
||||
}`;
|
||||
|
||||
expect(isDelegateCtor(Es2015ClassNoCtor)).toBe(true);
|
||||
expect(isDelegateCtor(Es2015ClassWithCtor)).toBe(false);
|
||||
expect(isDelegateCtor(Es5ClassNoCtor)).toBe(true);
|
||||
expect(isDelegateCtor(Es5ClassWithCtor)).toBe(false);
|
||||
});
|
||||
|
||||
it('should properly handle all class forms', () => {
|
||||
const ctor = (str: string) => expect(isDelegateCtor(str)).toBe(false);
|
||||
const noCtor = (str: string) => expect(isDelegateCtor(str)).toBe(true);
|
||||
@@ -299,6 +344,7 @@ class TestObj {
|
||||
ctor(`class Bar extends Foo {constructor(){}}`);
|
||||
ctor(`class Bar extends Foo { constructor ( ) {} }`);
|
||||
ctor(`class Bar extends Foo { other(){}; constructor(){} }`);
|
||||
ctor(`class Bar extends Foo { constructor(){} a(){super(...arguments)} }`);
|
||||
|
||||
noCtor(`class extends Foo{}`);
|
||||
noCtor(`class extends Foo {}`);
|
||||
|
||||
@@ -15,22 +15,33 @@ import {emailValidator, maxLengthValidator, maxValidator, minLengthValidator, mi
|
||||
/**
|
||||
* Method that updates string to integer if not already a number
|
||||
*
|
||||
* @param value The value to convert to integer
|
||||
* @returns value of parameter in number or integer.
|
||||
* @param value The value to convert to integer.
|
||||
* @returns value of parameter converted to number or integer.
|
||||
*/
|
||||
function toInteger(value: string|number): number {
|
||||
return typeof value === 'number' ? value : parseInt(value, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that converts null, false or 'false' string to boolean.
|
||||
*
|
||||
* @param value input value.
|
||||
* @returns value of parameter converted to boolean.
|
||||
*/
|
||||
function toBoolean(input: unknown): boolean {
|
||||
return input != null && input !== false && `${input}` !== 'false';
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that ensures that provided value is a float (and converts it to float if needed).
|
||||
*
|
||||
* @param value The value to convert to float
|
||||
* @returns value of parameter in number or float.
|
||||
* @param value The value to convert to float.
|
||||
* @returns value of parameter converted to number or float.
|
||||
*/
|
||||
function toFloat(value: string|number): number {
|
||||
return typeof value === 'number' ? value : parseFloat(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Defines the map of errors returned from failed validation checks.
|
||||
@@ -356,41 +367,27 @@ export const CHECKBOX_REQUIRED_VALIDATOR: StaticProvider = {
|
||||
selector:
|
||||
':not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]',
|
||||
providers: [REQUIRED_VALIDATOR],
|
||||
host: {'[attr.required]': 'required ? "" : null'}
|
||||
host: {'[attr.required]': '_enabled ? "" : null'}
|
||||
})
|
||||
export class RequiredValidator implements Validator {
|
||||
private _required = false;
|
||||
private _onChange?: () => void;
|
||||
|
||||
export class RequiredValidator extends AbstractValidatorDirective {
|
||||
/**
|
||||
* @description
|
||||
* Tracks changes to the required attribute bound to this directive.
|
||||
*/
|
||||
@Input()
|
||||
get required(): boolean|string {
|
||||
return this._required;
|
||||
}
|
||||
@Input() required!: boolean|string;
|
||||
|
||||
set required(value: boolean|string) {
|
||||
this._required = value != null && value !== false && `${value}` !== 'false';
|
||||
if (this._onChange) this._onChange();
|
||||
}
|
||||
/** @internal */
|
||||
override inputName = 'required';
|
||||
|
||||
/**
|
||||
* Method that validates whether the control is empty.
|
||||
* Returns the validation result if enabled, otherwise null.
|
||||
* @nodoc
|
||||
*/
|
||||
validate(control: AbstractControl): ValidationErrors|null {
|
||||
return this.required ? requiredValidator(control) : null;
|
||||
}
|
||||
/** @internal */
|
||||
override normalizeInput = (input: unknown): boolean => toBoolean(input);
|
||||
|
||||
/**
|
||||
* Registers a callback function to call when the validator inputs change.
|
||||
* @nodoc
|
||||
*/
|
||||
registerOnValidatorChange(fn: () => void): void {
|
||||
this._onChange = fn;
|
||||
/** @internal */
|
||||
override createValidator = (input: boolean): ValidatorFn => requiredValidator;
|
||||
|
||||
/** @nodoc */
|
||||
override enabled(input: boolean): boolean {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,17 +417,11 @@ export class RequiredValidator implements Validator {
|
||||
selector:
|
||||
'input[type=checkbox][required][formControlName],input[type=checkbox][required][formControl],input[type=checkbox][required][ngModel]',
|
||||
providers: [CHECKBOX_REQUIRED_VALIDATOR],
|
||||
host: {'[attr.required]': 'required ? "" : null'}
|
||||
host: {'[attr.required]': '_enabled ? "" : null'}
|
||||
})
|
||||
export class CheckboxRequiredValidator extends RequiredValidator {
|
||||
/**
|
||||
* Method that validates whether or not the checkbox has been checked.
|
||||
* Returns the validation result if enabled, otherwise null.
|
||||
* @nodoc
|
||||
*/
|
||||
override validate(control: AbstractControl): ValidationErrors|null {
|
||||
return this.required ? requiredTrueValidator(control) : null;
|
||||
}
|
||||
/** @internal */
|
||||
override createValidator = (input: unknown): ValidatorFn => requiredTrueValidator;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2268,6 +2268,21 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]');
|
||||
|
||||
expect(checkbox.nativeElement.checked).toBe(true);
|
||||
expect(control.hasError('required')).toEqual(false);
|
||||
|
||||
checkbox.nativeElement.required = false;
|
||||
dispatchEvent(checkbox.nativeElement, 'change');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(checkbox.nativeElement.checked).toBe(true);
|
||||
expect(control.hasError('required')).toEqual(false);
|
||||
|
||||
checkbox.nativeElement.checked = false;
|
||||
checkbox.nativeElement.required = true;
|
||||
dispatchEvent(checkbox.nativeElement, 'change');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(checkbox.nativeElement.checked).toBe(false);
|
||||
expect(control.hasError('required')).toEqual(true);
|
||||
});
|
||||
|
||||
// Note: this scenario goes against validator function rules were `null` is the only
|
||||
|
||||
@@ -1315,6 +1315,14 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||
|
||||
expect(input.nativeElement.checked).toBe(false);
|
||||
expect(control.hasError('required')).toBe(true);
|
||||
|
||||
fixture.componentInstance.required = false;
|
||||
dispatchEvent(input.nativeElement, 'change');
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(input.nativeElement.checked).toBe(false);
|
||||
expect(control.hasError('required')).toBe(false);
|
||||
}));
|
||||
|
||||
it('should validate email', fakeAsync(() => {
|
||||
|
||||
@@ -183,7 +183,7 @@ function toAttributeCssSelector(attribute: t.TextAttribute|t.BoundAttribute|t.Bo
|
||||
}
|
||||
|
||||
function getNodeName(node: t.Template|t.Element): string {
|
||||
return node instanceof t.Template ? node.tagName : node.name;
|
||||
return node instanceof t.Template ? (node.tagName ?? 'ng-template') : node.name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -384,7 +384,7 @@ describe('getSemanticDiagnostics', () => {
|
||||
expect(diags[0].category).toEqual(ts.DiagnosticCategory.Warning);
|
||||
});
|
||||
|
||||
it('should not produce invalid banana in box warning without the required flags', () => {
|
||||
it('should not produce invalid banana in box warning without `strictTemplates`', () => {
|
||||
const files = {
|
||||
'app.ts': `
|
||||
import {Component} from '@angular/core';
|
||||
@@ -397,7 +397,9 @@ describe('getSemanticDiagnostics', () => {
|
||||
}
|
||||
`
|
||||
};
|
||||
const project = createModuleAndProjectWithDeclarations(env, 'test', files);
|
||||
const project = createModuleAndProjectWithDeclarations(env, 'test', files, {
|
||||
strictTemplates: false,
|
||||
});
|
||||
|
||||
const diags = project.getDiagnosticsForFile('app.ts');
|
||||
expect(diags.length).toEqual(0);
|
||||
@@ -426,7 +428,7 @@ describe('getSemanticDiagnostics', () => {
|
||||
expect(diags[0].category).toEqual(ts.DiagnosticCategory.Warning);
|
||||
});
|
||||
|
||||
it('should not produce invalid banana in box warning in external html file without the required flags',
|
||||
it('should not produce invalid banana in box warning in external html file without `strictTemplates`',
|
||||
() => {
|
||||
const files = {
|
||||
'app.ts': `
|
||||
@@ -441,7 +443,9 @@ describe('getSemanticDiagnostics', () => {
|
||||
`,
|
||||
'app.html': `<div ([foo])="bar"></div>`
|
||||
};
|
||||
const project = createModuleAndProjectWithDeclarations(env, 'test', files);
|
||||
const project = createModuleAndProjectWithDeclarations(env, 'test', files, {
|
||||
strictTemplates: false,
|
||||
});
|
||||
|
||||
const diags = project.getDiagnosticsForFile('app.html');
|
||||
expect(diags.length).toEqual(0);
|
||||
|
||||
@@ -17,6 +17,7 @@ export const NAMESPACE_URIS: {[ns: string]: string} = {
|
||||
'xlink': 'http://www.w3.org/1999/xlink',
|
||||
'xml': 'http://www.w3.org/XML/1998/namespace',
|
||||
'xmlns': 'http://www.w3.org/2000/xmlns/',
|
||||
'math': 'http://www.w3.org/1998/MathML/',
|
||||
};
|
||||
|
||||
const COMPONENT_REGEX = /%COMP%/g;
|
||||
@@ -144,9 +145,7 @@ class DefaultDomRenderer2 implements Renderer2 {
|
||||
|
||||
createElement(name: string, namespace?: string): any {
|
||||
if (namespace) {
|
||||
// In cases where Ivy (not ViewEngine) is giving us the actual namespace, the look up by key
|
||||
// will result in undefined, so we just return the namespace here.
|
||||
return document.createElementNS(NAMESPACE_URIS[namespace] || namespace, name);
|
||||
return document.createElementNS(NAMESPACE_URIS[namespace], name);
|
||||
}
|
||||
|
||||
return document.createElement(name);
|
||||
@@ -199,8 +198,6 @@ class DefaultDomRenderer2 implements Renderer2 {
|
||||
setAttribute(el: any, name: string, value: string, namespace?: string): void {
|
||||
if (namespace) {
|
||||
name = namespace + ':' + name;
|
||||
// TODO(FW-811): Ivy may cause issues here because it's passing around
|
||||
// full URIs for namespaces, therefore this lookup will fail.
|
||||
const namespaceUri = NAMESPACE_URIS[namespace];
|
||||
if (namespaceUri) {
|
||||
el.setAttributeNS(namespaceUri, name, value);
|
||||
@@ -214,15 +211,10 @@ class DefaultDomRenderer2 implements Renderer2 {
|
||||
|
||||
removeAttribute(el: any, name: string, namespace?: string): void {
|
||||
if (namespace) {
|
||||
// TODO(FW-811): Ivy may cause issues here because it's passing around
|
||||
// full URIs for namespaces, therefore this lookup will fail.
|
||||
const namespaceUri = NAMESPACE_URIS[namespace];
|
||||
if (namespaceUri) {
|
||||
el.removeAttributeNS(namespaceUri, name);
|
||||
} else {
|
||||
// TODO(FW-811): Since ivy is passing around full URIs for namespaces
|
||||
// this could result in properties like `http://www.w3.org/2000/svg:cx="123"`,
|
||||
// which is wrong.
|
||||
el.removeAttribute(`${namespace}:${name}`);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -72,8 +72,6 @@ class DefaultServerRenderer2 implements Renderer2 {
|
||||
createElement(name: string, namespace?: string, debugInfo?: any): any {
|
||||
if (namespace) {
|
||||
const doc = this.document || getDOM().getDefaultDocument();
|
||||
// TODO(FW-811): Ivy may cause issues here because it's passing around
|
||||
// full URIs for namespaces, therefore this lookup will fail.
|
||||
return doc.createElementNS(NAMESPACE_URIS[namespace], name);
|
||||
}
|
||||
|
||||
@@ -131,8 +129,6 @@ class DefaultServerRenderer2 implements Renderer2 {
|
||||
|
||||
setAttribute(el: any, name: string, value: string, namespace?: string): void {
|
||||
if (namespace) {
|
||||
// TODO(FW-811): Ivy may cause issues here because it's passing around
|
||||
// full URIs for namespaces, therefore this lookup will fail.
|
||||
el.setAttributeNS(NAMESPACE_URIS[namespace], namespace + ':' + name, value);
|
||||
} else {
|
||||
el.setAttribute(name, value);
|
||||
@@ -141,8 +137,6 @@ class DefaultServerRenderer2 implements Renderer2 {
|
||||
|
||||
removeAttribute(el: any, name: string, namespace?: string): void {
|
||||
if (namespace) {
|
||||
// TODO(FW-811): Ivy may cause issues here because it's passing around
|
||||
// full URIs for namespaces, therefore this lookup will fail.
|
||||
el.removeAttributeNS(NAMESPACE_URIS[namespace], name);
|
||||
} else {
|
||||
el.removeAttribute(name);
|
||||
|
||||
@@ -10,7 +10,7 @@ import {animate, AnimationBuilder, state, style, transition, trigger} from '@ang
|
||||
import {DOCUMENT, isPlatformServer, PlatformLocation, ɵgetDOM as getDOM} from '@angular/common';
|
||||
import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
|
||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import {ApplicationRef, CompilerFactory, Component, destroyPlatform, getPlatform, HostListener, Inject, Injectable, Input, NgModule, NgZone, PLATFORM_ID, PlatformRef, ViewEncapsulation} from '@angular/core';
|
||||
import {ApplicationRef, CompilerFactory, Component, destroyPlatform, getPlatform, HostBinding, HostListener, Inject, Injectable, Input, NgModule, NgZone, OnInit, PLATFORM_ID, PlatformRef, ViewEncapsulation} from '@angular/core';
|
||||
import {inject, waitForAsync} from '@angular/core/testing';
|
||||
import {BrowserModule, makeStateKey, Title, TransferState} from '@angular/platform-browser';
|
||||
import {BEFORE_APP_SERIALIZED, INITIAL_CONFIG, platformDynamicServer, PlatformState, renderModule, renderModuleFactory, ServerModule, ServerTransferStateModule} from '@angular/platform-server';
|
||||
@@ -20,11 +20,16 @@ import {first} from 'rxjs/operators';
|
||||
@Component({selector: 'app', template: `Works!`})
|
||||
class MyServerApp {
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [MyServerApp],
|
||||
exports: [MyServerApp],
|
||||
})
|
||||
export class MyServerAppModule {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
bootstrap: [MyServerApp],
|
||||
declarations: [MyServerApp],
|
||||
imports: [ServerModule],
|
||||
imports: [MyServerAppModule, ServerModule],
|
||||
})
|
||||
class ExampleModule {
|
||||
}
|
||||
@@ -73,8 +78,8 @@ function asyncRejectRenderHook() {
|
||||
|
||||
@NgModule({
|
||||
bootstrap: [MyServerApp],
|
||||
declarations: [MyServerApp],
|
||||
imports: [BrowserModule.withServerTransition({appId: 'render-hook'}), ServerModule],
|
||||
imports:
|
||||
[MyServerAppModule, BrowserModule.withServerTransition({appId: 'render-hook'}), ServerModule],
|
||||
providers: [
|
||||
{provide: BEFORE_APP_SERIALIZED, useFactory: getTitleRenderHook, multi: true, deps: [DOCUMENT]},
|
||||
]
|
||||
@@ -84,8 +89,8 @@ class RenderHookModule {
|
||||
|
||||
@NgModule({
|
||||
bootstrap: [MyServerApp],
|
||||
declarations: [MyServerApp],
|
||||
imports: [BrowserModule.withServerTransition({appId: 'render-hook'}), ServerModule],
|
||||
imports:
|
||||
[MyServerAppModule, BrowserModule.withServerTransition({appId: 'render-hook'}), ServerModule],
|
||||
providers: [
|
||||
{provide: BEFORE_APP_SERIALIZED, useFactory: getTitleRenderHook, multi: true, deps: [DOCUMENT]},
|
||||
{provide: BEFORE_APP_SERIALIZED, useValue: exceptionRenderHook, multi: true},
|
||||
@@ -97,8 +102,8 @@ class MultiRenderHookModule {
|
||||
|
||||
@NgModule({
|
||||
bootstrap: [MyServerApp],
|
||||
declarations: [MyServerApp],
|
||||
imports: [BrowserModule.withServerTransition({appId: 'render-hook'}), ServerModule],
|
||||
imports:
|
||||
[MyServerAppModule, BrowserModule.withServerTransition({appId: 'render-hook'}), ServerModule],
|
||||
providers: [
|
||||
{
|
||||
provide: BEFORE_APP_SERIALIZED,
|
||||
@@ -112,8 +117,8 @@ class AsyncRenderHookModule {
|
||||
}
|
||||
@NgModule({
|
||||
bootstrap: [MyServerApp],
|
||||
declarations: [MyServerApp],
|
||||
imports: [BrowserModule.withServerTransition({appId: 'render-hook'}), ServerModule],
|
||||
imports:
|
||||
[MyServerAppModule, BrowserModule.withServerTransition({appId: 'render-hook'}), ServerModule],
|
||||
providers: [
|
||||
{provide: BEFORE_APP_SERIALIZED, useFactory: getMetaRenderHook, multi: true, deps: [DOCUMENT]},
|
||||
{
|
||||
@@ -188,7 +193,11 @@ class SVGServerModule {
|
||||
|
||||
@Component({
|
||||
selector: 'app',
|
||||
template: `<div [@myAnimation]="state">{{text}}</div>`,
|
||||
template: `
|
||||
<div [@myAnimation]="state">
|
||||
<svg *ngIf="true"></svg>
|
||||
{{text}}
|
||||
</div>`,
|
||||
animations: [trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
@@ -235,8 +244,7 @@ class ExampleStylesModule {
|
||||
|
||||
@NgModule({
|
||||
bootstrap: [MyServerApp],
|
||||
declarations: [MyServerApp],
|
||||
imports: [ServerModule, HttpClientModule, HttpClientTestingModule],
|
||||
imports: [MyServerAppModule, ServerModule, HttpClientModule, HttpClientTestingModule],
|
||||
})
|
||||
export class HttpClientExampleModule {
|
||||
}
|
||||
@@ -252,8 +260,7 @@ export class MyHttpInterceptor implements HttpInterceptor {
|
||||
|
||||
@NgModule({
|
||||
bootstrap: [MyServerApp],
|
||||
declarations: [MyServerApp],
|
||||
imports: [ServerModule, HttpClientModule, HttpClientTestingModule],
|
||||
imports: [MyServerAppModule, ServerModule, HttpClientModule, HttpClientTestingModule],
|
||||
providers: [
|
||||
{provide: HTTP_INTERCEPTORS, multi: true, useClass: MyHttpInterceptor},
|
||||
],
|
||||
|
||||
@@ -384,7 +384,7 @@ export interface Route {
|
||||
* matches a given path and stops when there is a config match. Importantly there must still be a
|
||||
* config match for each segment of the URL. For example, '/team/11/user' matches the prefix
|
||||
* 'team/:id' if one of the route's children matches the segment 'user'. That is, the URL
|
||||
* '/team/11/user` matches the config
|
||||
* '/team/11/user' matches the config
|
||||
* `{path: 'team/:id', children: [{path: ':user', component: User}]}`
|
||||
* but does not match when there are no children as in `{path: 'team/:id', component: Team}`.
|
||||
*
|
||||
|
||||
@@ -42,23 +42,23 @@
|
||||
"@angular-devkit/core" "13.1.1"
|
||||
rxjs "6.6.7"
|
||||
|
||||
"@angular-devkit/architect@0.1301.3":
|
||||
version "0.1301.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1301.3.tgz#197f92c984adf22776798ce568e64396e464a03d"
|
||||
integrity sha512-fFSevgYGZHCybYoyTkZ9b1YCSthBmoi77alwWjqMhYXUNXx7yx50zJZ6Ur2v3YpctVjU6eoGc5FDFyVHwXT0Iw==
|
||||
"@angular-devkit/architect@0.1301.4":
|
||||
version "0.1301.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1301.4.tgz#2fc51bcae0dcb581c8be401e2fde7bbd10b43076"
|
||||
integrity sha512-p6G8CEMnE+gYwxRyEttj3QGsuNJ3Kusi7iwBIzWyf2RpJSdGzXdwUEiRGg6iS0YHFr06/ZFfAWfnM2DQvNm4TA==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "13.1.3"
|
||||
"@angular-devkit/core" "13.1.4"
|
||||
rxjs "6.6.7"
|
||||
|
||||
"@angular-devkit/build-angular@13.1.3":
|
||||
version "13.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-13.1.3.tgz#c04cef8a2d405cb66332b674d204a2717b6807f6"
|
||||
integrity sha512-C5Qv8aGmpGbETG4Mawly/5LnkRwfJAzANL5BtYJn8ZaDlZKCkhvAaRXHpm4Mdqg5idACAT8hgYqPQvqyEBaVDA==
|
||||
"@angular-devkit/build-angular@13.1.4":
|
||||
version "13.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-13.1.4.tgz#942023fca240b0f5753dcf65bb67e57e4779bf86"
|
||||
integrity sha512-MTvlUCb02J4ODXDsnit4N0PR9PkpKeSYpTPueaSBuWTBeP3dvMPZQabvb3C5QT/5yUzBiXQZq11QYx3Gui4StA==
|
||||
dependencies:
|
||||
"@ampproject/remapping" "1.0.2"
|
||||
"@angular-devkit/architect" "0.1301.3"
|
||||
"@angular-devkit/build-webpack" "0.1301.3"
|
||||
"@angular-devkit/core" "13.1.3"
|
||||
"@angular-devkit/architect" "0.1301.4"
|
||||
"@angular-devkit/build-webpack" "0.1301.4"
|
||||
"@angular-devkit/core" "13.1.4"
|
||||
"@babel/core" "7.16.0"
|
||||
"@babel/generator" "7.16.0"
|
||||
"@babel/helper-annotate-as-pure" "7.16.0"
|
||||
@@ -69,7 +69,7 @@
|
||||
"@babel/runtime" "7.16.3"
|
||||
"@babel/template" "7.16.0"
|
||||
"@discoveryjs/json-ext" "0.5.6"
|
||||
"@ngtools/webpack" "13.1.3"
|
||||
"@ngtools/webpack" "13.1.4"
|
||||
ansi-colors "4.1.1"
|
||||
babel-loader "8.2.3"
|
||||
babel-plugin-istanbul "6.1.1"
|
||||
@@ -99,7 +99,7 @@
|
||||
postcss "8.4.4"
|
||||
postcss-import "14.0.2"
|
||||
postcss-loader "6.2.1"
|
||||
postcss-preset-env "6.7.0"
|
||||
postcss-preset-env "7.2.3"
|
||||
regenerator-runtime "0.13.9"
|
||||
resolve-url-loader "4.0.0"
|
||||
rxjs "6.6.7"
|
||||
@@ -194,10 +194,10 @@
|
||||
optionalDependencies:
|
||||
esbuild "0.14.2"
|
||||
|
||||
"@angular-devkit/build-optimizer@0.1301.3":
|
||||
version "0.1301.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1301.3.tgz#d4025ca89f1eff89d5d6243d85ea7ae0acebaf02"
|
||||
integrity sha512-ZLPPXDUUQWUxYeNR7NGnuMdi2IKJU4WMMg4LmwSlEJzulKO6MbjreXQsQtG1ILxXmVe/eL1v2XVaX0/AyTr9Zw==
|
||||
"@angular-devkit/build-optimizer@0.1301.4":
|
||||
version "0.1301.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1301.4.tgz#69441c53954a7bec4a992fe2c725fa668ee1fec5"
|
||||
integrity sha512-w09qgZ6oZ8TmLln4IvQuSe/664UITs+pW2PKA4Ya3N4bmnja+Sz7McoH5ifNnBw8QWUgJy9rSDj13bOoe1t7WA==
|
||||
dependencies:
|
||||
source-map "0.7.3"
|
||||
tslib "2.3.1"
|
||||
@@ -221,12 +221,12 @@
|
||||
"@angular-devkit/architect" "0.1301.1"
|
||||
rxjs "6.6.7"
|
||||
|
||||
"@angular-devkit/build-webpack@0.1301.3":
|
||||
version "0.1301.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1301.3.tgz#4f8f9fd9e09992aaf904c4457f268b203c19b45d"
|
||||
integrity sha512-FFwKdhq5n0lrqkiJRZoWKy21gERtvupkk0BpIVPTbRqyiqB2htiGM995uBBjpeDngytDLx+BwPFipVfQ+WIi9w==
|
||||
"@angular-devkit/build-webpack@0.1301.4":
|
||||
version "0.1301.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1301.4.tgz#7dd16ec7fb26c5e177ad26a6f485faf4393774ef"
|
||||
integrity sha512-IcC3Y5WhreIV0uT90ITqJVgRqFjGwH72hftwLxkslaX+FJDcL36mhsNdyN66BJiuX7R85xUxHq3PEb9cZrllJA==
|
||||
dependencies:
|
||||
"@angular-devkit/architect" "0.1301.3"
|
||||
"@angular-devkit/architect" "0.1301.4"
|
||||
rxjs "6.6.7"
|
||||
|
||||
"@angular-devkit/core@13.1.1":
|
||||
@@ -241,10 +241,10 @@
|
||||
rxjs "6.6.7"
|
||||
source-map "0.7.3"
|
||||
|
||||
"@angular-devkit/core@13.1.3":
|
||||
version "13.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-13.1.3.tgz#d1f8a6b4ad4326732a160a7549fccca1369fd108"
|
||||
integrity sha512-o14jGDk4h14dVYYQafOn+2rq9CDmDMbDV6logqKYCLzTDRlK8gccDnqJM/QKAlfWCzbllZqcHDmg6FyoRLO9RQ==
|
||||
"@angular-devkit/core@13.1.4":
|
||||
version "13.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-13.1.4.tgz#b5b6ddd674ae351f83beff2e4a0d702096bdfd47"
|
||||
integrity sha512-225Gjy4iVxh5Jo9njJnaG75M/Dt95UW+dEPCGWKV5E/++7UUlXlo9sNWq8x2vJm2nhtsPkpnXNOt4pW1mIDwqQ==
|
||||
dependencies:
|
||||
ajv "8.8.2"
|
||||
ajv-formats "2.1.1"
|
||||
@@ -253,12 +253,12 @@
|
||||
rxjs "6.6.7"
|
||||
source-map "0.7.3"
|
||||
|
||||
"@angular-devkit/schematics@13.1.3":
|
||||
version "13.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-13.1.3.tgz#2328f9cf63d6f556392d96b73af794fc52bfc87f"
|
||||
integrity sha512-TvjThB/pFXNFM0j0WX5yg0L2/3xNsqawQuWhkDJ05MBDEnSxbgv5hmOzNL8SNIEMgP0VbSTHtSg5kZvmNiH7vg==
|
||||
"@angular-devkit/schematics@13.1.4":
|
||||
version "13.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-13.1.4.tgz#e8ed817887aa51268dec27d8d6188e2f3f10742a"
|
||||
integrity sha512-yBa7IeC4cLZ7s137NAQD+sMB5c6SI6UJ6xYxl6J/CvV2RLGOZZA93i4GuRALi5s82eLi1fH+HEL/gvf3JQynzQ==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "13.1.3"
|
||||
"@angular-devkit/core" "13.1.4"
|
||||
jsonc-parser "3.0.0"
|
||||
magic-string "0.25.7"
|
||||
ora "5.4.1"
|
||||
@@ -279,15 +279,15 @@
|
||||
"@angular/core" "^10.0.0-0 || ^11.0.0"
|
||||
reflect-metadata "^0.1.13"
|
||||
|
||||
"@angular/cli@13.1.3":
|
||||
version "13.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-13.1.3.tgz#d143f30ee67481cc315e0d18fecb076101dfa280"
|
||||
integrity sha512-Ju/A8LFnfcv1PC665a5FiIQx9SXqB+3yWYFXPIiVkkRcye95gpfsbV48WW7QV35gzIwbR1m3H907Zg6ptiNv0A==
|
||||
"@angular/cli@13.1.4":
|
||||
version "13.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-13.1.4.tgz#34e6e87d1c6950408167c41293cf2cd5d1e00a2e"
|
||||
integrity sha512-PP9xpvDDCHhLTIZjewQQzzf+JbpF2s5mXTW2AgIL/E53ukaVvXwwjFMt9dQvECwut/LDpThoc3OfqcGrmwtqnA==
|
||||
dependencies:
|
||||
"@angular-devkit/architect" "0.1301.3"
|
||||
"@angular-devkit/core" "13.1.3"
|
||||
"@angular-devkit/schematics" "13.1.3"
|
||||
"@schematics/angular" "13.1.3"
|
||||
"@angular-devkit/architect" "0.1301.4"
|
||||
"@angular-devkit/core" "13.1.4"
|
||||
"@angular-devkit/schematics" "13.1.4"
|
||||
"@schematics/angular" "13.1.4"
|
||||
"@yarnpkg/lockfile" "1.1.0"
|
||||
ansi-colors "4.1.1"
|
||||
debug "4.3.3"
|
||||
@@ -1529,10 +1529,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-13.1.1.tgz#736988cda569b9b1eb925908ebe9ccf3faa095eb"
|
||||
integrity sha512-TGCuscGFNHPoXScswuj9UxNv8E+A/PXs5XH8cyTtbkmlpBec1EShnaUTdDP5nhykjjaWzd3TLChlp9DinE+Ctg==
|
||||
|
||||
"@ngtools/webpack@13.1.3":
|
||||
version "13.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-13.1.3.tgz#f3e516da2b2a352db9d723e8cebbe15b526de14c"
|
||||
integrity sha512-6Pf52IbChm/dFuegfv0smeBTW2moi0Gdkyjgk/7VWqE6hN35m+YGrCh+XnPp1POJwOKxhAByhV9zs6NWxrK1vA==
|
||||
"@ngtools/webpack@13.1.4":
|
||||
version "13.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-13.1.4.tgz#359c9078af281413cf9cabd0af06b3fca4ddbf7e"
|
||||
integrity sha512-s8gzjG2nYHawFhlkHMkQWYrocHkBI1nF6T9K/oCSTIsq6kvTv//Ahno2VdBSgVq8uMnpv1TymvX0nFC4Dgn1HA==
|
||||
|
||||
"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3":
|
||||
version "2.1.8-no-fsevents.3"
|
||||
@@ -1926,13 +1926,13 @@
|
||||
colors "~1.2.1"
|
||||
string-argv "~0.3.1"
|
||||
|
||||
"@schematics/angular@13.1.3":
|
||||
version "13.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-13.1.3.tgz#c763cdf1a2e0784d5263c23b581bfeb4a4a2f12e"
|
||||
integrity sha512-IixVWAEtN97N74PCxg3T03Ar/ECjGyJBWKAjKTTCrgNSWhm2mKgIc4RyI6cVCnltfJWIo48fcFhlOx/elShaCg==
|
||||
"@schematics/angular@13.1.4":
|
||||
version "13.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-13.1.4.tgz#8b8c9ad40403c485bae9adeb51649711651189d2"
|
||||
integrity sha512-P1YsHn1LLAmdpB9X2TBuUgrvEW/KaoBbHr8ifYO8/uQEXyeiIF+So8h/dnegkYkdsr3OwQ2X/j3UF6/+HS0odg==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "13.1.3"
|
||||
"@angular-devkit/schematics" "13.1.3"
|
||||
"@angular-devkit/core" "13.1.4"
|
||||
"@angular-devkit/schematics" "13.1.4"
|
||||
jsonc-parser "3.0.0"
|
||||
|
||||
"@sindresorhus/is@^0.14.0":
|
||||
@@ -3243,6 +3243,18 @@ atob@^2.1.2:
|
||||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
||||
|
||||
autoprefixer@^10.4.2:
|
||||
version "10.4.2"
|
||||
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.2.tgz#25e1df09a31a9fba5c40b578936b90d35c9d4d3b"
|
||||
integrity sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ==
|
||||
dependencies:
|
||||
browserslist "^4.19.1"
|
||||
caniuse-lite "^1.0.30001297"
|
||||
fraction.js "^4.1.2"
|
||||
normalize-range "^0.1.2"
|
||||
picocolors "^1.0.0"
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
autoprefixer@^9.6.1:
|
||||
version "9.8.8"
|
||||
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.8.tgz#fd4bd4595385fa6f06599de749a4d5f7a474957a"
|
||||
@@ -3600,6 +3612,17 @@ browserslist@^4.18.1:
|
||||
node-releases "^2.0.1"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
browserslist@^4.19.1:
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3"
|
||||
integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==
|
||||
dependencies:
|
||||
caniuse-lite "^1.0.30001286"
|
||||
electron-to-chromium "^1.4.17"
|
||||
escalade "^3.1.1"
|
||||
node-releases "^2.0.1"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
browserstack@^1.5.1:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/browserstack/-/browserstack-1.6.1.tgz#e051f9733ec3b507659f395c7a4765a1b1e358b3"
|
||||
@@ -3860,6 +3883,11 @@ caniuse-lite@^1.0.30001280:
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz#3e9debad420419618cfdf52dc9b6572b28a8fff6"
|
||||
integrity sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ==
|
||||
|
||||
caniuse-lite@^1.0.30001286, caniuse-lite@^1.0.30001297, caniuse-lite@^1.0.30001299:
|
||||
version "1.0.30001300"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001300.tgz#11ab6c57d3eb6f964cba950401fd00a146786468"
|
||||
integrity sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA==
|
||||
|
||||
canonical-path@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-1.0.0.tgz#fcb470c23958def85081856be7a86e904f180d1d"
|
||||
@@ -4824,6 +4852,13 @@ css-blank-pseudo@^0.1.4:
|
||||
dependencies:
|
||||
postcss "^7.0.5"
|
||||
|
||||
css-blank-pseudo@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-3.0.2.tgz#f8660f6a48b17888a9277e53f25cc5abec1f0169"
|
||||
integrity sha512-hOb1LFjRR+8ocA071xUSmg5VslJ8NGo/I2qpUpdeAYyBVCgupS5O8SEVo4SxEMYyFBNodBkzG3T1iqW9HCXxew==
|
||||
dependencies:
|
||||
postcss-selector-parser "^6.0.8"
|
||||
|
||||
css-has-pseudo@^0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz#3c642ab34ca242c59c41a125df9105841f6966ee"
|
||||
@@ -4832,6 +4867,13 @@ css-has-pseudo@^0.10.0:
|
||||
postcss "^7.0.6"
|
||||
postcss-selector-parser "^5.0.0-rc.4"
|
||||
|
||||
css-has-pseudo@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-3.0.3.tgz#4824a34cb92dae7e09ea1d3fd19691b653412098"
|
||||
integrity sha512-0gDYWEKaGacwxCqvQ3Ypg6wGdD1AztbMm5h1JsactG2hP2eiflj808QITmuWBpE7sjSEVrAlZhPTVd/nNMj/hQ==
|
||||
dependencies:
|
||||
postcss-selector-parser "^6.0.8"
|
||||
|
||||
css-loader@6.5.1:
|
||||
version "6.5.1"
|
||||
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.5.1.tgz#0c43d4fbe0d97f699c91e9818cb585759091d1b1"
|
||||
@@ -4853,6 +4895,11 @@ css-prefers-color-scheme@^3.1.1:
|
||||
dependencies:
|
||||
postcss "^7.0.5"
|
||||
|
||||
css-prefers-color-scheme@^6.0.2:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.2.tgz#d5c03a980caab92d8beeee176a8795d331e0c727"
|
||||
integrity sha512-gv0KQBEM+q/XdoKyznovq3KW7ocO7k+FhPP+hQR1MenJdu0uPGS6IZa9PzlbqBeS6XcZJNAoqoFxlAUW461CrA==
|
||||
|
||||
css-select@^4.1.3:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067"
|
||||
@@ -4904,6 +4951,11 @@ cssdb@^4.4.0:
|
||||
resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-4.4.0.tgz#3bf2f2a68c10f5c6a08abd92378331ee803cddb0"
|
||||
integrity sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==
|
||||
|
||||
cssdb@^5.0.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-5.1.0.tgz#ec728d5f5c0811debd0820cbebda505d43003400"
|
||||
integrity sha512-/vqjXhv1x9eGkE/zO6o8ZOI7dgdZbLVLUGyVRbPgk6YipXbW87YzUCcO+Jrmi5bwJlAH6oD+MNeZyRgXea1GZw==
|
||||
|
||||
cssesc@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703"
|
||||
@@ -5631,6 +5683,11 @@ electron-to-chromium@^1.3.896:
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.16.tgz#38ddecc616385e6f101359d1b978c802664157d2"
|
||||
integrity sha512-BQb7FgYwnu6haWLU63/CdVW+9xhmHls3RCQUFiV4lvw3wimEHTVcUk2hkuZo76QhR8nnDdfZE7evJIZqijwPdA==
|
||||
|
||||
electron-to-chromium@^1.4.17:
|
||||
version "1.4.49"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.49.tgz#5b6a3dc032590beef4be485a4b0b3fe7d0e3dfd7"
|
||||
integrity sha512-k/0t1TRfonHIp8TJKfjBu2cKj8MqYTiEpOhci+q7CVEE5xnCQnx1pTa+V8b/sdhe4S3PR4p4iceEQWhGrKQORQ==
|
||||
|
||||
emoji-regex@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||
@@ -6797,6 +6854,11 @@ forwarded@0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
|
||||
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
|
||||
|
||||
fraction.js@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.1.2.tgz#13e420a92422b6cf244dff8690ed89401029fbe8"
|
||||
integrity sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==
|
||||
|
||||
fragment-cache@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
|
||||
@@ -11183,6 +11245,13 @@ postcss-attribute-case-insensitive@^4.0.1:
|
||||
postcss "^7.0.2"
|
||||
postcss-selector-parser "^6.0.2"
|
||||
|
||||
postcss-attribute-case-insensitive@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.0.tgz#39cbf6babf3ded1e4abf37d09d6eda21c644105c"
|
||||
integrity sha512-b4g9eagFGq9T5SWX4+USfVyjIb3liPnjhHHRMP7FMB2kFVpYyfEscV0wP3eaXhKlcHKUut8lt5BGoeylWA/dBQ==
|
||||
dependencies:
|
||||
postcss-selector-parser "^6.0.2"
|
||||
|
||||
postcss-color-functional-notation@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz#5efd37a88fbabeb00a2966d1e53d98ced93f74e0"
|
||||
@@ -11191,6 +11260,13 @@ postcss-color-functional-notation@^2.0.1:
|
||||
postcss "^7.0.2"
|
||||
postcss-values-parser "^2.0.0"
|
||||
|
||||
postcss-color-functional-notation@^4.2.1:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.1.tgz#a25e9e1855e14d04319222a689f120b3240d39e0"
|
||||
integrity sha512-62OBIXCjRXpQZcFOYIXwXBlpAVWrYk8ek1rcjvMING4Q2cf0ipyN9qT+BhHA6HmftGSEnFQu2qgKO3gMscl3Rw==
|
||||
dependencies:
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
postcss-color-gray@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz#532a31eb909f8da898ceffe296fdc1f864be8547"
|
||||
@@ -11208,6 +11284,13 @@ postcss-color-hex-alpha@^5.0.3:
|
||||
postcss "^7.0.14"
|
||||
postcss-values-parser "^2.0.1"
|
||||
|
||||
postcss-color-hex-alpha@^8.0.2:
|
||||
version "8.0.2"
|
||||
resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.2.tgz#7a248b006dd47bd83063f662352d31fd982f74ec"
|
||||
integrity sha512-gyx8RgqSmGVK156NAdKcsfkY3KPGHhKqvHTL3hhveFrBBToguKFzhyiuk3cljH6L4fJ0Kv+JENuPXs1Wij27Zw==
|
||||
dependencies:
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
postcss-color-mod-function@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz#816ba145ac11cc3cb6baa905a75a49f903e4d31d"
|
||||
@@ -11225,6 +11308,13 @@ postcss-color-rebeccapurple@^4.0.1:
|
||||
postcss "^7.0.2"
|
||||
postcss-values-parser "^2.0.0"
|
||||
|
||||
postcss-color-rebeccapurple@^7.0.2:
|
||||
version "7.0.2"
|
||||
resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.0.2.tgz#5d397039424a58a9ca628762eb0b88a61a66e079"
|
||||
integrity sha512-SFc3MaocHaQ6k3oZaFwH8io6MdypkUtEy/eXzXEB1vEQlO3S3oDc/FSZA8AsS04Z25RirQhlDlHLh3dn7XewWw==
|
||||
dependencies:
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
postcss-custom-media@^7.0.8:
|
||||
version "7.0.8"
|
||||
resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz#fffd13ffeffad73621be5f387076a28b00294e0c"
|
||||
@@ -11232,6 +11322,18 @@ postcss-custom-media@^7.0.8:
|
||||
dependencies:
|
||||
postcss "^7.0.14"
|
||||
|
||||
postcss-custom-media@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz#1be6aff8be7dc9bf1fe014bde3b71b92bb4552f1"
|
||||
integrity sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==
|
||||
|
||||
postcss-custom-properties@^12.1.2:
|
||||
version "12.1.3"
|
||||
resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-12.1.3.tgz#8e37651c7188e72e6762eeae8db39755e84d3a64"
|
||||
integrity sha512-rtu3otIeY532PnEuuBrIIe+N+pcdbX/7JMZfrcL09wc78YayrHw5E8UkDfvnlOhEUrI4ptCuzXQfj+Or6spbGA==
|
||||
dependencies:
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
postcss-custom-properties@^8.0.11:
|
||||
version "8.0.11"
|
||||
resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz#2d61772d6e92f22f5e0d52602df8fae46fa30d97"
|
||||
@@ -11248,6 +11350,13 @@ postcss-custom-selectors@^5.1.2:
|
||||
postcss "^7.0.2"
|
||||
postcss-selector-parser "^5.0.0-rc.3"
|
||||
|
||||
postcss-custom-selectors@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-6.0.0.tgz#022839e41fbf71c47ae6e316cb0e6213012df5ef"
|
||||
integrity sha512-/1iyBhz/W8jUepjGyu7V1OPcGbc636snN1yXEQCinb6Bwt7KxsiU7/bLQlp8GwAXzCh7cobBU5odNn/2zQWR8Q==
|
||||
dependencies:
|
||||
postcss-selector-parser "^6.0.4"
|
||||
|
||||
postcss-dir-pseudo-class@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz#6e3a4177d0edb3abcc85fdb6fbb1c26dabaeaba2"
|
||||
@@ -11256,6 +11365,13 @@ postcss-dir-pseudo-class@^5.0.0:
|
||||
postcss "^7.0.2"
|
||||
postcss-selector-parser "^5.0.0-rc.3"
|
||||
|
||||
postcss-dir-pseudo-class@^6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.3.tgz#febfe305e75267913a53bf5094c7679f5cfa9b55"
|
||||
integrity sha512-qiPm+CNAlgXiMf0J5IbBBEXA9l/Q5HGsNGkL3znIwT2ZFRLGY9U2fTUpa4lqCUXQOxaLimpacHeQC80BD2qbDw==
|
||||
dependencies:
|
||||
postcss-selector-parser "^6.0.8"
|
||||
|
||||
postcss-double-position-gradients@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz#fc927d52fddc896cb3a2812ebc5df147e110522e"
|
||||
@@ -11264,6 +11380,13 @@ postcss-double-position-gradients@^1.0.0:
|
||||
postcss "^7.0.5"
|
||||
postcss-values-parser "^2.0.0"
|
||||
|
||||
postcss-double-position-gradients@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-3.0.4.tgz#2484b9785ef3ba81b0f03a279c52ec58fc5344c2"
|
||||
integrity sha512-qz+s5vhKJlsHw8HjSs+HVk2QGFdRyC68KGRQGX3i+GcnUjhWhXQEmCXW6siOJkZ1giu0ddPwSO6I6JdVVVPoog==
|
||||
dependencies:
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
postcss-env-function@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/postcss-env-function/-/postcss-env-function-2.0.2.tgz#0f3e3d3c57f094a92c2baf4b6241f0b0da5365d7"
|
||||
@@ -11272,6 +11395,13 @@ postcss-env-function@^2.0.2:
|
||||
postcss "^7.0.2"
|
||||
postcss-values-parser "^2.0.0"
|
||||
|
||||
postcss-env-function@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/postcss-env-function/-/postcss-env-function-4.0.4.tgz#4e85359ca4fcdde4ec4b73752a41de818dbe91cc"
|
||||
integrity sha512-0ltahRTPtXSIlEZFv7zIvdEib7HN0ZbUQxrxIKn8KbiRyhALo854I/CggU5lyZe6ZBvSTJ6Al2vkZecI2OhneQ==
|
||||
dependencies:
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
postcss-focus-visible@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz#477d107113ade6024b14128317ade2bd1e17046e"
|
||||
@@ -11279,6 +11409,13 @@ postcss-focus-visible@^4.0.0:
|
||||
dependencies:
|
||||
postcss "^7.0.2"
|
||||
|
||||
postcss-focus-visible@^6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/postcss-focus-visible/-/postcss-focus-visible-6.0.3.tgz#14635b71a6b9140f488f11f26cbc9965a13f6843"
|
||||
integrity sha512-ozOsg+L1U8S+rxSHnJJiET6dNLyADcPHhEarhhtCI9DBLGOPG/2i4ddVoFch9LzrBgb8uDaaRI4nuid2OM82ZA==
|
||||
dependencies:
|
||||
postcss-selector-parser "^6.0.8"
|
||||
|
||||
postcss-focus-within@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz#763b8788596cee9b874c999201cdde80659ef680"
|
||||
@@ -11286,6 +11423,13 @@ postcss-focus-within@^3.0.0:
|
||||
dependencies:
|
||||
postcss "^7.0.2"
|
||||
|
||||
postcss-focus-within@^5.0.3:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/postcss-focus-within/-/postcss-focus-within-5.0.3.tgz#0b0bf425f14a646bbfd973b463e2d20d85a3a841"
|
||||
integrity sha512-fk9y2uFS6/Kpp7/A9Hz9Z4rlFQ8+tzgBcQCXAFSrXFGAbKx+4ZZOmmfHuYjCOMegPWoz0pnC6fNzi8j7Xyqp5Q==
|
||||
dependencies:
|
||||
postcss-selector-parser "^6.0.8"
|
||||
|
||||
postcss-font-variant@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz#42d4c0ab30894f60f98b17561eb5c0321f502641"
|
||||
@@ -11293,6 +11437,11 @@ postcss-font-variant@^4.0.0:
|
||||
dependencies:
|
||||
postcss "^7.0.2"
|
||||
|
||||
postcss-font-variant@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz#efd59b4b7ea8bb06127f2d031bfbb7f24d32fa66"
|
||||
integrity sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==
|
||||
|
||||
postcss-gap-properties@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz#431c192ab3ed96a3c3d09f2ff615960f902c1715"
|
||||
@@ -11300,6 +11449,11 @@ postcss-gap-properties@^2.0.0:
|
||||
dependencies:
|
||||
postcss "^7.0.2"
|
||||
|
||||
postcss-gap-properties@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-3.0.2.tgz#562fbf43a6a721565b3ca0e01008690991d2f726"
|
||||
integrity sha512-EaMy/pbxtQnKDsnbEjdqlkCkROTQZzolcLKgIE+3b7EuJfJydH55cZeHfm+MtIezXRqhR80VKgaztO/vHq94Fw==
|
||||
|
||||
postcss-image-set-function@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz#28920a2f29945bed4c3198d7df6496d410d3f288"
|
||||
@@ -11308,6 +11462,13 @@ postcss-image-set-function@^3.0.1:
|
||||
postcss "^7.0.2"
|
||||
postcss-values-parser "^2.0.0"
|
||||
|
||||
postcss-image-set-function@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/postcss-image-set-function/-/postcss-image-set-function-4.0.4.tgz#ce91579ab2c1386d412ff5cd5e733c474b1f75ee"
|
||||
integrity sha512-BlEo9gSTj66lXjRNByvkMK9dEdEGFXRfGjKRi9fo8s0/P3oEk74cAoonl/utiM50E2OPVb/XSu+lWvdW4KtE/Q==
|
||||
dependencies:
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
postcss-import@14.0.2:
|
||||
version "14.0.2"
|
||||
resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.0.2.tgz#60eff77e6be92e7b67fe469ec797d9424cae1aa1"
|
||||
@@ -11324,6 +11485,11 @@ postcss-initial@^3.0.0:
|
||||
dependencies:
|
||||
postcss "^7.0.2"
|
||||
|
||||
postcss-initial@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-4.0.1.tgz#529f735f72c5724a0fb30527df6fb7ac54d7de42"
|
||||
integrity sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==
|
||||
|
||||
postcss-lab-function@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz#bb51a6856cd12289ab4ae20db1e3821ef13d7d2e"
|
||||
@@ -11333,6 +11499,13 @@ postcss-lab-function@^2.0.1:
|
||||
postcss "^7.0.2"
|
||||
postcss-values-parser "^2.0.0"
|
||||
|
||||
postcss-lab-function@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-4.0.3.tgz#633745b324afbcd5881da85fe2cef58b17487536"
|
||||
integrity sha512-MH4tymWmefdZQ7uVG/4icfLjAQmH6o2NRYyVh2mKoB4RXJp9PjsyhZwhH4ouaCQHvg+qJVj3RzeAR1EQpIlXZA==
|
||||
dependencies:
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
postcss-loader@6.2.1:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-6.2.1.tgz#0895f7346b1702103d30fdc66e4d494a93c008ef"
|
||||
@@ -11349,6 +11522,11 @@ postcss-logical@^3.0.0:
|
||||
dependencies:
|
||||
postcss "^7.0.2"
|
||||
|
||||
postcss-logical@^5.0.3:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-5.0.3.tgz#9934e0fb16af70adbd94217b24d2f315ceb5c2f0"
|
||||
integrity sha512-P5NcHWYrif0vK8rgOy/T87vg0WRIj3HSknrvp1wzDbiBeoDPVmiVRmkown2eSQdpPveat/MC1ess5uhzZFVnqQ==
|
||||
|
||||
postcss-media-minmax@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz#b75bb6cbc217c8ac49433e12f22048814a4f5ed5"
|
||||
@@ -11356,6 +11534,11 @@ postcss-media-minmax@^4.0.0:
|
||||
dependencies:
|
||||
postcss "^7.0.2"
|
||||
|
||||
postcss-media-minmax@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz#7140bddec173e2d6d657edbd8554a55794e2a5b5"
|
||||
integrity sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==
|
||||
|
||||
postcss-modules-extract-imports@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d"
|
||||
@@ -11384,6 +11567,13 @@ postcss-modules-values@^4.0.0:
|
||||
dependencies:
|
||||
icss-utils "^5.0.0"
|
||||
|
||||
postcss-nesting@^10.1.2:
|
||||
version "10.1.2"
|
||||
resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-10.1.2.tgz#2e5f811b3d75602ea18a95dd445bde5297145141"
|
||||
integrity sha512-dJGmgmsvpzKoVMtDMQQG/T6FSqs6kDtUDirIfl4KnjMCiY9/ETX8jdKyCd20swSRAbUYkaBKV20pxkzxoOXLqQ==
|
||||
dependencies:
|
||||
postcss-selector-parser "^6.0.8"
|
||||
|
||||
postcss-nesting@^7.0.0:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-7.0.1.tgz#b50ad7b7f0173e5b5e3880c3501344703e04c052"
|
||||
@@ -11398,6 +11588,11 @@ postcss-overflow-shorthand@^2.0.0:
|
||||
dependencies:
|
||||
postcss "^7.0.2"
|
||||
|
||||
postcss-overflow-shorthand@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.2.tgz#b4e9c89728cd1e4918173dfb95936b75f78d4148"
|
||||
integrity sha512-odBMVt6PTX7jOE9UNvmnLrFzA9pXS44Jd5shFGGtSHY80QCuJF+14McSy0iavZggRZ9Oj//C9vOKQmexvyEJMg==
|
||||
|
||||
postcss-page-break@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-page-break/-/postcss-page-break-2.0.0.tgz#add52d0e0a528cabe6afee8b46e2abb277df46bf"
|
||||
@@ -11405,6 +11600,11 @@ postcss-page-break@^2.0.0:
|
||||
dependencies:
|
||||
postcss "^7.0.2"
|
||||
|
||||
postcss-page-break@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/postcss-page-break/-/postcss-page-break-3.0.4.tgz#7fbf741c233621622b68d435babfb70dd8c1ee5f"
|
||||
integrity sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==
|
||||
|
||||
postcss-place@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/postcss-place/-/postcss-place-4.0.1.tgz#e9f39d33d2dc584e46ee1db45adb77ca9d1dcc62"
|
||||
@@ -11413,6 +11613,13 @@ postcss-place@^4.0.1:
|
||||
postcss "^7.0.2"
|
||||
postcss-values-parser "^2.0.0"
|
||||
|
||||
postcss-place@^7.0.3:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/postcss-place/-/postcss-place-7.0.3.tgz#ca8040dfd937c7769a233a3bd6e66e139cf89e62"
|
||||
integrity sha512-tDQ3m+GYoOar+KoQgj+pwPAvGHAp/Sby6vrFiyrELrMKQJ4AejL0NcS0mm296OKKYA2SRg9ism/hlT/OLhBrdQ==
|
||||
dependencies:
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
postcss-preset-env@6.7.0:
|
||||
version "6.7.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz#c34ddacf8f902383b35ad1e030f178f4cdf118a5"
|
||||
@@ -11456,6 +11663,45 @@ postcss-preset-env@6.7.0:
|
||||
postcss-selector-matches "^4.0.0"
|
||||
postcss-selector-not "^4.0.0"
|
||||
|
||||
postcss-preset-env@7.2.3:
|
||||
version "7.2.3"
|
||||
resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-7.2.3.tgz#01b9b6eea0ff16c27a3d514f10105d56363428a6"
|
||||
integrity sha512-Ok0DhLfwrcNGrBn8sNdy1uZqWRk/9FId0GiQ39W4ILop5GHtjJs8bu1MY9isPwHInpVEPWjb4CEcEaSbBLpfwA==
|
||||
dependencies:
|
||||
autoprefixer "^10.4.2"
|
||||
browserslist "^4.19.1"
|
||||
caniuse-lite "^1.0.30001299"
|
||||
css-blank-pseudo "^3.0.2"
|
||||
css-has-pseudo "^3.0.3"
|
||||
css-prefers-color-scheme "^6.0.2"
|
||||
cssdb "^5.0.0"
|
||||
postcss-attribute-case-insensitive "^5.0.0"
|
||||
postcss-color-functional-notation "^4.2.1"
|
||||
postcss-color-hex-alpha "^8.0.2"
|
||||
postcss-color-rebeccapurple "^7.0.2"
|
||||
postcss-custom-media "^8.0.0"
|
||||
postcss-custom-properties "^12.1.2"
|
||||
postcss-custom-selectors "^6.0.0"
|
||||
postcss-dir-pseudo-class "^6.0.3"
|
||||
postcss-double-position-gradients "^3.0.4"
|
||||
postcss-env-function "^4.0.4"
|
||||
postcss-focus-visible "^6.0.3"
|
||||
postcss-focus-within "^5.0.3"
|
||||
postcss-font-variant "^5.0.0"
|
||||
postcss-gap-properties "^3.0.2"
|
||||
postcss-image-set-function "^4.0.4"
|
||||
postcss-initial "^4.0.1"
|
||||
postcss-lab-function "^4.0.3"
|
||||
postcss-logical "^5.0.3"
|
||||
postcss-media-minmax "^5.0.0"
|
||||
postcss-nesting "^10.1.2"
|
||||
postcss-overflow-shorthand "^3.0.2"
|
||||
postcss-page-break "^3.0.4"
|
||||
postcss-place "^7.0.3"
|
||||
postcss-pseudo-class-any-link "^7.0.2"
|
||||
postcss-replace-overflow-wrap "^4.0.0"
|
||||
postcss-selector-not "^5.0.0"
|
||||
|
||||
postcss-pseudo-class-any-link@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz#2ed3eed393b3702879dec4a87032b210daeb04d1"
|
||||
@@ -11464,6 +11710,13 @@ postcss-pseudo-class-any-link@^6.0.0:
|
||||
postcss "^7.0.2"
|
||||
postcss-selector-parser "^5.0.0-rc.3"
|
||||
|
||||
postcss-pseudo-class-any-link@^7.0.2:
|
||||
version "7.0.2"
|
||||
resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.0.2.tgz#6284c2f970715c78fe992d2fac1130e9991585c9"
|
||||
integrity sha512-CG35J1COUH7OOBgpw5O+0koOLUd5N4vUGKUqSAuIe4GiuLHWU96Pqp+UPC8QITTd12zYAFx76pV7qWT/0Aj/TA==
|
||||
dependencies:
|
||||
postcss-selector-parser "^6.0.8"
|
||||
|
||||
postcss-replace-overflow-wrap@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz#61b360ffdaedca84c7c918d2b0f0d0ea559ab01c"
|
||||
@@ -11471,6 +11724,11 @@ postcss-replace-overflow-wrap@^3.0.0:
|
||||
dependencies:
|
||||
postcss "^7.0.2"
|
||||
|
||||
postcss-replace-overflow-wrap@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz#d2df6bed10b477bf9c52fab28c568b4b29ca4319"
|
||||
integrity sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==
|
||||
|
||||
postcss-selector-matches@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz#71c8248f917ba2cc93037c9637ee09c64436fcff"
|
||||
@@ -11487,6 +11745,13 @@ postcss-selector-not@^4.0.0:
|
||||
balanced-match "^1.0.0"
|
||||
postcss "^7.0.2"
|
||||
|
||||
postcss-selector-not@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-5.0.0.tgz#ac5fc506f7565dd872f82f5314c0f81a05630dc7"
|
||||
integrity sha512-/2K3A4TCP9orP4TNS7u3tGdRFVKqz/E6pX3aGnriPG0jU78of8wsUcqE4QAhWEU0d+WnMSF93Ah3F//vUtK+iQ==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
|
||||
postcss-selector-parser@^5.0.0-rc.3, postcss-selector-parser@^5.0.0-rc.4:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c"
|
||||
@@ -11504,11 +11769,24 @@ postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4:
|
||||
cssesc "^3.0.0"
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
postcss-selector-parser@^6.0.8:
|
||||
version "6.0.8"
|
||||
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.8.tgz#f023ed7a9ea736cd7ef70342996e8e78645a7914"
|
||||
integrity sha512-D5PG53d209Z1Uhcc0qAZ5U3t5HagH3cxu+WLZ22jt3gLUpXM4eXXfiO14jiDWST3NNooX/E8wISfOhZ9eIjGTQ==
|
||||
dependencies:
|
||||
cssesc "^3.0.0"
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
|
||||
integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
|
||||
|
||||
postcss-value-parser@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
|
||||
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
||||
|
||||
postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz#da8b472d901da1e205b47bdc98637b9e9e550e5f"
|
||||
@@ -12532,10 +12810,10 @@ rollup@~1.11.3:
|
||||
"@types/node" "^11.13.9"
|
||||
acorn "^6.1.1"
|
||||
|
||||
rollup@~2.63.0:
|
||||
version "2.63.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.63.0.tgz#fe2f7fec2133f3fab9e022b9ac245628d817c6bb"
|
||||
integrity sha512-nps0idjmD+NXl6OREfyYXMn/dar3WGcyKn+KBzPdaLecub3x/LrId0wUcthcr8oZUAcZAR8NKcfGGFlNgGL1kQ==
|
||||
rollup@~2.65.0:
|
||||
version "2.65.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.65.0.tgz#936ed140ac28f87aff22c6ac950b3d0303fadc3b"
|
||||
integrity sha512-ohZVYrhtVMTqqeqH26sngfMiyGDg6gCUReOsoflXvYpzUkDHp8sVG8F9FQxjs72OfnLWpXP2nNNqQ9I0vkRovA==
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user