Compare commits

...

42 Commits

Author SHA1 Message Date
Andrew Kushnir 59680c2644 release: cut the v11.1.0 release 2021-01-20 18:30:35 +00:00
twerske 34092a5052 docs: add embedded videos to error guides (#40453)
add embedded youtube videos
provide visual debugging for top errors

PR Close #40453
2021-01-20 08:47:03 -08:00
Keen Yee Liau b8e47e277e fix(language-service): Paths on Windows should be normalized (#40492)
Many `ts.LanguageService` APIs accept a filename, for example
```ts
getQuickInfoAtPosition(fileName: string, position: number)
```
The requirement is that `fileName` is agnostic to the platform (Linux, Mac,
Windows, etc), and is always normalized to TypeScript's internal
`NormalizedPath`.

This is evident from the way these APIs are called from the language server:
```ts
  private onHover(params: lsp.TextDocumentPositionParams) {
    const lsInfo = this.getLSAndScriptInfo(params.textDocument);
    if (lsInfo === undefined) {
      return;
    }
    const {languageService, scriptInfo} = lsInfo;
    const offset = lspPositionToTsPosition(scriptInfo, params.position);
    const info = languageService.getQuickInfoAtPosition(scriptInfo.fileName, offset);
    // ...
  }
```
https://github.com/angular/vscode-ng-language-service/blob/9fca9c66510974c26d5c21b31adb9fa39ac0a38a/server/src/session.ts#L594
Here `scriptInfo.fileName` is always a `ts.server.NormalizedPath`.

However, https://github.com/angular/angular/pull/39917 accidentally leaked
the platform-specific paths, and caused a mismatch between the incoming paths
and the paths stored in the internal data structure `fileToComponent`.

This PR fixes the bug by always normalizing the paths, and updating the
type to reflect the format of the underlying data.

Fixes https://github.com/angular/vscode-ng-language-service/issues/1063

PR Close #40492
2021-01-20 08:46:03 -08:00
David Shevitz 2731a4b633 docs: Return SVG topic to documentation; light edits for clarity (#40487)
PR Close #40487
2021-01-19 14:18:57 -08:00
Andrew Scott 3e134e441f fix(language-service): fix go to definition for template variables and references (#40455) (#40491)
The current "go to definition" is broken for template variables and
references when a template is overridden. This is because we get the
file url from the source span, which uses the overridden name
'override.html'. Instead, we can retrieve the template file from the
compiler in the same manner that is done for references.

Another way to fix this would have been to use the real template file path when
overriding a template, but this was the more straightforward fix since
the strategy was already used in find references and rename locations.

fixes https://github.com/angular/vscode-ng-language-service/issues/1054

PR Close #40455

PR Close #40491
2021-01-19 14:18:07 -08:00
Kristiyan Kostadinov 2c5ad5cbe8 fix(forms): error if control is removed as a result of another one being reset (#40462)
When a form is reset, it goes through `_forEachChild` to call `reset` on each of its children.
The problem is that if a control is removed while the loop is running (e.g. by a subscription),
the form will throw an error, because it built up the list of available control before the loop
started.

These changes fix the issue by adding a null check before invoing the callback.

Fixes #33401.

PR Close #40462
2021-01-19 11:41:23 -08:00
twerske 106df618bc refactor(core): add links to top compiler errors (#40326)
add links to 5 compiler error messages
navigate user to AIO new /errors pages for debugging

PR Close #40326
2021-01-19 10:14:57 -08:00
twerske d1c542b5c3 refactor(core): add links to top runtime errors (#40326)
add links to 5 runtime error messages
navigate user to AIO new /errors pages for debugging

PR Close #40326
2021-01-19 10:14:57 -08:00
Aristeidis Bampakos 2047d632d8 docs: add Angular Builders to community resources (#40475)
PR Close #40475
2021-01-19 09:22:35 -08:00
abarghoud 4468ac2999 feat(docs-infra): use Record type instead of key-value (#40463)
use Record type instead of key-value

Closes #39804

PR Close #40463
2021-01-19 09:21:33 -08:00
Alexey Elin 5578e23e54 docs: fix broken link to XSRF common protection mechanism (#40458)
PR Close #40458
2021-01-19 09:17:58 -08:00
JiaLiPassion ad2b50bc81 fix(core): fix fakeAsync() error messages (#40442)
Fix error messages when calling `fakeAsync()` without importing `zone-testing.js`

PR Close #40442
2021-01-19 09:15:12 -08:00
ivanwonder 524415ef8a fix(compiler): correct the KeySpan for animation events and properties (#40347)
We should provide the completion when the cursor is in the attribute
name after the `@` and `animate-`, but now the `KeySpan` starts from the
`@` or `animate-`. For example, the animation event `(@name.done)="v"`,
we can know where the cursor is by the `KeySpan` of `name.done` exactly,
it's in the event name or in the phase name.

PR Close #40347
2021-01-15 11:38:42 -08:00
Scott Wang 8e0b01eec3 refactor(compiler): remove unused files in css_parser/ and corresponding spec tests (#37463)
Reasons for change:
- css_parser, css_ast, and css_lexer are not used anywhere and there are
no entry points from compiler.ts
- tested by building Angular and building/running aio with build-local

PR Close #37463
2021-01-15 11:37:26 -08:00
Aristeidis Bampakos 0a00b7db93 docs: add Angular Nation to community resources (#40449)
PR Close #40449
2021-01-15 11:36:31 -08:00
Martin Sikora 6675b6fb35 fix(router): lazy loaded modules without RouterModule.forChild() won't cause an infinite loop (#36605)
When loading a module that doesn't provide `RouterModule.forChild()` preloader will get stuck
in an infinite loop and throw `ERROR Error: Maximum call stack size exceeded.`
The issue is that child module's `Injector` will look to its parent `Injector` when it doesn't
find any `ROUTES` so it will return routes for it's parent module instead. This will load the
child again that returns its parent's routes and so on.

Closes #29164

PR Close #36605
2021-01-15 10:39:56 -08:00
George Kalpakas 9c595b0fe5 feat(docs-infra): add shortcuts for the angular.io PWA (#40393)
This commits adds some shortcut definitions for the angular.io PWA. The
user agent can use them to assemble a context menu to be displayed by
the operating system when a user engages with the app's icon. (In
addition, shortcuts provide an easy way for users to add links to
specific pages on their home screen.)

See [here][1] for more details on the `shortcuts` property of the PWA
manifest.

The choice of pages to create shortcuts to was influenced by the
following facts/criteria:
- It seems that only the first 4 shortcuts are displayed by Chrome (at
  least on my Android phone).
- Since the PWA is mostly used on mobile, I omitted pages that are less
  likely to be useful for mobile users (such as pages related to CLI).

[1]: https://developer.mozilla.org/en-US/docs/Web/Manifest/shortcuts

PR Close #40393
2021-01-15 10:38:43 -08:00
George Kalpakas c8464eded1 refactor(docs-infra): more some pwa-manifest.json keys to the top to improve readability (#40393)
This commit moves some properties with simple, string values in
`pwa-manifest.json` to the top (i.e. above the more lengthy `icons`
array) to make them easier to discover.

PR Close #40393
2021-01-15 10:38:42 -08:00
Tibing 861fa84566 docs: add UI Bakery to IDEs section in resources (#40444)
PR Close #40444
2021-01-15 10:37:40 -08:00
Misko Hevery d5f696c187 refactor(core): Remove the need for explicit static query instruction (#40091)
Because the query now has `flags` which specify the mode, the static query
instruction can now be remove. It is simply normal query with `static` flag.

PR Close #40091
2021-01-14 13:55:03 -08:00
Misko Hevery 76f363381f fix(core): QueryList should not fire changes if the underlying list did not change. (#40091)
Previous implementation would fire changes `QueryList.changes.subscribe`
whenever the `QueryList` was recomputed. This resulted in artificially
high number of change notifications, as it is possible that recomputing
`QueryList` results in the same list. When the `QueryList` gets recomputed
is an implementation detail and it should not be the thing which determines
how often change event should fire.

This change introduces a new `emitDistinctChangesOnly` option for
`ContentChildren` and `ViewChildren`.

```
export class QueryCompWithStrictChangeEmitParent {
  @ContentChildren('foo', {
    // This option will become the default in the future
    emitDistinctChangesOnly: true,
  })
  foos!: QueryList<any>;
}
```

PR Close #40091
2021-01-14 13:55:03 -08:00
Alexey Elin 63bf613785 docs: remove duplicated the (#40434)
PR Close #40434
2021-01-14 11:33:58 -08:00
Pete Bacon Darwin 798aae45a8 perf(ngcc): do not copy files that have been processed (#40429)
When using the `NewEntryPointWriter`, we must copy over all files from the
entry-point bundle to the new entry-point. But since we are going to
write out the modified files directly, there is no need to copy those.
This commit skips copying the files that have been modified.

PR Close #40429
2021-01-14 11:30:43 -08:00
Pete Bacon Darwin e2e3862548 fix(ngcc): copy (and update) source-maps for unmodified source files (#40429)
When using the `NewEntryPointWriter` we copy unmodified files over to the new
entry-point in addition to writing out the source files that are processed by ngcc.
But we were not copying over associated source-map files for these unmodified
source files, leading to warnings in downstream tooling.

Now we will also copy over source-maps that reside as siblings of unmodified
source files. We have to make sure that the sources of the source-map point
to the correct files, so we also update the `sourceRoot` property of the copied
source-map.

Fixes #40358

PR Close #40429
2021-01-14 11:30:43 -08:00
bjarkler b1ae2396e1 docs: Fix typo in Trusted Types documentation (#40433)
Remove an extraneous 'd' from the Trusted Types header.
PR Close #40433
2021-01-14 11:29:44 -08:00
twerske 32bf23a489 docs: add guides for the top 10 errors (#40060)
add top 10 error guides
provide description and debugging for top errors

PR Close #40060
2021-01-14 11:28:56 -08:00
twerske 873bdf0345 build(docs-infra): error guides in docs (#40060)
add /errors to docs
create formatting for error guides infra

PR Close #40060
2021-01-14 11:28:56 -08:00
Max Schorradt 30a7351933 docs(core): change URL to shadow dom spec (#39797)
The old URL (https://w3c.github.io/webcomponents/spec/shadow/) is no longer available.
This commit updates to link to MDN instead.

Fixes #39822

PR Close #39797
2021-01-14 11:27:58 -08:00
David Shevitz 968b2bbfc6 docs: Update security.md to include Trusted Types (#40142)
PR Close #40142
2021-01-14 10:19:08 -08:00
Vytautas Petrikas 17f6b78969 docs(core): add Ivy note to entryComponents page (#35479)
PR Close #35479
2021-01-14 10:18:17 -08:00
Santosh Yadav f4ed12211e docs(common): updated differential loading section (#37913)
PR Close #37913
2021-01-14 09:16:37 -08:00
Bill Ludwig c6c7e9a4e3 docs: update identity-revealed.directive.ts to use AbstractControl instead of FormGroup (#40132)
Changes example code to accept an `AbstractControl` instead of `FormGroup` in
`identityRevealedValidator` function.

Closes #40131.

PR Close #40132
2021-01-13 14:47:54 -08:00
Joey Perrott 76cabb21f6 build: remove pre-check script from publish scripts as it no longer performs its check (#40422)
Previously the pre-check confirmed that the stamping value was normal and expected, however
with the new stamping methodology of marking for publish via flag, the check no longer will
ever see anything but a passing version, since it is stamped as publish.

PR Close #40422
2021-01-13 14:43:00 -08:00
Andrew Kushnir 0664d750b6 perf(core): make DI decorators tree-shakable when used for useFactory deps config (#40145)
This commit updates the logic that calculates `useFactory` function arguments to avoid relying on `instanceof`
checks (thus always retaining symbols) and relies on flags that DI decorators contain (as a monkey-patched property).

Another perf benefit is having less megamorphic reads while calculating args for the `useFactory` call: we used to
check whether a token has `ngMetadataName` property 4 times (in worst case), now we have just 1 megamorphic read in
all cases.

Closes #40143.

PR Close #40145
2021-01-13 14:08:45 -08:00
mgechev a5c4adcdf0 ci: add mgechev to docs-marketing approvers (#40396)
Add mgechev to docs-marketing approvals to allow approval of aio changes

PR Close #40396
2021-01-13 14:03:35 -08:00
Pete Bacon Darwin bbda311f32 build(docs-infra): fail if we attempt to autolink to a private doc (#40404)
Previously we only logged a warning if we attempted to auto-link
to a doc that had no `path` property, which implies that it is private
and is not rendered.

Now we fail hard during full doc-gen, although when running the
`yarn serv-and-sync` command it should not fail if changes are
only made to guides, which is what authors who use this tool
are most likely to do.

PR Close #40404
2021-01-13 14:03:07 -08:00
Pete Bacon Darwin f675dbffac build(docs-infra): ensure private exported subclasses are not rendered (#40404)
Previously, only the first layer of sub-classes were filtered to remove
private exports. But on recursing down further levels these were not
filtered out.

PR Close #40404
2021-01-13 14:03:07 -08:00
Pete Bacon Darwin b12b648517 build(docs-infra): add @default tag-def support (#40404)
This tag-def is being used in the `platform-server` source code,
and was causing warnings in the doc-gen. Adding support
prevents the warning from being shown.
Other than that this change has no effect on the generated
documentation, since the `@default` tag is not used in any
templates.

PR Close #40404
2021-01-13 14:03:07 -08:00
Pete Bacon Darwin 71c5e75d9c build(docs-infra): do not display stack trace on doc-gen error (#40404)
This commit updates `dgeni` to the latest version, which
will now only display the stack-trace after an error if the
logger is set to `debug`.

Fixes #39610

PR Close #40404
2021-01-13 14:03:07 -08:00
Alexey Elin 7a9bc9958a docs: add links to TypeScript (#40410)
PR Close #40410
2021-01-13 14:02:33 -08:00
Andrew Scott ebd94ef41b release: cut the v11.1.0-rc.0 release (#40420) 2021-01-13 13:26:54 -08:00
Andrew Scott 1ec5f869e0 release: cut the v11.1.0-next.5 release (#40417) 2021-01-13 12:12:35 -08:00
177 changed files with 2635 additions and 4268 deletions
+1 -1
View File
@@ -107,7 +107,7 @@ echo "export CI_SECRET_PAYLOAD_FIREBASE_TOKEN=\"${CI_SECRET_PAYLOAD_FIREBASE_TOK
####################################################################################################
####################################################################################################
## Source `$BASH_ENV` to make the variables available immediately. ##
## ***NOTE: This must remain the the last action in this script*** ##
## ***NOTE: This must remain the last action in this script*** ##
####################################################################################################
####################################################################################################
source $BASH_ENV;
+2
View File
@@ -950,6 +950,7 @@ groups:
users:
- aikidave
- IgorMinar
- mgechev
- StephenFluin
# =========================================================
@@ -1231,6 +1232,7 @@ groups:
'goldens/public-api/**',
'CHANGELOG.md',
'docs/NAMING.md',
'aio/content/errors/*.md',
'aio/content/guide/glossary.md',
'aio/content/guide/styleguide.md',
'aio/content/examples/styleguide/**',
+116 -144
View File
@@ -1,3 +1,119 @@
<a name="11.1.0"></a>
# 11.1.0 (2021-01-20)
### Bug Fixes
* **animations:** getAnimationStyle causes exceptions in older browsers ([#29709](https://github.com/angular/angular/issues/29709)) ([66d863f](https://github.com/angular/angular/commit/66d863f))
* **animations:** replace copy of query selector node-list from "spread" to "for" ([#39646](https://github.com/angular/angular/issues/39646)) ([bfa197f](https://github.com/angular/angular/commit/bfa197f)), closes [#38551](https://github.com/angular/angular/issues/38551)
* **common:** add `HttpParamsOptions` to the public api ([#35829](https://github.com/angular/angular/issues/35829)) ([b33b89d](https://github.com/angular/angular/commit/b33b89d)), closes [#20276](https://github.com/angular/angular/issues/20276)
* **common:** Prefer to use pageXOffset / pageYOffset instance of scrollX / scrollY ([#28262](https://github.com/angular/angular/issues/28262)) ([b1d300d](https://github.com/angular/angular/commit/b1d300d))
* **compiler:** correct the `KeySpan` for animation events and properties ([#40347](https://github.com/angular/angular/issues/40347)) ([524415e](https://github.com/angular/angular/commit/524415e))
* **compiler:** incorrectly interpreting some HostBinding names ([#40233](https://github.com/angular/angular/issues/40233)) ([1045465](https://github.com/angular/angular/commit/1045465)), closes [#40220](https://github.com/angular/angular/issues/40220) [#40230](https://github.com/angular/angular/issues/40230) [#18698](https://github.com/angular/angular/issues/18698)
* **compiler:** recover event parse when animation event name is empty ([#39925](https://github.com/angular/angular/issues/39925)) ([15b15be](https://github.com/angular/angular/commit/15b15be))
* **compiler:** avoid duplicate i18n blocks for i18n attrs on elements with structural directives ([#40077](https://github.com/angular/angular/issues/40077)) ([caa4666](https://github.com/angular/angular/commit/caa4666)), closes [#39942](https://github.com/angular/angular/issues/39942)
* **compiler:** handle strings inside bindings that contain binding characters ([#39826](https://github.com/angular/angular/issues/39826)) ([dc6d40e](https://github.com/angular/angular/commit/dc6d40e)), closes [#39601](https://github.com/angular/angular/issues/39601)
* **compiler:** disallow i18n of security-sensitive attributes ([#39554](https://github.com/angular/angular/issues/39554)) ([c8a99ef](https://github.com/angular/angular/commit/c8a99ef))
* **compiler:** ensure that placeholders have the correct sourceSpan ([#39717](https://github.com/angular/angular/issues/39717)) ([0462a61](https://github.com/angular/angular/commit/0462a61)), closes [#39671](https://github.com/angular/angular/issues/39671)
* **compiler:** only promote Trusted Types to constants when necessary ([#39554](https://github.com/angular/angular/issues/39554)) ([4916870](https://github.com/angular/angular/commit/4916870))
* **compiler:** report better error on interpolation in an expression ([#30300](https://github.com/angular/angular/issues/30300)) ([94e790d](https://github.com/angular/angular/commit/94e790d))
* **compiler-cli:** do not duplicate repeated source-files in rendered source-maps ([#40237](https://github.com/angular/angular/issues/40237)) ([3158858](https://github.com/angular/angular/commit/3158858))
* **compiler-cli:** remove classes in .d.ts files from provider checks ([#40118](https://github.com/angular/angular/issues/40118)) ([973bb40](https://github.com/angular/angular/commit/973bb40))
* **animations:** implement getPosition in browser animation builder ([#39983](https://github.com/angular/angular/issues/39983)) ([ca08625](https://github.com/angular/angular/commit/ca08625))
* **compiler-cli:** correct incremental behavior even with broken imports ([#39923](https://github.com/angular/angular/issues/39923)) ([c7c5b2f](https://github.com/angular/angular/commit/c7c5b2f))
* **compiler-cli:** remove the concept of an errored trait ([#39923](https://github.com/angular/angular/issues/39923)) ([6d42954](https://github.com/angular/angular/commit/6d42954))
* **compiler-cli:** track poisoned scopes with a flag ([#39923](https://github.com/angular/angular/issues/39923)) ([0823622](https://github.com/angular/angular/commit/0823622))
* **compiler-cli:** report error when a reference target is missing instead of crashing ([#39805](https://github.com/angular/angular/issues/39805)) ([453b32f](https://github.com/angular/angular/commit/453b32f)), closes [#38618](https://github.com/angular/angular/issues/38618) [#39744](https://github.com/angular/angular/issues/39744)
* **core:** fix possible XSS attack in development through SSR. ([#40136](https://github.com/angular/angular/issues/40136)) ([47d9b6d](https://github.com/angular/angular/commit/47d9b6d))
* **core:** set `ngDevMode` to `false` when calling `enableProdMode()` ([#40124](https://github.com/angular/angular/issues/40124)) ([70b4816](https://github.com/angular/angular/commit/70b4816))
* **core:** Allow passing AbstractType to the inject function ([#37958](https://github.com/angular/angular/issues/37958)) ([a1b6ad0](https://github.com/angular/angular/commit/a1b6ad0))
* **core:** Ensure OnPush ancestors are marked dirty when events occur ([#39833](https://github.com/angular/angular/issues/39833)) ([68d4a74](https://github.com/angular/angular/commit/68d4a74)), closes [#39832](https://github.com/angular/angular/issues/39832)
* **core:** meta addTag() adds incorrect attribute for httpEquiv ([#32531](https://github.com/angular/angular/issues/32531)) ([ff0a90e](https://github.com/angular/angular/commit/ff0a90e))
* **core:** migration error if program contains files outside of the project ([#39790](https://github.com/angular/angular/issues/39790)) ([1a26f6d](https://github.com/angular/angular/commit/1a26f6d)), closes [#39778](https://github.com/angular/angular/issues/39778)
* **core:** not invoking object's toString when rendering to the DOM ([#39843](https://github.com/angular/angular/issues/39843)) ([11cd37f](https://github.com/angular/angular/commit/11cd37f)), closes [#38839](https://github.com/angular/angular/issues/38839)
* **core:** remove duplicated noop function ([#39761](https://github.com/angular/angular/issues/39761)) ([066126a](https://github.com/angular/angular/commit/066126a))
* **core:** support `Attribute` DI decorator in `deps` section of a token ([#37085](https://github.com/angular/angular/issues/37085)) ([f5cbf0b](https://github.com/angular/angular/commit/f5cbf0b)), closes [#36479](https://github.com/angular/angular/issues/36479)
* **core:** remove application from the testability registry when the root view is removed ([#39876](https://github.com/angular/angular/issues/39876)) ([df27027](https://github.com/angular/angular/commit/df27027)), closes [#22106](https://github.com/angular/angular/issues/22106)
* **core:** Support extending differs from root `NgModule` ([#39981](https://github.com/angular/angular/issues/39981)) ([5fc4508](https://github.com/angular/angular/commit/5fc4508)), closes [#25015](https://github.com/angular/angular/issues/25015) [#11309](https://github.com/angular/angular/issues/11309) [#18554](https://github.com/angular/angular/issues/18554)
* **core:** unsubscribe from the `onError` when the root view is removed ([#39940](https://github.com/angular/angular/issues/39940)) ([5a3a154](https://github.com/angular/angular/commit/5a3a154))
* **core:** `QueryList` should not fire changes if the underlying list did not change. ([#40091](https://github.com/angular/angular/issues/40091)) ([76f3633](https://github.com/angular/angular/commit/76f3633))
* **core:** fix fakeAsync() error messages ([#40442](https://github.com/angular/angular/issues/40442)) ([ad2b50b](https://github.com/angular/angular/commit/ad2b50b))
* **forms:** error if control is removed as a result of another one being reset ([#40462](https://github.com/angular/angular/issues/40462)) ([2c5ad5c](https://github.com/angular/angular/commit/2c5ad5c)), closes [#33401](https://github.com/angular/angular/issues/33401)
* **forms:** clean up connection between FormControl/FormGroup and corresponding directive instances ([#39235](https://github.com/angular/angular/issues/39235)) ([a384961](https://github.com/angular/angular/commit/a384961)), closes [#20007](https://github.com/angular/angular/issues/20007) [#37431](https://github.com/angular/angular/issues/37431) [#39590](https://github.com/angular/angular/issues/39590)
* **language-service:** fix go to definition for template variables and references ([#40455](https://github.com/angular/angular/issues/40455)) ([#40491](https://github.com/angular/angular/issues/40491)) ([3e134e4](https://github.com/angular/angular/commit/3e134e4))
* **language-service:** Paths on Windows should be normalized ([#40492](https://github.com/angular/angular/issues/40492)) ([b8e47e2](https://github.com/angular/angular/commit/b8e47e2)), closes [/github.com/angular/vscode-ng-language-service/blob/9fca9c66510974c26d5c21b31adb9fa39ac0a38a/server/src/session.ts#L594](https://github.com//github.com/angular/vscode-ng-language-service/blob/9fca9c66510974c26d5c21b31adb9fa39ac0a38a/server/src/session.ts/issues/L594)
* **language-service:** report non-template diagnostics ([#40331](https://github.com/angular/angular/issues/40331)) ([4db89f4](https://github.com/angular/angular/commit/4db89f4))
* **language-service:** diagnostic and definition should work for absolute url ([#40406](https://github.com/angular/angular/issues/40406)) ([625d2c2](https://github.com/angular/angular/commit/625d2c2))
* **language-service:** reinstate overridden compiler option after change ([#40364](https://github.com/angular/angular/issues/40364)) ([811cacc](https://github.com/angular/angular/commit/811cacc))
* **language-service:** Support 'find references' for two-way bindings ([#40185](https://github.com/angular/angular/issues/40185)) ([ebb7ac5](https://github.com/angular/angular/commit/ebb7ac5))
* **language-service:** Support 'go to definition' for two-way bindings ([#40185](https://github.com/angular/angular/issues/40185)) ([a9d8c22](https://github.com/angular/angular/commit/a9d8c22))
* **language-service:** Support completions of two-way bindings ([#40185](https://github.com/angular/angular/issues/40185)) ([7d74853](https://github.com/angular/angular/commit/7d74853))
* **language-service:** Do not include $event parameter in reference results ([#40158](https://github.com/angular/angular/issues/40158)) ([d466db8](https://github.com/angular/angular/commit/d466db8)), closes [#40157](https://github.com/angular/angular/issues/40157)
* **language-service:** include compilerOptions.rootDir in rootDirs ([#40243](https://github.com/angular/angular/issues/40243)) ([a62416c](https://github.com/angular/angular/commit/a62416c)), closes [angular/vscode-ng-language-service#1039](https://github.com/angular/vscode-ng-language-service/issues/1039)
* **language-service:** LSParseConfigHost.resolve should not concat abs paths ([#40242](https://github.com/angular/angular/issues/40242)) ([0264f76](https://github.com/angular/angular/commit/0264f76))
* **language-service:** return all typecheck files via getExternalFiles ([#40162](https://github.com/angular/angular/issues/40162)) ([183fb7e](https://github.com/angular/angular/commit/183fb7e))
* **language-service:** shorthand syntax with variables ([#40239](https://github.com/angular/angular/issues/40239)) ([12cb39c](https://github.com/angular/angular/commit/12cb39c))
* **language-service:** force compileNonExportedClasses: false in LS ([#40092](https://github.com/angular/angular/issues/40092)) ([028e4f7](https://github.com/angular/angular/commit/028e4f7))
* **language-service:** Prevent matching nodes after finding a keySpan ([#40047](https://github.com/angular/angular/issues/40047)) ([1bf1b68](https://github.com/angular/angular/commit/1bf1b68))
* **language-service:** do not return external template that does not exist ([#39898](https://github.com/angular/angular/issues/39898)) ([2b84882](https://github.com/angular/angular/commit/2b84882))
* **language-service:** do not treat file URIs as general URLs ([#39917](https://github.com/angular/angular/issues/39917)) ([3b70098](https://github.com/angular/angular/commit/3b70098))
* **localize:** ensure extracted messages are serialized in a consistent order ([#40192](https://github.com/angular/angular/issues/40192)) ([212245f](https://github.com/angular/angular/commit/212245f)), closes [#39262](https://github.com/angular/angular/issues/39262)
* **ngcc:** copy (and update) source-maps for unmodified source files ([#40429](https://github.com/angular/angular/issues/40429)) ([e2e3862](https://github.com/angular/angular/commit/e2e3862)), closes [#40358](https://github.com/angular/angular/issues/40358)
* **service-worker:** handle error with ErrorHandler ([#39990](https://github.com/angular/angular/issues/39990)) ([74e42cf](https://github.com/angular/angular/commit/74e42cf)), closes [#39913](https://github.com/angular/angular/issues/39913)
* **router:** correctly handle string command in outlets ([#39728](https://github.com/angular/angular/issues/39728)) ([c33a823](https://github.com/angular/angular/commit/c33a823)), closes [#18928](https://github.com/angular/angular/issues/18928)
* **router:** lazy loaded modules without RouterModule.forChild() won't cause an infinite loop ([#36605](https://github.com/angular/angular/issues/36605)) ([6675b6f](https://github.com/angular/angular/commit/6675b6f)), closes [#29164](https://github.com/angular/angular/issues/29164)
* **router:** remove duplicated getOutlet function ([#39764](https://github.com/angular/angular/issues/39764)) ([5fa7673](https://github.com/angular/angular/commit/5fa7673))
* **service-worker:** correctly handle failed cache-busted request ([#39786](https://github.com/angular/angular/issues/39786)) ([6046419](https://github.com/angular/angular/commit/6046419)), closes [#39775](https://github.com/angular/angular/issues/39775) [#39775](https://github.com/angular/angular/issues/39775)
* **upgrade:** avoid memory leak when removing downgraded components ([#39965](https://github.com/angular/angular/issues/39965)) ([6dc43a4](https://github.com/angular/angular/commit/6dc43a4)), closes [#26209](https://github.com/angular/angular/issues/26209) [#39911](https://github.com/angular/angular/issues/39911) [#39921](https://github.com/angular/angular/issues/39921)
* **upgrade:** fix HMR for hybrid applications ([#40045](https://github.com/angular/angular/issues/40045)) ([b4b21bd](https://github.com/angular/angular/commit/b4b21bd)), closes [#39935](https://github.com/angular/angular/issues/39935)
### Performance Improvements
* **animations:** use `ngDevMode` to tree-shake warning ([#39964](https://github.com/angular/angular/issues/39964)) ([9ebe423](https://github.com/angular/angular/commit/9ebe423))
* **common:** use `ngDevMode` to tree-shake warnings ([#39964](https://github.com/angular/angular/issues/39964)) ([f022efa](https://github.com/angular/angular/commit/f022efa))
* **core:** use `ngDevMode` to tree-shake `checkNoChanges` ([#39964](https://github.com/angular/angular/issues/39964)) ([e1fe9ec](https://github.com/angular/angular/commit/e1fe9ec))
* **core:** use `ngDevMode` to tree-shake warnings ([#39959](https://github.com/angular/angular/issues/39959)) ([8b0cccc](https://github.com/angular/angular/commit/8b0cccc))
* **core:** make DI decorators tree-shakable when used for `useFactory` deps config ([#40145](https://github.com/angular/angular/issues/40145)) ([0664d75](https://github.com/angular/angular/commit/0664d75)), closes [#40143](https://github.com/angular/angular/issues/40143)
* **forms:** use `ngDevMode` to tree-shake `_ngModelWarning` ([#39964](https://github.com/angular/angular/issues/39964)) ([7954c8d](https://github.com/angular/angular/commit/7954c8d))
* **ngcc:** do not copy files that have been processed ([#40429](https://github.com/angular/angular/issues/40429)) ([798aae4](https://github.com/angular/angular/commit/798aae4))
### Features
* **common:** allow any Subscribable in async pipe ([#39627](https://github.com/angular/angular/issues/39627)) ([c7f4abf](https://github.com/angular/angular/commit/c7f4abf))
* **compiler:** recover expression parsing in more malformed pipe cases ([#39437](https://github.com/angular/angular/issues/39437)) ([e336572](https://github.com/angular/angular/commit/e336572))
* **compiler:** support recovery of malformed property writes ([#39103](https://github.com/angular/angular/issues/39103)) ([e44e10b](https://github.com/angular/angular/commit/e44e10b))
* **compiler:** add schema for Trusted Types sinks ([#39554](https://github.com/angular/angular/issues/39554)) ([358c50e](https://github.com/angular/angular/commit/358c50e))
* **compiler:** support error reporting in I18nMetaVisitor ([#39554](https://github.com/angular/angular/issues/39554)) ([bb70a9b](https://github.com/angular/angular/commit/bb70a9b))
* **compiler:** support tagged template literals in code generator ([#39122](https://github.com/angular/angular/issues/39122)) ([ef89274](https://github.com/angular/angular/commit/ef89274))
* **compiler:** allow trailing comma in array literal ([#22277](https://github.com/angular/angular/issues/22277)) ([8d613c1](https://github.com/angular/angular/commit/8d613c1)), closes [#20773](https://github.com/angular/angular/issues/20773)
* **compiler-cli:** implement partial directive declaration linking ([#39518](https://github.com/angular/angular/issues/39518)) ([87e9cd6](https://github.com/angular/angular/commit/87e9cd6))
* **compiler-cli:** partial compilation of directives ([#39518](https://github.com/angular/angular/issues/39518)) ([8c0a92b](https://github.com/angular/angular/commit/8c0a92b))
* **compiler-cli:** add support for using TypeScript 4.1 ([#39571](https://github.com/angular/angular/issues/39571)) ([a7e7c21](https://github.com/angular/angular/commit/a7e7c21))
* **compiler-cli:** support for partial compilation of components ([#39707](https://github.com/angular/angular/issues/39707)) ([e75244e](https://github.com/angular/angular/commit/e75244e))
* **compiler-cli:** expose function to allow short-circuiting of linking ([#40137](https://github.com/angular/angular/issues/40137)) ([7dcf286](https://github.com/angular/angular/commit/7dcf286))
* **compiler-cli:** JIT compilation of component declarations ([#40127](https://github.com/angular/angular/issues/40127)) ([d4327d5](https://github.com/angular/angular/commit/d4327d5))
* **compiler-cli:** JIT compilation of directive declarations ([#40101](https://github.com/angular/angular/issues/40101)) ([9186f1f](https://github.com/angular/angular/commit/9186f1f))
* **core:** add shouldCoalesceRunChangeDetection option to coalesce change detections in the same event loop. ([#39422](https://github.com/angular/angular/issues/39422)) ([5e92d64](https://github.com/angular/angular/commit/5e92d64)), closes [#39348](https://github.com/angular/angular/issues/39348) [#39348](https://github.com/angular/angular/issues/39348)
* **core:** adds get method to QueryList ([#36907](https://github.com/angular/angular/issues/36907)) ([a965589](https://github.com/angular/angular/commit/a965589)), closes [#29467](https://github.com/angular/angular/issues/29467)
* **core:** Add schematic to fix invalid `Route` configs ([#40067](https://github.com/angular/angular/issues/40067)) ([805b4f9](https://github.com/angular/angular/commit/805b4f9))
* **language-service:** log Angular compiler options ([#40364](https://github.com/angular/angular/issues/40364)) ([6a9e328](https://github.com/angular/angular/commit/6a9e328))
* **language-service:** autocomplete pipe binding expressions ([#40032](https://github.com/angular/angular/issues/40032)) ([cbb6eae](https://github.com/angular/angular/commit/cbb6eae))
* **language-service:** autocompletion of element tags ([#40032](https://github.com/angular/angular/issues/40032)) ([e42250f](https://github.com/angular/angular/commit/e42250f))
* **language-service:** autocompletion within expression contexts ([#39727](https://github.com/angular/angular/issues/39727)) ([93a8326](https://github.com/angular/angular/commit/93a8326))
* **language-service:** complete attributes on elements ([#40032](https://github.com/angular/angular/issues/40032)) ([66378ed](https://github.com/angular/angular/commit/66378ed))
* **language-service:** completions for structural directives ([#40032](https://github.com/angular/angular/issues/40032)) ([2a74431](https://github.com/angular/angular/commit/2a74431))
* **language-service:** enable get references for directive and component from template ([#40054](https://github.com/angular/angular/issues/40054)) ([973f797](https://github.com/angular/angular/commit/973f797))
* **language-service:** Add "find references" capability to Ivy integrated LS ([#39768](https://github.com/angular/angular/issues/39768)) ([06a782a](https://github.com/angular/angular/commit/06a782a))
* **language-service:** implement autocompletion for global properties (Ivy) ([#39250](https://github.com/angular/angular/issues/39250)) ([28a0bcb](https://github.com/angular/angular/commit/28a0bcb))
* **language-service:** Implement go to definition for style and template urls ([#39202](https://github.com/angular/angular/issues/39202)) ([563fb6c](https://github.com/angular/angular/commit/563fb6c))
* **localize:** support Application Resource Bundle (ARB) translation file format ([#36795](https://github.com/angular/angular/issues/36795)) ([5684ac5](https://github.com/angular/angular/commit/5684ac5))
* **platform-browser:** add doubletap HammerJS support ([#26362](https://github.com/angular/angular/issues/26362)) ([b5c0f9d](https://github.com/angular/angular/commit/b5c0f9d)), closes [#23954](https://github.com/angular/angular/issues/23954)
* **router:** add `relativeTo` as an input to `routerLink` ([#39720](https://github.com/angular/angular/issues/39720)) ([112324a](https://github.com/angular/angular/commit/112324a)), closes [#13523](https://github.com/angular/angular/issues/13523)
<a name="11.0.9"></a>
## 11.0.9 (2021-01-13)
@@ -36,30 +152,6 @@
<a name="11.1.0-next.4"></a>
# 11.1.0-next.4 (2021-01-06)
### Bug Fixes
* **forms:** clean up connection between FormControl/FormGroup and corresponding directive instances ([#39235](https://github.com/angular/angular/issues/39235)) ([a384961](https://github.com/angular/angular/commit/a384961)), closes [#20007](https://github.com/angular/angular/issues/20007) [#37431](https://github.com/angular/angular/issues/37431) [#39590](https://github.com/angular/angular/issues/39590)
* **language-service:** Do not include $event parameter in reference results ([#40158](https://github.com/angular/angular/issues/40158)) ([d466db8](https://github.com/angular/angular/commit/d466db8)), closes [#40157](https://github.com/angular/angular/issues/40157)
* **language-service:** include compilerOptions.rootDir in rootDirs ([#40243](https://github.com/angular/angular/issues/40243)) ([a62416c](https://github.com/angular/angular/commit/a62416c)), closes [angular/vscode-ng-language-service#1039](https://github.com/angular/vscode-ng-language-service/issues/1039)
* **language-service:** LSParseConfigHost.resolve should not concat abs paths ([#40242](https://github.com/angular/angular/issues/40242)) ([0264f76](https://github.com/angular/angular/commit/0264f76))
* **language-service:** return all typecheck files via getExternalFiles ([#40162](https://github.com/angular/angular/issues/40162)) ([183fb7e](https://github.com/angular/angular/commit/183fb7e))
* **language-service:** shorthand syntax with variables ([#40239](https://github.com/angular/angular/issues/40239)) ([12cb39c](https://github.com/angular/angular/commit/12cb39c))
* **localize:** ensure extracted messages are serialized in a consistent order ([#40192](https://github.com/angular/angular/issues/40192)) ([212245f](https://github.com/angular/angular/commit/212245f)), closes [#39262](https://github.com/angular/angular/issues/39262)
### Features
* **compiler-cli:** expose function to allow short-circuiting of linking ([#40137](https://github.com/angular/angular/issues/40137)) ([7dcf286](https://github.com/angular/angular/commit/7dcf286))
* **compiler-cli:** JIT compilation of component declarations ([#40127](https://github.com/angular/angular/issues/40127)) ([d4327d5](https://github.com/angular/angular/commit/d4327d5))
* **compiler-cli:** JIT compilation of directive declarations ([#40101](https://github.com/angular/angular/issues/40101)) ([9186f1f](https://github.com/angular/angular/commit/9186f1f))
* **core:** Add schematic to fix invalid `Route` configs ([#40067](https://github.com/angular/angular/issues/40067)) ([805b4f9](https://github.com/angular/angular/commit/805b4f9))
<a name="11.0.6"></a>
## 11.0.6 (2021-01-06)
@@ -91,32 +183,6 @@
* **core:** set `ngDevMode` to `false` when calling `enableProdMode()` ([#40124](https://github.com/angular/angular/issues/40124)) ([922f492](https://github.com/angular/angular/commit/922f492))
* **upgrade:** fix HMR for hybrid applications ([#40045](https://github.com/angular/angular/issues/40045)) ([c4c7509](https://github.com/angular/angular/commit/c4c7509)), closes [#39935](https://github.com/angular/angular/issues/39935)
<a name="11.1.0-next.3"></a>
# 11.1.0-next.3 (2020-12-16)
### Bug Fixes
* **compiler:** avoid duplicate i18n blocks for i18n attrs on elements with structural directives ([#40077](https://github.com/angular/angular/issues/40077)) ([caa4666](https://github.com/angular/angular/commit/caa4666)), closes [#39942](https://github.com/angular/angular/issues/39942)
* **compiler:** handle strings inside bindings that contain binding characters ([#39826](https://github.com/angular/angular/issues/39826)) ([dc6d40e](https://github.com/angular/angular/commit/dc6d40e)), closes [#39601](https://github.com/angular/angular/issues/39601)
* **compiler-cli:** remove classes in .d.ts files from provider checks ([#40118](https://github.com/angular/angular/issues/40118)) ([973bb40](https://github.com/angular/angular/commit/973bb40))
* **core:** fix possible XSS attack in development through SSR. ([#40136](https://github.com/angular/angular/issues/40136)) ([47d9b6d](https://github.com/angular/angular/commit/47d9b6d))
* **core:** set `ngDevMode` to `false` when calling `enableProdMode()` ([#40124](https://github.com/angular/angular/issues/40124)) ([70b4816](https://github.com/angular/angular/commit/70b4816))
* **language-service:** force compileNonExportedClasses: false in LS ([#40092](https://github.com/angular/angular/issues/40092)) ([028e4f7](https://github.com/angular/angular/commit/028e4f7))
* **language-service:** Prevent matching nodes after finding a keySpan ([#40047](https://github.com/angular/angular/issues/40047)) ([1bf1b68](https://github.com/angular/angular/commit/1bf1b68))
* **upgrade:** fix HMR for hybrid applications ([#40045](https://github.com/angular/angular/issues/40045)) ([b4b21bd](https://github.com/angular/angular/commit/b4b21bd)), closes [#39935](https://github.com/angular/angular/issues/39935)
### Features
* **language-service:** autocomplete pipe binding expressions ([#40032](https://github.com/angular/angular/issues/40032)) ([cbb6eae](https://github.com/angular/angular/commit/cbb6eae))
* **language-service:** autocompletion of element tags ([#40032](https://github.com/angular/angular/issues/40032)) ([e42250f](https://github.com/angular/angular/commit/e42250f))
* **language-service:** autocompletion within expression contexts ([#39727](https://github.com/angular/angular/issues/39727)) ([93a8326](https://github.com/angular/angular/commit/93a8326))
* **language-service:** complete attributes on elements ([#40032](https://github.com/angular/angular/issues/40032)) ([66378ed](https://github.com/angular/angular/commit/66378ed))
* **language-service:** completions for structural directives ([#40032](https://github.com/angular/angular/issues/40032)) ([2a74431](https://github.com/angular/angular/commit/2a74431))
* **language-service:** enable get references for directive and component from template ([#40054](https://github.com/angular/angular/issues/40054)) ([973f797](https://github.com/angular/angular/commit/973f797))
* **router:** add `relativeTo` as an input to `routerLink` ([#39720](https://github.com/angular/angular/issues/39720)) ([112324a](https://github.com/angular/angular/commit/112324a)), closes [#13523](https://github.com/angular/angular/issues/13523)
<a name="11.0.4"></a>
@@ -147,43 +213,6 @@
<a name="11.1.0-next.2"></a>
# 11.1.0-next.2 (2020-12-09)
### Bug Fixes
* **animations:** implement getPosition in browser animation builder ([#39983](https://github.com/angular/angular/issues/39983)) ([ca08625](https://github.com/angular/angular/commit/ca08625))
* **compiler-cli:** correct incremental behavior even with broken imports ([#39923](https://github.com/angular/angular/issues/39923)) ([c7c5b2f](https://github.com/angular/angular/commit/c7c5b2f))
* **compiler-cli:** remove the concept of an errored trait ([#39923](https://github.com/angular/angular/issues/39923)) ([6d42954](https://github.com/angular/angular/commit/6d42954))
* **compiler-cli:** track poisoned scopes with a flag ([#39923](https://github.com/angular/angular/issues/39923)) ([0823622](https://github.com/angular/angular/commit/0823622))
* **core:** remove application from the testability registry when the root view is removed ([#39876](https://github.com/angular/angular/issues/39876)) ([df27027](https://github.com/angular/angular/commit/df27027)), closes [#22106](https://github.com/angular/angular/issues/22106)
* **core:** Support extending differs from root `NgModule` ([#39981](https://github.com/angular/angular/issues/39981)) ([5fc4508](https://github.com/angular/angular/commit/5fc4508)), closes [#25015](https://github.com/angular/angular/issues/25015) [#11309](https://github.com/angular/angular/issues/11309) [#18554](https://github.com/angular/angular/issues/18554)
* **core:** unsubscribe from the `onError` when the root view is removed ([#39940](https://github.com/angular/angular/issues/39940)) ([5a3a154](https://github.com/angular/angular/commit/5a3a154))
* **language-service:** do not return external template that does not exist ([#39898](https://github.com/angular/angular/issues/39898)) ([2b84882](https://github.com/angular/angular/commit/2b84882))
* **language-service:** do not treat file URIs as general URLs ([#39917](https://github.com/angular/angular/issues/39917)) ([3b70098](https://github.com/angular/angular/commit/3b70098))
* **service-worker:** handle error with ErrorHandler ([#39990](https://github.com/angular/angular/issues/39990)) ([74e42cf](https://github.com/angular/angular/commit/74e42cf)), closes [#39913](https://github.com/angular/angular/issues/39913)
* **upgrade:** avoid memory leak when removing downgraded components ([#39965](https://github.com/angular/angular/issues/39965)) ([6dc43a4](https://github.com/angular/angular/commit/6dc43a4)), closes [#26209](https://github.com/angular/angular/issues/26209) [#39911](https://github.com/angular/angular/issues/39911) [#39921](https://github.com/angular/angular/issues/39921)
### Features
* **compiler:** allow trailing comma in array literal ([#22277](https://github.com/angular/angular/issues/22277)) ([8d613c1](https://github.com/angular/angular/commit/8d613c1)), closes [#20773](https://github.com/angular/angular/issues/20773)
* **compiler:** support tagged template literals in code generator ([#39122](https://github.com/angular/angular/issues/39122)) ([ef89274](https://github.com/angular/angular/commit/ef89274))
* **language-service:** Add "find references" capability to Ivy integrated LS ([#39768](https://github.com/angular/angular/issues/39768)) ([06a782a](https://github.com/angular/angular/commit/06a782a))
* **language-service:** implement autocompletion for global properties (Ivy) ([#39250](https://github.com/angular/angular/issues/39250)) ([28a0bcb](https://github.com/angular/angular/commit/28a0bcb))
### Performance Improvements
* **animations:** use `ngDevMode` to tree-shake warning ([#39964](https://github.com/angular/angular/issues/39964)) ([9ebe423](https://github.com/angular/angular/commit/9ebe423))
* **common:** use `ngDevMode` to tree-shake warnings ([#39964](https://github.com/angular/angular/issues/39964)) ([f022efa](https://github.com/angular/angular/commit/f022efa))
* **core:** use `ngDevMode` to tree-shake `checkNoChanges` ([#39964](https://github.com/angular/angular/issues/39964)) ([e1fe9ec](https://github.com/angular/angular/commit/e1fe9ec))
* **core:** use `ngDevMode` to tree-shake warnings ([#39959](https://github.com/angular/angular/issues/39959)) ([8b0cccc](https://github.com/angular/angular/commit/8b0cccc))
* **forms:** use `ngDevMode` to tree-shake `_ngModelWarning` ([#39964](https://github.com/angular/angular/issues/39964)) ([7954c8d](https://github.com/angular/angular/commit/7954c8d))
<a name="11.0.3"></a>
## 11.0.3 (2020-12-02)
@@ -211,43 +240,6 @@
* **forms:** Mark the {[key: string]: any} type for the options property of the FormBuilder.group method as deprecated. Using AbstractControlOptions gives the same functionality and is type-safe.
<a name="11.1.0-next.1"></a>
# 11.1.0-next.1 (2020-12-02)
### Bug Fixes
* **animations:** getAnimationStyle causes exceptions in older browsers ([#29709](https://github.com/angular/angular/issues/29709)) ([66d863f](https://github.com/angular/angular/commit/66d863f))
* **animations:** replace copy of query selector node-list from "spread" to "for" ([#39646](https://github.com/angular/angular/issues/39646)) ([bfa197f](https://github.com/angular/angular/commit/bfa197f)), closes [#38551](https://github.com/angular/angular/issues/38551)
* **common:** Prefer to use pageXOffset / pageYOffset instance of scrollX / scrollY ([#28262](https://github.com/angular/angular/issues/28262)) ([b1d300d](https://github.com/angular/angular/commit/b1d300d))
* **compiler:** disallow i18n of security-sensitive attributes ([#39554](https://github.com/angular/angular/issues/39554)) ([c8a99ef](https://github.com/angular/angular/commit/c8a99ef))
* **compiler:** ensure that placeholders have the correct sourceSpan ([#39717](https://github.com/angular/angular/issues/39717)) ([0462a61](https://github.com/angular/angular/commit/0462a61)), closes [#39671](https://github.com/angular/angular/issues/39671)
* **compiler:** only promote Trusted Types to constants when necessary ([#39554](https://github.com/angular/angular/issues/39554)) ([4916870](https://github.com/angular/angular/commit/4916870))
* **compiler:** report better error on interpolation in an expression ([#30300](https://github.com/angular/angular/issues/30300)) ([94e790d](https://github.com/angular/angular/commit/94e790d))
* **compiler-cli:** report error when a reference target is missing instead of crashing ([#39805](https://github.com/angular/angular/issues/39805)) ([453b32f](https://github.com/angular/angular/commit/453b32f)), closes [#38618](https://github.com/angular/angular/issues/38618) [#39744](https://github.com/angular/angular/issues/39744)
* **core:** Allow passing AbstractType to the inject function ([#37958](https://github.com/angular/angular/issues/37958)) ([a1b6ad0](https://github.com/angular/angular/commit/a1b6ad0))
* **core:** Ensure OnPush ancestors are marked dirty when events occur ([#39833](https://github.com/angular/angular/issues/39833)) ([68d4a74](https://github.com/angular/angular/commit/68d4a74)), closes [#39832](https://github.com/angular/angular/issues/39832)
* **core:** meta addTag() adds incorrect attribute for httpEquiv ([#32531](https://github.com/angular/angular/issues/32531)) ([ff0a90e](https://github.com/angular/angular/commit/ff0a90e))
* **core:** migration error if program contains files outside of the project ([#39790](https://github.com/angular/angular/issues/39790)) ([1a26f6d](https://github.com/angular/angular/commit/1a26f6d)), closes [#39778](https://github.com/angular/angular/issues/39778)
* **core:** not invoking object's toString when rendering to the DOM ([#39843](https://github.com/angular/angular/issues/39843)) ([11cd37f](https://github.com/angular/angular/commit/11cd37f)), closes [#38839](https://github.com/angular/angular/issues/38839)
* **core:** remove duplicated noop function ([#39761](https://github.com/angular/angular/issues/39761)) ([066126a](https://github.com/angular/angular/commit/066126a))
* **core:** support `Attribute` DI decorator in `deps` section of a token ([#37085](https://github.com/angular/angular/issues/37085)) ([f5cbf0b](https://github.com/angular/angular/commit/f5cbf0b)), closes [#36479](https://github.com/angular/angular/issues/36479)
* **router:** correctly handle string command in outlets ([#39728](https://github.com/angular/angular/issues/39728)) ([c33a823](https://github.com/angular/angular/commit/c33a823)), closes [#18928](https://github.com/angular/angular/issues/18928)
* **router:** remove duplicated getOutlet function ([#39764](https://github.com/angular/angular/issues/39764)) ([5fa7673](https://github.com/angular/angular/commit/5fa7673))
* **service-worker:** correctly handle failed cache-busted request ([#39786](https://github.com/angular/angular/issues/39786)) ([6046419](https://github.com/angular/angular/commit/6046419)), closes [#39775](https://github.com/angular/angular/issues/39775) [#39775](https://github.com/angular/angular/issues/39775)
### Features
* **common:** allow any Subscribable in async pipe ([#39627](https://github.com/angular/angular/issues/39627)) ([c7f4abf](https://github.com/angular/angular/commit/c7f4abf))
* **compiler:** add schema for Trusted Types sinks ([#39554](https://github.com/angular/angular/issues/39554)) ([358c50e](https://github.com/angular/angular/commit/358c50e))
* **compiler:** support error reporting in I18nMetaVisitor ([#39554](https://github.com/angular/angular/issues/39554)) ([bb70a9b](https://github.com/angular/angular/commit/bb70a9b))
* **compiler-cli:** add support for using TypeScript 4.1 ([#39571](https://github.com/angular/angular/issues/39571)) ([a7e7c21](https://github.com/angular/angular/commit/a7e7c21))
* **compiler-cli:** support for partial compilation of components ([#39707](https://github.com/angular/angular/issues/39707)) ([e75244e](https://github.com/angular/angular/commit/e75244e))
* **core:** adds get method to QueryList ([#36907](https://github.com/angular/angular/issues/36907)) ([a965589](https://github.com/angular/angular/commit/a965589)), closes [#29467](https://github.com/angular/angular/issues/29467)
* **localize:** support Application Resource Bundle (ARB) translation file format ([#36795](https://github.com/angular/angular/issues/36795)) ([5684ac5](https://github.com/angular/angular/commit/5684ac5))
* **platform-browser:** add doubletap HammerJS support ([#26362](https://github.com/angular/angular/issues/26362)) ([b5c0f9d](https://github.com/angular/angular/commit/b5c0f9d)), closes [#23954](https://github.com/angular/angular/issues/23954)
<a name="11.0.2"></a>
## 11.0.2 (2020-11-19)
@@ -259,26 +251,6 @@
<a name="11.1.0-next.0"></a>
# 11.1.0-next.0 (2020-11-18)
### Bug Fixes
* **common:** add `HttpParamsOptions` to the public api ([#35829](https://github.com/angular/angular/issues/35829)) ([b33b89d](https://github.com/angular/angular/commit/b33b89d)), closes [#20276](https://github.com/angular/angular/issues/20276)
### Features
* **compiler:** recover expression parsing in more malformed pipe cases ([#39437](https://github.com/angular/angular/issues/39437)) ([e336572](https://github.com/angular/angular/commit/e336572))
* **compiler:** support recovery of malformed property writes ([#39103](https://github.com/angular/angular/issues/39103)) ([e44e10b](https://github.com/angular/angular/commit/e44e10b))
* **compiler-cli:** implement partial directive declaration linking ([#39518](https://github.com/angular/angular/issues/39518)) ([87e9cd6](https://github.com/angular/angular/commit/87e9cd6))
* **compiler-cli:** partial compilation of directives ([#39518](https://github.com/angular/angular/issues/39518)) ([8c0a92b](https://github.com/angular/angular/commit/8c0a92b))
* **core:** add shouldCoalesceRunChangeDetection option to coalesce change detections in the same event loop. ([#39422](https://github.com/angular/angular/issues/39422)) ([5e92d64](https://github.com/angular/angular/commit/5e92d64)), closes [#39348](https://github.com/angular/angular/issues/39348) [#39348](https://github.com/angular/angular/issues/39348)
* **language-service:** Implement go to definition for style and template urls ([#39202](https://github.com/angular/angular/issues/39202)) ([563fb6c](https://github.com/angular/angular/commit/563fb6c))
<a name="11.0.1"></a>
## 11.0.1 (2020-11-18)
+20
View File
@@ -0,0 +1,20 @@
@name Expression Changed After Checked
@category runtime
@videoUrl https://www.youtube.com/embed/O47uUnJjbJc
@shortDescription Expression has changed after it was checked
@description
Angular throws an `ExpressionChangedAfterItHasBeenCheckedError` when an expression value has been changed after change detection has completed. Angular only throws this error in development mode.
In dev mode, Angular performs an additional check after each change detection run, to ensure the bindings haven’t changed. This catches errors where the view is left in an inconsistent state. This can occur, for example, if a method or getter returns a different value each time it is called, or if a child component changes values on its parent. If either of these occur, this is a sign that change detection is not stabilized. Angular throws the error to ensure data is always reflected correctly in the view, which prevents erratic UI behavior or a possible infinite loop.
This error commonly occurs when you’ve added template expressions or begun to implement lifecycle hooks like `ngAfterViewInit` or `ngOnChanges`. It is also common when dealing with loading status and asynchronous operations, or a child component changes its parent bindings.
@debugging
The [source maps](https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Use_a_source_map) generated by the CLI are very useful when debugging. Navigate up the call stack until you find a template expression where the value displayed in the error has changed.
Ensure that there are no changes to the bindings in the template after change detection is run. This often means refactoring to use the correct [component lifecycle hook](https://angular.io/guide/lifecycle-hooks) for your use case. If the issue exists within `ngAfterViewInit`, the recommended solution is to use a constructor or `ngOnInit` to set initial values, or use `ngAfterContentInit` for other value bindings.
If you are binding to methods in the view, ensure that the invocation does not update any of the other bindings in the template.
Read more about which solution is right for you in ['Everything you need to know about the "ExpressionChangedAfterItHasBeenCheckedError" error'](https://indepth.dev/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error/) and why this is useful at ['Angular Debugging "Expression has changed after it was checked": Simple Explanation (and Fix)'](https://blog.angular-university.io/angular-debugging/).
+12
View File
@@ -0,0 +1,12 @@
@name Circular Dependency in DI
@category runtime
@videoUrl https://www.youtube.com/embed/CpLOm4o_FzM
@shortDescription Circular dependency in DI detected while instantiating a provider
@description
A cyclic dependency exists when a [dependency of a service](https://angular.io/guide/hierarchical-dependency-injection) directly or indirectly depends on the service itself. For example, if `UserService` depends on `EmployeeService`, which also depends on `UserService`. Angular will have to instantiate `EmployeeService` to create `UserService`, which depends on `UserService`, itself.
@debugging
Use the call stack to determine where the cyclical dependency exists. You will be able to see if any child dependencies rely on the original file by [mapping out](https://angular.io/guide/dependency-injection-in-action) the component, module, or service’s dependencies and identify the loop causing the problem.
Break this loop (or circle) of dependency to resolve this error. This most commonly means removing or refactoring the dependencies to not be reliant on one another.
+20
View File
@@ -0,0 +1,20 @@
@name No Provider Found
@category runtime
@videoUrl https://www.youtube.com/embed/lAlOryf1-WU
@shortDescription No provider for found injectorDetails
@description
You see this error when you try to inject a service but have not declared a corresponding provider. A provider is a mapping that supplies a value that you can inject into the constructor of a class in your application.
Read more on providers in our [Dependency Injection guide](https://angular.io/guide/dependency-injection).
@debugging
Work backwards from the object where the error states that a [provider](https://angular.io/guide/architecture-services) is missing: `No provider for ${this}!`. This is commonly thrown in [services](https://angular.io/tutorial/toh-pt4), which require non-existing providers.
To fix the error ensure that your service is registered in the list of providers of an `NgModule` or has the `@Injectable` decorator with a `providedIn` property at top.
The most common solution is to add a provider in `@Injectable` using `providedIn`:
```typescript
@Injectable({ providedIn: 'app' })
```
+21
View File
@@ -0,0 +1,21 @@
@name Selector Collision
@category runtime
@videoUrl https://www.youtube.com/embed/z_3Z5mOm59I
@shortDescription Multiple components match with the same tagname
@description
Two or more components use the same [element selector](https://angular.io/guide/component-overview#specifying-a-components-css-selector). Because there can only be a single component associated with an element, selectors must be unique strings to prevent ambiguity for Angular.
@debugging
Use the element name from the error message to search for places where you’re using the same [selector declaration](https://angular.io/guide/architecture-components) in your codebase:
```typescript
@Component({
selector: 'YOUR_STRING',
...
})
```
Ensure that each component has a unique CSS selector. This will guarantee that Angular renders the component you expect.
If you’re having trouble finding multiple components with this selector tag name, check for components from imported component libraries, such as Angular Material. Make sure you're following the [best practices](https://angular.io/guide/styleguide#component-selectors) for your selectors to prevent collisions.
+32
View File
@@ -0,0 +1,32 @@
@name Export Not Found
@category runtime
@videoUrl https://www.youtube.com/embed/fUSAg4kp2WQ
@shortDescription Export not found!
@description
Angular can’t find a directive with `{{ PLACEHOLDER }}` export name. The export name is specified in the `exportAs` property of the directive decorator. This is common when using FormsModule or Material modules in templates, and you’ve forgotten to [import the corresponding modules](https://angular.io/guide/sharing-ngmodules).
<div class="alert is-helpful">
This is the runtime equivalent of a common compiler error [NG8003: No directive found with export](errors/NG8003).
</div>
@debugging
Use the export name to trace the templates or modules using this export.
Ensure that all dependencies are [properly imported and declared in your NgModules](https://angular.io/guide/sharing-ngmodules). For example, if the export not found is `ngForm`, we need to import `FormsModule` and declare it in the list of imports in `*.module.ts` to resolve the error.
```typescript
import { FormsModule } from '@angular/forms';
@NgModule({
...
imports: [
FormsModule,
…
```
If you recently added an import, you may need to restart your server to see these changes.
+29
View File
@@ -0,0 +1,29 @@
@name Argument Not Literal
@category compiler
@shortDescription Decorator argument is not an object literal
@description
To make the metadata extraction in the Angular compiler faster, the decorators `@NgModule`, `@Pipe`, `@Component`, `@Directive`, and `@Injectable` accept only object literals as arguments.
This is an [intentional change in Ivy](https://github.com/angular/angular/issues/30840#issuecomment-498869540), which enforces stricter argument requirements for decorators than View Engine. Ivy requires this approach because it compiles decorators by moving the expressions into other locations in the class output.
@debugging
Move all declarations:
```typescript
const moduleDefinition = {...}
@NgModule(moduleDefinition)
export class AppModule {
constructor() {}
}
```
into the decorator:
```typescript
@NgModule({...})
export class AppModule {
constructor() {}
}
```
+11
View File
@@ -0,0 +1,11 @@
@name Missing Token
@category compiler
@shortDescription No suitable injection token for parameter
@description
There is no injection token for a constructor parameter at compile time. [InjectionTokens](https://angular.io/api/core/InjectionToken) are tokens that can be used in a Dependency Injection Provider.
@debugging
Look at the parameter that throws the error and all uses of the class. This error is commonly thrown when a constructor defines parameters with primitive types like `string`, `number`, `boolean`, and `Object`.
Use the [@Injectable](https://angular.io/api/core/Injectable) method or [@Inject](https://angular.io/api/core/Inject) decorator from `@angular/core` to ensure that the type you are injecting is reified (has a runtime representation). Make sure to add a provider to this decorator so that you do not throw [NG0201: No Provider Found](https://angular.io/errors/NG0201).
+21
View File
@@ -0,0 +1,21 @@
@name Invalid Element
@category compiler
@shortDescription Unknown HTML element or component
@description
One or more elements cannot be resolved during compilation because the element is not defined by the HTML spec, or there is no component or directive with such element selector.
<div class="alert is-helpful">
This is the compiler equivalent of a common runtime error `NG0304: '${tagName}' is not a known element: …`.
</div>
@debugging
Use the element name in the error to find the file(s) where the element is being used.
Check that the name and selector are correct. If the component is from a different module or import, check that the component is exported from its origin module and imported into the correct `*.modules.ts` file, and declared in the imports list.
When using custom elements or web components, ensure that you add [`CUSTOM_ELEMENTS_SCHEMA`](https://angular.io/api/core/CUSTOM_ELEMENTS_SCHEMA) to the app module.
If this does not resolve the error, check the imported libraries for any recent changes to the exports and properties you are using, and restart your server.
+16
View File
@@ -0,0 +1,16 @@
@name Invalid Attribute
@category compiler
@videoUrl https://www.youtube.com/embed/wfLkB3RsSJM
@shortDescription Unknown attribute or input
@description
An attribute or property cannot be resolved during compilation.
This error arises when attempting to bind to a property that does not exist. Any property binding must correspond to either:
* A native property on the HTML element, or
* An `@Input()` property of a component or directive applied to the element.
The runtime error for this is `NG0304: '${tagName}' is not a known element: …’`.
@debugging
Look at documentation for the specific [binding syntax](https://angular.io/guide/binding-syntax) used. This is usually a typo or incorrect import. There may also be a missing direction with property selector ‘name’ or missing input.
+31
View File
@@ -0,0 +1,31 @@
@name Missing Reference Target
@category compiler
@videoUrl https://www.youtube.com/embed/fUSAg4kp2WQ
@shortDescription No directive found with export
@description
Angular can’t find a directive with `{{ PLACEHOLDER }}` export name. This is common with a missing import or a missing [`exportAs`](https://angular.io/api/core/Directive#exportAs) on a directive.
<div class="alert is-helpful">
This is the compiler equivalent of a common runtime error [NG0301: Export Not Found](errors/NG0301).
</div>
@debugging
Use the string name of the export not found to trace the templates or modules using this export.
Ensure that all dependencies are properly imported and declared in our Modules. For example, if the export not found is `ngForm`, we will need to import `FormsModule` and declare it in our list of imports in `*.module.ts` to resolve the missing export error.
```typescript
import { FormsModule } from '@angular/forms';
@NgModule({
...
imports: [
FormsModule,
…
```
If you recently added an import, you will need to restart your server to see these changes.
+1
View File
@@ -0,0 +1 @@
# Errors List
@@ -1,6 +1,6 @@
// #docregion
import { Directive, Input, OnChanges, SimpleChanges } from '@angular/core';
import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn, Validators } from '@angular/forms';
import { Directive, Input } from '@angular/core';
import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator, ValidatorFn } from '@angular/forms';
// #docregion custom-validator
/** A hero's name can't match the given regular expression */
@@ -22,7 +22,7 @@ export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
export class ForbiddenValidatorDirective implements Validator {
@Input('appForbiddenName') forbiddenName: string;
validate(control: AbstractControl): {[key: string]: any} | null {
validate(control: AbstractControl): ValidationErrors | null {
return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control)
: null;
}
@@ -4,7 +4,7 @@ import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, Validator,
// #docregion cross-validation-validator
/** A hero's name can't match the hero's alter ego */
export const identityRevealedValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
export const identityRevealedValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
const name = control.get('name');
const alterEgo = control.get('alterEgo');
+3 -3
View File
@@ -150,7 +150,7 @@ The following table summarizes class binding syntax.
<td><code>"my-class-1 my-class-2 my-class-3"</code></td>
</tr>
<tr>
<td><code>{[key: string]: boolean | undefined | null}</code></td>
<td><code>Record&lt;string, boolean | undefined | null&gt;</code></td>
<td><code>{foo: true, bar: false}</code></td>
</tr>
<tr>
@@ -248,7 +248,7 @@ The following table summarizes style binding syntax.
<td><code>"width: 100px; height: 100px"</code></td>
</tr>
<tr>
<td><code>{[key: string]: string | undefined | null}</code></td>
<td><code>Record&lt;string, string | undefined | null&gt;</code></td>
<td><code>{width: '100px', height: '100px'}</code></td>
</tr>
</table>
@@ -362,4 +362,4 @@ Another example is the [RouterOutlet](api/router/RouterOutlet) directive, which
Remember, use [@Input()](api/core/Input) when you want to keep track of the attribute value and update the associated property. Use [@Attribute()](api/core/Attribute) when you want to inject the value of an HTML attribute to a component or directive constructor.
</div>
</div>
+1 -2
View File
@@ -603,10 +603,9 @@ If your Browserslist configuration includes support for any legacy browsers, the
## Local development in older browsers
In Angular CLI version 8 and higher, differential loading is enabled by default for the `ng build` command.
Differential loading is not enabled by default for application projects that were generated with Angular CLI 10 and above.
The `ng serve`, `ng test`, and `ng e2e` commands, however, generate a single ES2015 build which cannot run in older browsers that don't support the modules, such as IE 11.
If you want to run ES5 code during development, you could disable differential loading completely.
To maintain the benefits of differential loading, however, a better option is to define multiple configurations for `ng serve`, `ng e2e`, and `ng test`.
{@a differential-serve}
+1 -1
View File
@@ -37,7 +37,7 @@ See the <live-example name="dynamic-form"></live-example>.
Before doing this tutorial, you should have a basic understanding to the following.
* [TypeScript](https://www.typescriptlang.org/docs/home.html "The TypeScript language") and HTML5 programming.
* [TypeScript](https://www.typescriptlang.org/ "The TypeScript language") and HTML5 programming.
* Fundamental concepts of [Angular app design](guide/architecture "Introduction to Angular app-design concepts").
+7
View File
@@ -1,5 +1,12 @@
# Entry components
<div class="alert is-helpful">
Entry components have been deprecated with the [Ivy rendering engine](https://angular.io/guide/ivy).
For more information, see [entryComponents deprecation](https://angular.io/guide/deprecations#entrycomponents-and-analyze_for_entry_components-no-longer-required) in the [Deprecated APIs and features](https://angular.io/guide/deprecations).
</div>
An entry component is any component that Angular loads imperatively, (which means you’re not referencing it in the template), by type. You specify an entry component by bootstrapping it in an NgModule, or including it in a routing definition.
<div class="alert is-helpful">
+1 -1
View File
@@ -8,7 +8,7 @@ in both reactive and template-driven forms.
Before reading about form validation, you should have a basic understanding of the following.
* [TypeScript](https://www.typescriptlang.org/docs/home.html "The TypeScript language") and HTML5 programming.
* [TypeScript](https://www.typescriptlang.org/ "The TypeScript language") and HTML5 programming.
* Fundamental concepts of [Angular app design](guide/architecture "Introduction to Angular app-design concepts").
+1 -1
View File
@@ -10,7 +10,7 @@ This guide provides information to help you decide which type of form works best
This guide assumes that you have a basic understanding of the following.
* [TypeScript](https://www.typescriptlang.org/docs/home.html "The TypeScript language") and HTML5 programming.
* [TypeScript](https://www.typescriptlang.org/ "The TypeScript language") and HTML5 programming.
* Angular app-design fundamentals, as described in [Angular Concepts](guide/architecture "Introduction to Angular concepts.").
+1 -1
View File
@@ -42,7 +42,7 @@ This tutorial teaches you how to do the following:
Before going further into template-driven forms, you should have a basic understanding of the following.
* TypeScript and HTML5 programming.
* [TypeScript](https://www.typescriptlang.org/ "The TypeScript language") and HTML5 programming.
* Angular app-design fundamentals, as described in [Angular Concepts](guide/architecture "Introduction to Angular concepts.").
* The basics of [Angular template syntax](guide/template-syntax "Template syntax guide").
* The form-design concepts that are presented in [Introduction to Forms](guide/forms-overview "Overview of Angular forms.").
+1 -1
View File
@@ -995,7 +995,7 @@ consider moving it to a utility function or into the `PackageSearchService` itse
## Security: XSRF protection
[Cross-Site Request Forgery (XSRF or CSRF)](https://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by which the attacker can trick an authenticated user into unknowingly executing actions on your website.
`HttpClient` supports a [common mechanism](https://en.wikipedia.org/wiki/Cross-site_request_forgery#Cookie-to-Header_Token) used to prevent XSRF attacks.
`HttpClient` supports a [common mechanism](https://en.wikipedia.org/wiki/Cross-site_request_forgery#Cookie-to-header_token) used to prevent XSRF attacks.
When performing HTTP requests, an interceptor reads a token from a cookie, by default `XSRF-TOKEN`, and sets it as an HTTP header, `X-XSRF-TOKEN`.
Since only code that runs on your domain could read the cookie, the backend can be certain that the HTTP request came from your client application and not an attacker.
+1 -1
View File
@@ -12,7 +12,7 @@ Try this <live-example title="Reactive Forms in Stackblitz">Reactive Forms live-
Before going further into reactive forms, you should have a basic understanding of the following:
* TypeScript programming.
* [TypeScript](https://www.typescriptlang.org/ "The TypeScript language") programming.
* Angular app-design fundamentals, as described in [Angular Concepts](guide/architecture "Introduction to Angular concepts.").
* The form-design concepts that are presented in [Introduction to Forms](guide/forms-overview "Overview of Angular forms.").
+99 -109
View File
@@ -1,34 +1,29 @@
# Security
This page describes Angular's built-in
This topic describes Angular's built-in
protections against common web-application vulnerabilities and attacks such as cross-site
scripting attacks. It doesn't cover application-level security, such as authentication (_Who is
this user?_) and authorization (_What can this user do?_).
scripting attacks. It doesn't cover application-level security, such as authentication and authorization.
For more information about the attacks and mitigations described below, see [OWASP Guide Project](https://www.owasp.org/index.php/Category:OWASP_Guide_Project).
You can run the <live-example></live-example> in Stackblitz and download the code from there.
<div class="callout is-important">
<h2 id='report-issues'>
Reporting vulnerabilities
</h2>
{@a report-issues}
<header>Reporting vulnerabilities</header>
To report vulnerabilities in Angular itself, email us at [security@angular.io](mailto:security@angular.io).
For more information about how Google handles security issues, see [Google's security
philosophy](https://www.google.com/about/appsecurity/).
</div>
<div class="callout is-helpful">
<h2 id='best-practices'>
Best practices
</h2>
{@a best-practices}
<header>Best practices</header>
* **Keep current with the latest Angular library releases.**
We regularly update the Angular libraries, and these updates may fix security defects discovered in
@@ -43,13 +38,9 @@ community and make a pull request.
* **Avoid Angular APIs marked in the documentation as “_Security Risk_.”**
For more information, see the [Trusting safe values](guide/security#bypass-security-apis) section of this page.
</div>
<h2 id='xss'>
Preventing cross-site scripting (XSS)
</h2>
## Preventing cross-site scripting (XSS)
[Cross-site scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting) enables attackers
to inject malicious code into web pages. Such code can then, for example, steal user data (in
@@ -64,16 +55,11 @@ attacker-controlled data enters the DOM, expect security vulnerabilities.
### Angular’s cross-site scripting security model
To systematically block XSS bugs, Angular treats all values as untrusted by default. When a value
is inserted into the DOM from a template, via property, attribute, style, class binding, or interpolation,
Angular sanitizes and escapes untrusted values.
To systematically block XSS bugs, Angular treats all values as untrusted by default. When a value is inserted into the DOM from a template binding, or interpolation, Angular sanitizes and escapes untrusted values. If a value was already sanitized outside of Angular and is considered safe, you can communicate this to Angular by marking the [value as trusted](#bypass-security-apis).
_Angular templates are the same as executable code_: HTML, attributes, and binding expressions
(but not the values bound) in templates are trusted to be safe. This means that applications must
prevent values that an attacker can control from ever making it into the source code of a
template. Never generate template source code by concatenating user input and templates.
To prevent these vulnerabilities, use
the [offline template compiler](guide/security#offline-template-compiler), also known as _template injection_.
Unlike values to be used for rendering, Angular templates are considered trusted by default, and should be treated as executable code. Never generate templates by concatenating user input and template syntax. Doing this would enable attackers to [inject arbitrary code](https://en.wikipedia.org/wiki/Code_injection) into your application. To prevent these vulnerabilities, always use the default [AOT template compiler](/guide/security#offline-template-compiler) in production deployments.
An additional layer of protection can be provided through the use of Content security policy and Trusted Types. These web platform features operate at the DOM level which is the most effective place to prevent XSS issues because they can't be bypassed using other, lower-level APIs. For this reason, we strongly encourage developers to take advantage of these features by configuring the [content security policy](#content-security-policy) for their application and enabling [trusted types enforcement](#trusted-types).
### Sanitization and security contexts
@@ -97,35 +83,26 @@ when it has to change a value during sanitization.
The following template binds the value of `htmlSnippet`, once by interpolating it into an element's
content, and once by binding it to the `innerHTML` property of an element:
<code-example path="security/src/app/inner-html-binding.component.html" header="src/app/inner-html-binding.component.html"></code-example>
Interpolated content is always escaped&mdash;the HTML isn't interpreted and the browser displays
angle brackets in the element's text content.
For the HTML to be interpreted, bind it to an HTML property such as `innerHTML`. But binding
a value that an attacker might control into `innerHTML` normally causes an XSS
vulnerability. For example, code contained in a `<script>` tag is executed:
vulnerability. For example, one could execute JavaScript in a following way:
<code-example path="security/src/app/inner-html-binding.component.ts" header="src/app/inner-html-binding.component.ts (class)" region="class"></code-example>
Angular recognizes the value as unsafe and automatically sanitizes it, which removes the `<script>`
tag but keeps safe content such as the `<b>` element.
Angular recognizes the value as unsafe and automatically sanitizes it, which removes the `onerror` attribute but keeps safe content such as the `<b>` element.
<div class="lightbox">
<img src='generated/images/guide/security/binding-inner-html.png' alt='A screenshot showing interpolated and bound HTML values'>
</div>
### Direct use of the DOM APIs and explicit sanitization calls
The built-in browser DOM APIs don't automatically protect you from security vulnerabilities.
Unless you enforce Trusted Types, the built-in browser DOM APIs don't automatically protect you from security vulnerabilities.
For example, `document`, the node available through `ElementRef`, and many third-party APIs
contain unsafe methods. In the same way, if you interact with other libraries that manipulate
the DOM, you likely won't have the same automatic sanitization as with Angular interpolations.
@@ -137,43 +114,9 @@ method and the appropriate `SecurityContext`. That function also accepts values
marked as trusted using the `bypassSecurityTrust`... functions, and will not sanitize them,
as [described below](#bypass-security-apis).
### Content security policy
Content Security Policy (CSP) is a defense-in-depth
technique to prevent XSS. To enable CSP, configure your web server to return an appropriate
`Content-Security-Policy` HTTP header. Read more about content security policy at the
[Web Fundamentals guide](https://developers.google.com/web/fundamentals/security/csp) on the
Google Developers website.
{@a offline-template-compiler}
### Use the offline template compiler
The offline template compiler prevents a whole class of vulnerabilities called template injection,
and greatly improves application performance. Use the offline template compiler in production
deployments; don't dynamically generate templates. Angular trusts template code, so generating
templates, in particular templates containing user data, circumvents Angular's built-in protections.
For information about dynamically constructing forms in a safe way, see the
[Dynamic Forms](guide/dynamic-form) guide page.
### Server-side XSS protection
HTML constructed on the server is vulnerable to injection attacks. Injecting template code into an
Angular application is the same as injecting executable code into the
application: it gives the attacker full control over the application. To prevent this,
use a templating language that automatically escapes values to prevent XSS vulnerabilities on
the server. Don't generate Angular templates on the server side using a templating language; doing this
carries a high risk of introducing template-injection vulnerabilities.
<h2 id='bypass-security-apis'>
Trusting safe values
</h2>
{@a bypass-security-apis}
### Trusting safe values
Sometimes applications genuinely need to include executable code, display an `<iframe>` from some
URL, or construct potentially dangerous URLs. To prevent automatic sanitization in any of these
@@ -195,26 +138,18 @@ Remember, whether a value is safe depends on context, so choose the right contex
your intended use of the value. Imagine that the following template needs to bind a URL to a
`javascript:alert(...)` call:
<code-example path="security/src/app/bypass-security.component.html" header="src/app/bypass-security.component.html (URL)" region="URL"></code-example>
Normally, Angular automatically sanitizes the URL, disables the dangerous code, and
in development mode, logs this action to the console. To prevent
this, mark the URL value as a trusted URL using the `bypassSecurityTrustUrl` call:
<code-example path="security/src/app/bypass-security.component.ts" header="src/app/bypass-security.component.ts (trust-url)" region="trust-url"></code-example>
<div class="lightbox">
<img src='generated/images/guide/security/bypass-security-component.png' alt='A screenshot showing an alert box created from a trusted URL'>
</div>
If you need to convert user input into a trusted value, use a
controller method. The following template allows users to enter a YouTube video ID and load the
corresponding video in an `<iframe>`. The `<iframe src>` attribute is a resource URL security
@@ -225,29 +160,93 @@ Angular to allow binding into `<iframe src>`:
<code-example path="security/src/app/bypass-security.component.html" header="src/app/bypass-security.component.html (iframe)" region="iframe"></code-example>
<code-example path="security/src/app/bypass-security.component.ts" header="src/app/bypass-security.component.ts (trust-video-url)" region="trust-video-url"></code-example>
{@a content-security-policy}
### Content security policy
Content Security Policy (CSP) is a defense-in-depth
technique to prevent XSS. To enable CSP, configure your web server to return an appropriate
`Content-Security-Policy` HTTP header. Read more about content security policy at the
[Web Fundamentals guide](https://developers.google.com/web/fundamentals/security/csp) on the
Google Developers website.
{@a trusted-types}
### Enforcing Trusted Types
<h2 id='http'>
HTTP-level vulnerabilities
</h2>
We recommend the use of [Trusted Types](https://w3c.github.io/webappsec-trusted-types/dist/spec/) as a way to help secure your applications from cross-site scripting attacks. Trusted Types is a [web platform](https://en.wikipedia.org/wiki/Web_platform)
feature that can help you prevent cross-site scripting attacks by enforcing
safer coding practices. Trusted Types can also help simplify the auditing of application code.
<div class="callout is-helpful">
Trusted Types might not yet be available in all browsers your application targets. In the case your Trusted-Types-enabled application runs in a browser that doesn't support Trusted Types, the functionality of the application will be preserved, and your application will be guarded against XSS via Angular's DomSanitizer. See [caniuse.com/trusted-types](https://caniuse.com/trusted-types) for the current browser support.
</div>
To enforce Trusted Types for your application, you must configure your application's web server to emit HTTP headers with one of the following Angular policies:
* `angular` - This policy is used in security-reviewed code that is internal to Angular, and is required for Angular to function when Trusted Types are enforced. Any inline template values or content sanitized by Angular is treated as safe by this policy.
* `angular#unsafe-bypass` - This policy is used for applications that use any of the methods in Angular's [DomSanitizer](api/platform-browser/DomSanitizer) that bypass security, such as `bypassSecurityTrustHtml`. Any application that uses these methods must enable this policy.
* `angular#unsafe-jit` - This policy is used by the [JIT compiler](api/core/Compiler). You must enable this policy if your application interacts directly with the JIT compiler or is running in JIT mode using the [platform browser dynamic](api/platform-browser-dynamic/platformBrowserDynamic).
You should configure the HTTP headers for Trusted Types in the following locations:
* Production serving infrastructure
* Angular CLI (`ng serve`), using the `headers` property in the `angular.json` file, for local development and end-to-end testing
* Karma (`ng test`), using the `customHeaders` property in the `karma.config.js` file, for unit testing
The following is an example of a header specifically configured for Trusted Types and Angular:
<code-example language="html">
Content-Security-Policy: trusted-types angular; require-trusted-types-for 'script';
</code-example>
The following is an example of a header specifically configured for Trusted Types and Angular applications that use any of the methods in Angular's [DomSanitizer](api/platform-browser/DomSanitizer) that bypasses security.
<code-example language="html">
Content-Security-Policy: trusted-types angular angular#unsafe-bypass; require-trusted-types-for 'script';
</code-example>
The following is an example of a header specifically configured for Trusted Types and Angular applications using JIT:
<code-example language="html">
Content-Security-Policy: trusted-types angular angular#unsafe-jit; require-trusted-types-for 'script';
</code-example>
<div class="callout is-helpful">
<header>Community contributions</header>
To learn more about troubleshooting Trusted Type configurations, the following resource might be helpful:
[Prevent DOM-based cross-site scripting vulnerabilities with Trusted Types](https://web.dev/trusted-types/#how-to-use-trusted-types)
</div>
{@a offline-template-compiler}
### Use the AOT template compiler
The AOT template compiler prevents a whole class of vulnerabilities called template injection,
and greatly improves application performance. The AOT template compiler is the default compiler used by Angular CLI applications, and you should use it in all production deployments.
An alternative to the AOT compiler is the JIT compiler which compiles templates to executable template code within the browser at runtime. Angular trusts template code, so dynamically generating templates and compiling them, in particular templates containing user data, circumvents Angular's built-in protections and is a security anti-pattern. For information about dynamically constructing forms in a safe way, see the [Dynamic Forms](guide/dynamic-form) guide.
{@a server-side-xss}
### Server-side XSS protection
HTML constructed on the server is vulnerable to injection attacks. Injecting template code into an Angular application is the same as injecting executable code into the application: it gives the attacker full control over the application. To prevent this, use a templating language that automatically escapes values to prevent XSS vulnerabilities on the server. Don't generate Angular templates on the server side using a templating language; doing this carries a high risk of introducing template-injection vulnerabilities.
{@a http}
## HTTP-level vulnerabilities
Angular has built-in support to help prevent two common HTTP vulnerabilities, cross-site request
forgery (CSRF or XSRF) and cross-site script inclusion (XSSI). Both of these must be mitigated primarily
on the server side, but Angular provides helpers to make integration on the client side easier.
<h3 id='xsrf'>
Cross-site request forgery
</h3>
{@a xsrf}
### Cross-site request forgery
In a cross-site request forgery (CSRF or XSRF), an attacker tricks the user into visiting
a different web page (such as `evil.com`) with malignant code that secretly sends a malicious request
@@ -287,12 +286,8 @@ The Stanford University paper
See also Dave Smith's easy-to-understand
[talk on XSRF at AngularConnect 2016](https://www.youtube.com/watch?v=9inczw6qtpY "Cross Site Request Funkery Securing Your Angular Apps From Evil Doers").
<h3 id='xssi'>
Cross-site script inclusion (XSSI)
</h3>
{@a xssi}
### Cross-site script inclusion (XSSI)
Cross-site script inclusion, also known as JSON vulnerability, can allow an attacker's website to
read data from a JSON API. The attack works on older browsers by overriding native JavaScript
@@ -308,13 +303,8 @@ Angular's `HttpClient` library recognizes this convention and automatically stri
For more information, see the XSSI section of this [Google web security blog
post](https://security.googleblog.com/2011/05/website-security-for-webmasters.html).
<h2 id='code-review'>
Auditing Angular applications
</h2>
{@a code-review}
## Auditing Angular applications
Angular applications must follow the same security principles as regular web applications, and
must be audited as such. Angular-specific APIs that should be audited in a security review,
+6 -21
View File
@@ -1,18 +1,6 @@
# SVG in templates
# SVG as templates
<div class="callout is-critical">
<header>Marked for archiving</header>
To ensure that you have the best experience possible, this topic is marked for archiving until we determine that it clearly conveys the most accurate information possible.
If you think this content should not be archived, please file a [GitHub issue](https://github.com/angular/angular/issues/new?template=3-docs-bug.md).
</div>
It is possible to use SVG as valid templates in Angular. All of the template syntax below is
applicable to both SVG and HTML. Learn more in the SVG [1.1](https://www.w3.org/TR/SVG11/) and
[2.0](https://www.w3.org/TR/SVG2/) specifications.
You can use SVG files as templates in your Angular applications. When you use an SVG as the template, you are able to use directives and bindings just like with HTML templates. With these features, you can dynamically generate interactive graphics.
<div class="alert is-helpful">
@@ -20,18 +8,15 @@ See the <live-example name="template-syntax"></live-example> for a working examp
</div>
Why would you use SVG as template, instead of simply adding it as image to your application?
## SVG syntax example
When you use an SVG as the template, you are able to use directives and bindings just like with HTML
templates. This means that you will be able to dynamically generate interactive graphics.
Refer to the sample code snippet below for a syntax example:
The following example shows the syntax for using an SVG as a template.
<code-example path="template-syntax/src/app/svg.component.ts" header="src/app/svg.component.ts"></code-example>
Add the following code to your `svg.component.svg` file:
To see property and event binding in action, add the following code to your `svg.component.svg` file:
<code-example path="template-syntax/src/app/svg.component.svg" header="src/app/svg.component.svg"></code-example>
Here you can see the use of a `click()` event binding and the property binding syntax
The example given uses a `click()` event binding and the property binding syntax
(`[attr.fill]="fillColor"`).
+17
View File
@@ -34,6 +34,16 @@
"desc": "Peer-reviewed Angular articles and tutorials.",
"url": "https://indepth.dev/angular/",
"title": "Angular inDepth"
},
"angular-nation": {
"desc": "Angular Nation is a free private network created for Angular developers to network, collaborate, and ask questions.",
"url": "https://www.angularnation.net/",
"title": "Angular Nation"
},
"angular-builders": {
"desc": "A collection of Angular builders for your Angular projects",
"url": "https://angular-builders.dev/",
"title": "Angular Builders"
}
}
},
@@ -190,6 +200,13 @@
"Tooling": {
"order": 2,
"resources": {
"ub": {
"desc": "UI Bakery is a low code platform to generate Angular applications.",
"logo": "",
"rev": true,
"title": "UI Bakery",
"url": "https://uibakery.io/"
},
"a1": {
"desc": "A Google Chrome Dev Tools extension for debugging Angular applications.",
"logo": "https://augury.angular.io/images/augury-logo.svg",
+45 -29
View File
@@ -56,35 +56,35 @@
"tooltip": "Set up your environment and learn basic concepts",
"children": [
{
"title": "Try it",
"tooltip": "Examine and work with a ready-made sample app, with no setup.",
"children": [
{
"url": "start",
"title": "Getting started",
"tooltip": "Take a look at Angular's component model, template syntax, and component communication."
},
{
"url": "start/start-routing",
"title": "Adding navigation",
"tooltip": "Navigate among different page views using the browser's URL."
},
{
"url": "start/start-data",
"title": "Managing Data",
"tooltip": "Use services and access external data via HTTP."
},
{
"url": "start/start-forms",
"title": "Using Forms for User Input",
"tooltip": "Learn about fetching and managing data from users with forms."
},
{
"url": "start/start-deployment",
"title": "Deploying an application",
"tooltip": "Move to local development, or deploy your application to Firebase or your own server."
}
]
"title": "Try it",
"tooltip": "Examine and work with a ready-made sample app, with no setup.",
"children": [
{
"url": "start",
"title": "Getting started",
"tooltip": "Take a look at Angular's component model, template syntax, and component communication."
},
{
"url": "start/start-routing",
"title": "Adding navigation",
"tooltip": "Navigate among different page views using the browser's URL."
},
{
"url": "start/start-data",
"title": "Managing Data",
"tooltip": "Use services and access external data via HTTP."
},
{
"url": "start/start-forms",
"title": "Using Forms for User Input",
"tooltip": "Learn about fetching and managing data from users with forms."
},
{
"url": "start/start-deployment",
"title": "Deploying an application",
"tooltip": "Move to local development, or deploy your application to Firebase or your own server."
}
]
},
{
"url": "guide/setup-local",
@@ -196,6 +196,11 @@
"url": "guide/inputs-outputs",
"title": "Inputs and Outputs",
"tooltip": "Introductory guide to sharing data between parent and child directives or components."
},
{
"url": "guide/svg-in-templates",
"title": "SVG as templates",
"tooltip": "Learn how to use SVGs as templates for Angular applications."
}
]
},
@@ -916,6 +921,17 @@
"tooltip": "Details of the Angular packages, classes, interfaces, and other types.",
"url": "api"
},
{
"title": "Error Reference",
"tooltip": "Details of the errors that can be thrown by Angular.",
"children": [
{
"title": "Overview",
"url": "errors",
"hidden": true
}
]
},
{
"title": "Example applications",
"tooltip": "List of all of the example applications in the Angular documentation.",
+1 -1
View File
@@ -13,7 +13,7 @@ To get the most out of this tutorial you should already have a basic understandi
* [HTML](https://developer.mozilla.org/en-US/docs/Learn/HTML "Learning HTML: Guides and tutorials")
* [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript "JavaScript")
* [TypeScript](https://www.typescriptlang.org/docs/home.html "TypeScript documentation")
* [TypeScript](https://www.typescriptlang.org/ "The TypeScript language")
<hr />
+1 -1
View File
@@ -121,7 +121,7 @@
"codelyzer": "^6.0.0",
"cross-spawn": "^5.1.0",
"css-selector-parser": "^1.3.0",
"dgeni": "^0.4.11",
"dgeni": "^0.4.13",
"dgeni-packages": "^0.28.4",
"entities": "^1.1.1",
"eslint": "^3.19.0",
+30 -4
View File
@@ -1,6 +1,9 @@
{
"short_name": "angular.io",
"name": "Angular Documentation",
"short_name": "angular.io",
"background_color": "#1976d2",
"theme_color": "#1976d2",
"display": "standalone",
"icons": [
{
"src": "assets/images/favicons/favicon-194x194.png",
@@ -20,7 +23,30 @@
}
],
"start_url": "/?utm_source=homescreen",
"background_color": "#1976d2",
"theme_color": "#1976d2",
"display": "standalone"
"shortcuts": [
{
"name": "Go to API Reference",
"short_name": "API",
"description": "Go to the Angular API reference page.",
"url": "/api?utm_source=homescreen"
},
{
"name": "Go to Glossary",
"short_name": "Glossary",
"description": "Go to the glossary page: A list of common Angular terms and their explanation.",
"url": "/guide/glossary?utm_source=homescreen"
},
{
"name": "Go to Resources",
"short_name": "Resources",
"description": "Go to the resources page: A list of Angular resouces, such as development tooling, UI libraries, books, courses, community publications, podcasts, etc.",
"url": "/resources?utm_source=homescreen"
},
{
"name": "Go to Tutorial: Tour of Heroes",
"short_name": "Tutorial",
"description": "Go to the \"Tour of Heroes\" tutorial page: Learn how to create your first Angular application.",
"url": "/tutorial?utm_source=homescreen"
}
]
}
+1
View File
@@ -52,6 +52,7 @@ aio-shell.folder-api mat-toolbar.mat-toolbar,
aio-shell.folder-cli mat-toolbar.mat-toolbar,
aio-shell.folder-docs mat-toolbar.mat-toolbar,
aio-shell.folder-guide mat-toolbar.mat-toolbar,
aio-shell.folder-errors mat-toolbar.mat-toolbar,
aio-shell.folder-start mat-toolbar.mat-toolbar,
aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
@media (min-width: $showTopMenuWidth) {
+73
View File
@@ -0,0 +1,73 @@
.error-list {
display: grid;
list-style: none;
padding: 0;
overflow: hidden;
@media screen and (max-width: 600px) {
margin: 0 0 0 -8px;
}
li {
@include font-size(14);
margin: 8px 0;
@include line-height(14);
padding: 0;
float: left;
overflow: hidden;
min-width: 220px;
text-overflow: ellipsis;
white-space: nowrap;
.symbol {
margin-right: 8px;
&.runtime {
background: $green-500;
}
&.compiler {
background: $blue-500;
}
}
.symbol.runtime:before {
content: "R";
}
.symbol.compiler:before {
content: "C";
}
a {
color: $blue-grey-600;
display: inline-block;
@include line-height(16);
padding: 0 16px 0;
text-decoration: none;
transition: all .3s;
overflow: hidden;
text-overflow: ellipsis;
&:hover {
background: $blue-grey-50;
color: $blue-500;
}
}
}
}
.error-video-container {
position: relative;
padding-bottom: 56.25%; /* 16:9 */
height: 0;
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
@@ -15,6 +15,7 @@
@import 'deploy-theme';
@import 'details';
@import 'edit-page-cta';
@import 'errors';
@import 'features';
@import 'filetree';
@import 'heading-anchors';
+1 -1
View File
@@ -41,7 +41,7 @@ export class SitePage {
async navigateTo(pageUrl: string) {
// Navigate to the page, disable animations, and wait for Angular.
await browser.get(`/${pageUrl}`);
await browser.get(`/${pageUrl.replace(/^\//, '')}`);
await browser.executeScript('document.body.classList.add(\'no-animations\')');
await browser.waitForAngular();
}
@@ -0,0 +1,29 @@
import { PwaManifestPage, PwaShortcutItem } from './pwa-manifest.po';
describe('PWA manifest', () => {
const page = new PwaManifestPage();
describe('shortcuts', () => {
let shortcuts: PwaShortcutItem[];
// Helpers
const pageExists = async (url: string) => {
await page.navigateTo(url);
const content = await page.getDocViewerText();
return !/page not found/i.test(content);
};
beforeEach(async () => {
shortcuts = await page.getPwaShortcuts();
});
it('should exist', async () => {
for (const {short_name, url} of shortcuts) {
expect(await pageExists(url)).toBe(
true,
`Page for shortcut '${short_name}' (from '${page.pwaManifestUrl}') does not exist. (URL: ${url})`);
}
});
});
});
+74
View File
@@ -0,0 +1,74 @@
import { get as httpGet } from 'http';
import { get as httpsGet } from 'https';
import { browser } from 'protractor';
import { SitePage } from './app.po';
export type Json = null | boolean | number | string | Json[] | { [key: string]: Json };
/**
* The shape of a PWA manifest.
* For simplicity, we only define types for the properties we care about in tests.
* @see https://developer.mozilla.org/en-US/docs/Web/Manifest
*/
export type PwaManifest = Json & {
shortcuts?: PwaShortcutItem[],
};
/**
* The shape of an item in a PWA manifest's `shortcuts` list.
* @see https://developer.mozilla.org/en-US/docs/Web/Manifest/shortcuts
*/
export type PwaShortcutItem = Json & {
url: string,
name: string,
short_name?: string,
description?: string,
icons?: PwaImageResource[],
};
/**
* The shape of an item in a PWA manifest's icons list (such as the value of the top-level `icons` property or that of
* the `icons` property of a shortcut item).
* @see https://w3c.github.io/manifest/#manifestimageresource-and-its-members
*/
export type PwaImageResource = Json & {
src: string,
sizes?: string,
type?: string,
purpose?: string,
};
export class PwaManifestPage extends SitePage {
/** The base URL with the trailing `/` stripped off (if any). */
baseUrl = browser.baseUrl.replace(/\/$/, '');
/** The URL to the app's PWA manifest. */
pwaManifestUrl = `${this.baseUrl}/pwa-manifest.json`;
private pwaManifestText: string | null = null;
/** Get the app's PWA manifest as an object. */
async getPwaManifest(): Promise<PwaManifest> {
if (this.pwaManifestText === null) {
const get = /^https:/.test(this.pwaManifestUrl) ? httpsGet : httpGet;
this.pwaManifestText = await new Promise<string>((resolve, reject) => {
let responseText = '';
get(this.pwaManifestUrl, res => res
.on('data', chunk => responseText += chunk)
.on('end', () => resolve(responseText))
.on('error', reject));
});
}
return JSON.parse(this.pwaManifestText);
}
/** Get a list of PWA shortcuts as extracted from the app's PWA manifest. */
async getPwaShortcuts(): Promise<PwaShortcutItem[]> {
const {shortcuts = []} = await this.getPwaManifest();
return shortcuts;
}
}
-1
View File
@@ -17,7 +17,6 @@ module.exports =
// Register the processors
.processor(require('./processors/mergeParameterInfo'))
.processor(require('./processors/processPseudoClasses'))
.processor(require('./processors/splitDescription'))
.processor(require('./processors/convertPrivateClassesToInterfaces'))
.processor(require('./processors/generateApiListDoc'))
.processor(require('./processors/addNotYetDocumentedProperty'))
@@ -0,0 +1,3 @@
module.exports = function() {
return {name: 'default'};
};
+2
View File
@@ -32,6 +32,7 @@ module.exports = new Package('angular-base', [
.processor(require('./processors/copyContentAssets'))
.processor(require('./processors/renderLinkInfo'))
.processor(require('./processors/checkContentRules'))
.processor(require('./processors/splitDescription'))
// overrides base packageInfo and returns the one for the 'angular/angular' repo.
.factory('packageInfo', function() { return require(path.resolve(PROJECT_ROOT, 'package.json')); })
@@ -140,6 +141,7 @@ module.exports = new Package('angular-base', [
.config(function(postProcessHtml, addImageDimensions, autoLinkCode, filterPipes, filterAmbiguousDirectiveAliases, ignoreHttpInUrls, ignoreGenericWords) {
addImageDimensions.basePath = path.resolve(AIO_PATH, 'src');
autoLinkCode.customFilters = [ignoreGenericWords, ignoreHttpInUrls, filterPipes, filterAmbiguousDirectiveAliases];
autoLinkCode.failOnMissingDocPath = true;
postProcessHtml.plugins = [
require('./post-processors/autolink-headings'),
addImageDimensions,
@@ -19,12 +19,23 @@ const textContent = require('hast-util-to-string');
* @property codeElements an array of strings.
* Only text contained in these elements will be linked to.
* Usually set to "code" but also "code-example" for angular.io.
*
* @property ignoredLanguages an array of languages that should not be auto-linked
*
* @property ignoredLanguages an array of languages that should not be auto-linked
*
* @property failOnMissingDocPath if set to true then this post-processor will cause the doc-gen
* to fail when it attempts to auto-link to a doc that has no `doc.path` property, which implies
* that it exists but is not public (nor rendered).
*
*/
module.exports = function autoLinkCode(getDocFromAlias) {
autoLinkCodeImpl.docTypes = [];
autoLinkCodeImpl.customFilters = [];
autoLinkCodeImpl.codeElements = ['code'];
autoLinkCodeImpl.ignoredLanguages = ['bash', 'sh', 'shell', 'json', 'markdown'];
autoLinkCodeImpl.failOnMissingDocPath = false;
return autoLinkCodeImpl;
function autoLinkCodeImpl() {
@@ -64,8 +75,10 @@ module.exports = function autoLinkCode(getDocFromAlias) {
// * do not have an ignored language
// * are not inside links
const isCodeElement = autoLinkCodeImpl.codeElements.some(elementType => is(node, elementType));
const hasNoAutoLink = node.properties.className && node.properties.className.includes('no-auto-link');
const isLanguageSupported = !autoLinkCodeImpl.ignoredLanguages.includes(node.properties.language);
const hasNoAutoLink =
node.properties.className && node.properties.className.includes('no-auto-link');
const isLanguageSupported =
!autoLinkCodeImpl.ignoredLanguages.includes(node.properties.language);
const isInLink = isInsideLink(ancestors);
return isCodeElement && !hasNoAutoLink && isLanguageSupported && !isInLink;
}
@@ -76,19 +89,19 @@ module.exports = function autoLinkCode(getDocFromAlias) {
function getNodes(node, file) {
return textContent(node)
.split(/([A-Za-z0-9_.-]+)/)
.filter(word => word.length)
.map((word, index, words) => {
// remove docs that fail the custom filter tests
const filteredDocs = autoLinkCodeImpl.customFilters.reduce(
(docs, filter) => filter(docs, words, index), getDocFromAlias(word));
.split(/([A-Za-z0-9_.-]+)/)
.filter(word => word.length)
.map((word, index, words) => {
// remove docs that fail the custom filter tests
const filteredDocs = autoLinkCodeImpl.customFilters.reduce(
(docs, filter) => filter(docs, words, index), getDocFromAlias(word));
return foundValidDoc(filteredDocs, word, file) ?
// Create a link wrapping the text node.
createLinkNode(filteredDocs[0], word) :
// this is just text so push a new text node
{type: 'text', value: word};
});
return foundValidDoc(filteredDocs, word, file) ?
// Create a link wrapping the text node.
createLinkNode(filteredDocs[0], word) :
// this is just text so push a new text node
{type: 'text', value: word};
});
}
/**
@@ -112,12 +125,16 @@ module.exports = function autoLinkCode(getDocFromAlias) {
return false;
}
if (doc.path === '') {
if (!doc.path) {
var message = `
autoLinkCode: Doc path is empty for "${doc.id}" - link will not be generated for "${keyword}".
Please make sure if the doc should be public. If not, it should probably not be referenced in the docs.`;
file.message(message);
if (autoLinkCodeImpl.failOnMissingDocPath) {
file.fail(message);
} else {
file.message(message);
}
return false;
}
@@ -2,7 +2,7 @@ var createTestPackage = require('../../helpers/test-package');
var Dgeni = require('dgeni');
describe('autoLinkCode post-processor', () => {
let processor, autoLinkCode, aliasMap, filterPipes;
let processor, autoLinkCode, aliasMap, filterPipes, log;
beforeEach(() => {
const testPackage = createTestPackage('angular-base-package');
@@ -15,6 +15,7 @@ describe('autoLinkCode post-processor', () => {
processor.docTypes = ['test-doc'];
processor.plugins = [autoLinkCode];
filterPipes = injector.get('filterPipes');
log = injector.get('log');
});
it('should insert an anchor into every code item that matches the id of an API doc', () => {
@@ -126,19 +127,9 @@ describe('autoLinkCode post-processor', () => {
expect(doc.renderedContent).toEqual('<code>MyClass</code>');
});
it('should ignore code items that match an API doc but have no path set',
() => {
aliasMap.addDoc(
{docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: ''});
const doc = {docType: 'test-doc', renderedContent: '<code>MyClass</code>'};
processor.$process([doc]);
expect(doc.renderedContent).toEqual('<code>MyClass</code>');
});
it('should ignore documents when the `docType` is set to `member` and the keyword doesn\'t include `.`',
() => {
aliasMap.addDoc(
{docType: 'member', id: 'MyEnum', aliases: ['MyEnum'], path: 'a/b/c'});
aliasMap.addDoc({docType: 'member', id: 'MyEnum', aliases: ['MyEnum'], path: 'a/b/c'});
const doc = {docType: 'test-doc', renderedContent: '<code>MyEnum</code>'};
processor.$process([doc]);
expect(doc.renderedContent).toEqual('<code>MyEnum</code>');
@@ -193,4 +184,25 @@ describe('autoLinkCode post-processor', () => {
processor.$process([doc]);
expect(doc.renderedContent).toEqual('<code language="bash">MyClass</code>');
});
it('should record a warning if the autolinked doc has no `path` and `failOnMissingDocPath` is false',
() => {
aliasMap.addDoc({docType: 'class', id: 'MyClass', aliases: ['MyClass']});
const doc = {docType: 'test-doc', renderedContent: '<code>MyClass</code>'};
autoLinkCode.failOnMissingDocPath = false;
processor.$process([doc]);
expect(log.warn).toHaveBeenCalledWith(`
autoLinkCode: Doc path is empty for "MyClass" - link will not be generated for "MyClass".
Please make sure if the doc should be public. If not, it should probably not be referenced in the docs. - doc (test-doc) `);
});
it('should fail if the autolinked doc has no `path` and `failOnMissingDocPath` is true', () => {
aliasMap.addDoc({docType: 'class', id: 'MyClass', aliases: ['MyClass']});
const doc = {docType: 'test-doc', renderedContent: '<code>MyClass</code>'};
autoLinkCode.failOnMissingDocPath = true;
expect(() => processor.$process([doc])).toThrowError(`
autoLinkCode: Doc path is empty for "MyClass" - link will not be generated for "MyClass".
Please make sure if the doc should be public. If not, it should probably not be referenced in the docs. - doc (test-doc) `);
});
});
+87
View File
@@ -0,0 +1,87 @@
/**
* @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 contentPackage = require('../content-package');
const {CONTENTS_PATH, TEMPLATES_PATH, requireFolder} = require('../config');
const errorPackage = new Package('angular-errors', [basePackage, contentPackage]);
errorPackage.factory(require('./readers/error'))
.processor(require('./processors/processErrorDocs'))
.processor(require('./processors/processErrorsContainerDoc'))
// Where do we find the error documentation files?
.config(function(readFilesProcessor, errorFileReader) {
readFilesProcessor.fileReaders.push(errorFileReader);
readFilesProcessor.sourceFiles = readFilesProcessor.sourceFiles.concat([
{
basePath: CONTENTS_PATH,
include: CONTENTS_PATH + '/errors/**/*.md',
exclude: CONTENTS_PATH + '/errors/index.md',
fileReader: 'errorFileReader'
},
{
basePath: CONTENTS_PATH,
include: CONTENTS_PATH + '/errors/index.md',
fileReader: 'contentFileReader'
},
]);
})
// Here we compute the `id`, `code`, `aliases`, `path` and `outputPath` for the `error` 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 errors 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: ['error'],
getId: function(doc) {
return doc.fileInfo
.relativePath
// strip off the extension
.replace(/\.\w*$/, '');
},
getAliases: function(doc) {
doc.code = path.basename(doc.id);
return [doc.id, doc.code];
}
});
computePathsProcessor.pathTemplates = computePathsProcessor.pathTemplates.concat([
{
docTypes: ['error'],
getPath: (doc) => doc.id,
outputPathTemplate: '${path}.json',
},
]);
})
// Configure jsdoc-style tag parsing
.config(function(parseTagsProcessor, getInjectables) {
// Load up all the tag definitions in the tag-defs folder
parseTagsProcessor.tagDefinitions = parseTagsProcessor.tagDefinitions.concat(
getInjectables(requireFolder(__dirname, './tag-defs')));
})
// The templates that define how the `error` and `error-container` doc-types are rendered are
// found in the `TEMPLATES_PATH/error` directory.
.config(function(templateFinder) {
templateFinder.templateFolders.unshift(path.resolve(TEMPLATES_PATH, 'error'));
})
// 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 `error` in those
// that it converts.
.config(function(convertToJsonProcessor, postProcessHtml) {
convertToJsonProcessor.docTypes.push('error');
postProcessHtml.docTypes.push('error');
});
module.exports = errorPackage;
@@ -0,0 +1,55 @@
module.exports = function processErrorDocs(createDocMessage) {
return {
$runAfter: ['extra-docs-added'],
$runBefore: ['rendering-docs'],
$process(docs) {
const navigationDoc = docs.find(doc => doc.docType === 'navigation-json');
const errorsNode = navigationDoc && findErrorsNode(navigationDoc.data['SideNav']);
if (!errorsNode) {
throw new Error(createDocMessage(
'Missing `errors` url - This node is needed as a place to insert the generated errors docs.',
navigationDoc));
}
docs.forEach(doc => {
if (doc.docType === 'error') {
// Add to navigation doc
const title = `${doc.code}: ${doc.name}`;
errorsNode.children.push({url: doc.path, title: title, tooltip: doc.name});
}
});
},
};
};
/**
* Look for the `errors` navigation node. It is the node whose first child has `url: 'errors'`.
* (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 `errors` node. Keep a list of nodes lists to check.
* (NOTE: Each item in the list is a LIST of nodes.)
*/
function findErrorsNode(nodes) {
const nodesList = [nodes];
while (nodesList.length > 0) {
// Get the first item from the list of nodes lists.
const currentNodes = nodesList.shift();
const errorsNode = currentNodes.find(isErrorsNode);
// One of the nodes in `currentNodes` was the `errors` node. Return it.
if (errorsNode) return errorsNode;
// The `errors` 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 `errors` node.
return undefined;
}
function isErrorsNode(node) {
return node.children && node.children.length && node.children[0].url === 'errors';
}
@@ -0,0 +1,125 @@
const testPackage = require('../../helpers/test-package');
const Dgeni = require('dgeni');
describe('processErrorDocs processor', () => {
let dgeni, injector, processor, createDocMessage;
beforeEach(() => {
dgeni = new Dgeni([testPackage('angular-errors-package')]);
injector = dgeni.configureInjector();
processor = injector.get('processErrorDocs');
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 error to the `errors` node in the navigation doc if there is a top level node with a `errors` url',
() => {
const errorDoc = {
docType: 'error',
name: 'error1',
code: '888',
path: 'errors/error1',
};
const navigation = {
docType: 'navigation-json',
data: {
SideNav: [
{url: 'some/page', title: 'Some Page'},
{
title: 'Errors',
children: [{'title': 'Overview', 'url': 'errors'}],
},
{url: 'other/page', title: 'Other Page'},
],
},
};
processor.$process([errorDoc, navigation]);
expect(navigation.data.SideNav[1].title).toEqual('Errors');
expect(navigation.data.SideNav[1].children).toEqual([
{url: 'errors', title: 'Overview'},
{url: 'errors/error1', title: '888: error1', tooltip: 'error1'},
]);
});
it('should detect the `errors` node if it is nested in another node', () => {
const errorDoc = {
docType: 'error',
name: 'error1',
code: '888',
path: 'errors/error1',
};
const navigation = {
docType: 'navigation-json',
data: {
SideNav: [
{url: 'some/page', title: 'Some Page'},
{
title: 'Errors Grandparent',
children: [
{url: 'some/nested/page', title: 'Some Nested Page'},
{
title: 'Errors Parent',
children: [
{url: 'some/more/nested/page', title: 'Some More Nested Page'},
{
title: 'Errors',
children: [{'title': 'Overview', 'url': 'errors'}],
},
{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([errorDoc, navigation]);
const errorsContainerNode = navigation.data.SideNav[1].children[1].children[1];
expect(errorsContainerNode.title).toEqual('Errors');
expect(errorsContainerNode.children).toEqual([
{url: 'errors', title: 'Overview'},
{url: 'errors/error1', title: '888: error1', tooltip: 'error1'},
]);
});
it('should complain if there is no child with `errors` url', () => {
const errorDoc = {
docType: 'error',
name: 'error1',
code: '888',
path: 'errors/error1',
};
const navigation = {
docType: 'navigation-json',
data: {
SideNav: [
{url: 'some/page', title: 'Some Page'}, {
title: 'Errors',
tooltip: 'Angular Error reference',
children: [{'title': 'Overview', 'url': 'not-errors'}]
},
{url: 'other/page', title: 'Other Page'}
]
}
};
expect(() => processor.$process([errorDoc, navigation]))
.toThrowError(createDocMessage(
'Missing `errors` url - This node is needed as a place to insert the generated errors docs.',
navigation));
});
});
@@ -0,0 +1,11 @@
module.exports = function processErrorsContainerDoc() {
return {
$runAfter: ['extra-docs-added'],
$runBefore: ['rendering-docs'],
$process(docs) {
const errorsDoc = docs.find(doc => doc.id === 'errors/index');
errorsDoc.id = 'errors-container';
errorsDoc.errors = docs.filter(doc => doc.docType === 'error');
}
};
};
@@ -0,0 +1,22 @@
const testPackage = require('../../helpers/test-package');
const processorFactory = require('./processErrorsContainerDoc');
const Dgeni = require('dgeni');
describe('processErrorsContainerDoc processor', () => {
it('should be available on the injector', () => {
const dgeni = new Dgeni([testPackage('angular-errors-package')]);
const injector = dgeni.configureInjector();
const processor = injector.get('processErrorsContainerDoc');
expect(processor.$process).toBeDefined();
});
it('should run after the correct processor', () => {
const processor = processorFactory();
expect(processor.$runAfter).toEqual(['extra-docs-added']);
});
it('should run before the correct processor', () => {
const processor = processorFactory();
expect(processor.$runBefore).toEqual(['rendering-docs']);
});
});
@@ -0,0 +1,23 @@
/**
* @dgService
* @description
* This file reader will pull the contents from a text file (by default .md)
*
* The doc will initially have the form:
* ```
* {
* docType: 'error',
* content: 'the content of the file',
* }
* ```
*/
module.exports = function errorFileReader() {
return {
name: 'errorFileReader',
defaultPattern: /\.md$/,
getDocs: function(fileInfo) {
// We return a single element array because content files only contain one document
return [{docType: 'error', content: fileInfo.content}];
}
};
};
@@ -0,0 +1,44 @@
const Dgeni = require('dgeni');
const path = require('canonical-path');
const testPackage = require('../../helpers/test-package');
describe('errorFileReader', () => {
let dgeni, injector, fileReader;
beforeEach(() => {
dgeni = new Dgeni([testPackage('angular-errors-package', false)]);
injector = dgeni.configureInjector();
fileReader = injector.get('errorFileReader');
});
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')).toBeTruthy();
expect(fileReader.defaultPattern.test('abc.js')).toBeFalsy();
});
});
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: 'error', content: 'A load of content'}
]);
});
});
});
@@ -0,0 +1,3 @@
module.exports = function() {
return {name: 'category'};
};
@@ -0,0 +1,3 @@
module.exports = function() {
return {name: 'debugging'};
};
@@ -0,0 +1,3 @@
module.exports = function() {
return {name: 'shortDescription'};
};
@@ -0,0 +1,3 @@
module.exports = function() {
return {name: 'videoUrl'};
};
@@ -9,12 +9,13 @@ const Package = require('dgeni').Package;
const gitPackage = require('dgeni-packages/git');
const apiPackage = require('../angular-api-package');
const contentPackage = require('../angular-content-package');
const errorsPackage = require('../angular-errors-package');
const cliDocsPackage = require('../cli-docs-package');
const { extname, resolve } = require('canonical-path');
const { existsSync } = require('fs');
const { SRC_PATH } = require('../config');
module.exports = new Package('angular.io', [gitPackage, apiPackage, contentPackage, cliDocsPackage])
module.exports = new Package('angular.io', [gitPackage, apiPackage, contentPackage, cliDocsPackage, errorsPackage])
// This processor relies upon the versionInfo. See below...
.processor(require('./processors/processNavigationMap'))
@@ -4,7 +4,7 @@
{% for descendant in descendants %}
<li>
<code>{$ descendant.name $}</code>
{$ renderDescendantList(descendant.descendants | filterByPropertyValue('docType', docTypeMatcher), docType, recursed, docTypeMatcher) $}
{$ renderDescendantList(descendant.descendants | filterByPropertyValue('docType', docTypeMatcher) | filterByPropertyValue('privateExport', undefined), docType, recursed, docTypeMatcher) $}
</li>
{% endfor %}
</ul>
@@ -0,0 +1,28 @@
{% import "lib/githubLinks.html" as github -%}
<h1>{$ doc.code $}: {$ doc.shortDescription $}</h1>
<div class="github-links">
{$ github.githubEditLink(doc, versionInfo) $}
</div>
{% if doc.videoUrl.length %}
<div class="error-video-container">
<iframe
src="{$ doc.videoUrl $}"
frameborder="0"
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
</div>
{% endif%}
{% block content %}
<div class="content">
<h2>Description</h2>
{$ doc.description | marked $}
</div>
<br>
<div class="debugging">
<h2>Debugging the error</h2>
{$ doc.debugging | marked $}
</div>
{% endblock %}
@@ -0,0 +1,18 @@
{% extends 'content.template.html' -%}
{% block content %}
<div class="content">
{$ doc.description | marked $}
</div>
<ul class="error-list">
{% for error in doc.errors %}
<li>
<a class="code-anchor" href="{$ error.path $}">
<span class="symbol {$ error.category $}"></span>
<code class="no-auto-link">{$ error.code $}: {$ error.name $}</code>
</a>
</li>
{% endfor %}
</ul>
{% endblock %}
+26 -1
View File
@@ -3616,6 +3616,11 @@ clone@^1.0.2:
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
clonedeep@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/clonedeep/-/clonedeep-2.0.0.tgz#8ceca0777f477bbf31fe8c871aaf63a390bbc272"
integrity sha1-jOygd39He78x/oyHGq9jo5C7wnI=
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
@@ -4677,7 +4682,22 @@ dgeni-packages@^0.28.4:
typescript "^3.2.2"
urlencode "^1.1.0"
dgeni@^0.4.11, dgeni@^0.4.9:
dgeni@^0.4.13:
version "0.4.13"
resolved "https://registry.yarnpkg.com/dgeni/-/dgeni-0.4.13.tgz#274ad9edfe17bd6c2c1c51e9f30cb20faccdabc0"
integrity sha512-gryF1gNdzbQdgAeQxjaFbbKgR0qzyjq93Rc6/3CsBIN88NJ5yvsT4x26BvCKVI6jh2WatgvuGj+akmWvGndTyQ==
dependencies:
canonical-path "~0.0.2"
clonedeep "^2.0.0"
dependency-graph "^0.7.0"
di "0.0.1"
fast-deep-equal "^3.1.3"
objectdiff "^1.1.0"
optimist "~0.6.1"
validate.js "^0.12.0"
winston "^2.1.1"
dgeni@^0.4.9:
version "0.4.12"
resolved "https://registry.yarnpkg.com/dgeni/-/dgeni-0.4.12.tgz#574a91de25dab0018a9832da83fdb7c2b8c7507b"
integrity sha512-AhlRuwduzZhHIpf8DYZ0wWHLvV+9IQ8dQRqzx3ryYEg9trU/gWc2xz3wnJrRpDnS6Hu6F73saj6m1xbckW3k9Q==
@@ -5560,6 +5580,11 @@ fast-deep-equal@^3.1.1:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-glob@^2.2.6:
version "2.2.7"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d"
+1 -1
View File
@@ -205,7 +205,7 @@ function validateCommonConfig(config) {
*/
function readConfigFile(configPath, returnEmptyObjectOnError) {
if (returnEmptyObjectOnError === void 0) { returnEmptyObjectOnError = false; }
// If the the `.ts` extension has not been set up already, and a TypeScript based
// If the `.ts` extension has not been set up already, and a TypeScript based
// version of the given configuration seems to exist, set up `ts-node` if available.
if (require.extensions['.ts'] === undefined && fs.existsSync(configPath + ".ts") &&
isTsNodeAvailable()) {
+1 -1
View File
@@ -121,7 +121,7 @@ function validateCommonConfig(config) {
*/
function readConfigFile(configPath, returnEmptyObjectOnError) {
if (returnEmptyObjectOnError === void 0) { returnEmptyObjectOnError = false; }
// If the the `.ts` extension has not been set up already, and a TypeScript based
// If the `.ts` extension has not been set up already, and a TypeScript based
// version of the given configuration seems to exist, set up `ts-node` if available.
if (require.extensions['.ts'] === undefined && fs.existsSync(configPath + ".ts") &&
isTsNodeAvailable()) {
+1 -1
View File
@@ -100,7 +100,7 @@ function validateCommonConfig(config: Partial<NgDevConfig>) {
* configuration file cannot be read.
*/
function readConfigFile(configPath: string, returnEmptyObjectOnError = false): object {
// If the the `.ts` extension has not been set up already, and a TypeScript based
// If the `.ts` extension has not been set up already, and a TypeScript based
// version of the given configuration seems to exist, set up `ts-node` if available.
if (require.extensions['.ts'] === undefined && existsSync(`${configPath}.ts`) &&
isTsNodeAvailable()) {
+10 -5
View File
@@ -174,10 +174,12 @@ export declare type ContentChildren = Query;
export declare interface ContentChildrenDecorator {
(selector: Type<any> | InjectionToken<unknown> | Function | string, opts?: {
descendants?: boolean;
emitDistinctChangesOnly?: boolean;
read?: any;
}): any;
new (selector: Type<any> | InjectionToken<unknown> | Function | string, opts?: {
descendants?: boolean;
emitDistinctChangesOnly?: boolean;
read?: any;
}): Query;
}
@@ -734,6 +736,7 @@ export declare type Provider = TypeProvider | ValueProvider | ClassProvider | Co
export declare interface Query {
descendants: boolean;
emitDistinctChangesOnly: boolean;
first: boolean;
isViewQuery: boolean;
read: any;
@@ -746,12 +749,12 @@ export declare abstract class Query {
export declare class QueryList<T> implements Iterable<T> {
[Symbol.iterator]: () => Iterator<T>;
readonly changes: Observable<any>;
get changes(): Observable<any>;
readonly dirty = true;
readonly first: T;
readonly last: T;
readonly length: number;
constructor();
constructor(_emitDistinctChangesOnly?: boolean);
destroy(): void;
filter(fn: (item: T, index: number, array: T[]) => boolean): T[];
find(fn: (item: T, index: number, array: T[]) => boolean): T | undefined;
@@ -760,7 +763,7 @@ export declare class QueryList<T> implements Iterable<T> {
map<U>(fn: (item: T, index: number, array: T[]) => U): U[];
notifyOnChanges(): void;
reduce<U>(fn: (prevValue: U, curValue: T, curIndex: number, array: T[]) => U, init: U): U;
reset(resultsTree: Array<T | any[]>): void;
reset(resultsTree: Array<T | any[]>, identityAccessor?: (value: T) => unknown): void;
setDirty(): void;
some(fn: (value: T, index: number, array: T[]) => boolean): boolean;
toArray(): T[];
@@ -838,11 +841,11 @@ export declare interface RendererType2 {
}
export declare class ResolvedReflectiveFactory {
dependencies: ɵangular_packages_core_core_d[];
dependencies: ɵangular_packages_core_core_e[];
factory: Function;
constructor(
factory: Function,
dependencies: ɵangular_packages_core_core_d[]);
dependencies: ɵangular_packages_core_core_e[]);
}
export declare interface ResolvedReflectiveProvider {
@@ -1010,9 +1013,11 @@ export declare type ViewChildren = Query;
export declare interface ViewChildrenDecorator {
(selector: Type<any> | InjectionToken<unknown> | Function | string, opts?: {
read?: any;
emitDistinctChangesOnly?: boolean;
}): any;
new (selector: Type<any> | InjectionToken<unknown> | Function | string, opts?: {
read?: any;
emitDistinctChangesOnly?: boolean;
}): ViewChildren;
}
+2 -2
View File
@@ -12,7 +12,7 @@
"master": {
"uncompressed": {
"runtime-es2015": 3033,
"main-es2015": 447349,
"main-es2015": 448090,
"polyfills-es2015": 52493
}
}
@@ -21,7 +21,7 @@
"master": {
"uncompressed": {
"runtime-es2015": 3153,
"main-es2015": 431137,
"main-es2015": 432078,
"polyfills-es2015": 52493
}
}
@@ -39,7 +39,7 @@
"master": {
"uncompressed": {
"runtime-es2015": 2285,
"main-es2015": 240909,
"main-es2015": 241738,
"polyfills-es2015": 36709,
"5-es2015": 745
}
+2 -2
View File
@@ -4,7 +4,7 @@ load(":angular_integration_test.bzl", "angular_integration_test")
# cases the tests are able to select a random free port.
#
# Where `ng e2e` is used we pass `ng e2e --port 0` which prompts the cli
# to select a random free port for the the e2e test. The protractor.conf is
# to select a random free port for the e2e test. The protractor.conf is
# automatically updated to use this port.
#
# Karma automatically finds a free port so no effort is needed there.
@@ -61,7 +61,7 @@ INTEGRATION_TESTS = {
},
"hello_world__systemjs_umd": {
# Special case for `hello_world__systemjs_umd` test as we want to pin
# `systems` at version 0.20.2 and not link to the the root @npm//systemjs
# `systems` at version 0.20.2 and not link to the root @npm//systemjs
# which is stuck at 0.18.10 and can't be updated to 0.20.2 without
# breaking the legacy saucelabs job.
"pinned_npm_packages": ["systemjs"],
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "11.1.0-next.4",
"version": "11.1.0",
"private": true,
"description": "Angular - a web framework for modern web apps",
"homepage": "https://github.com/angular/angular",
+1 -1
View File
@@ -331,7 +331,7 @@ def _filter_out_generated_files(files, extension, package_path = None):
files_list = files.to_list() if type(files) == _DEPSET_TYPE else files
for file in files_list:
# If the "package_path" parameter has been specified, filter out files
# that do not start with the the specified package path.
# that do not start with the specified package path.
if package_path and not file.short_path.startswith(package_path):
continue
+1 -1
View File
@@ -1773,7 +1773,7 @@ export class HttpClient {
/**
* Constructs a `PATCH` request that interprets the body as an `ArrayBuffer` and
* returns the the full event stream.
* returns the full event stream.
*
* @param url The endpoint URL.
* @param body The resources to edit.
+1 -1
View File
@@ -90,7 +90,7 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri
* You might want to show a set of properties from the same object. If you are waiting
* for asynchronous data, the object can be undefined.
* In this case, you can use `ngIf` and store the result of the condition in a local
* variable as shown in the the following example.
* variable as shown in the following example.
*
* {@example common/ngIf/ts/module.ts region='NgIfAs'}
*
@@ -63,9 +63,11 @@ describe('Ivy NgModule', () => {
}
const errorCode = ivyEnabled ? 'NG0200: ' : '';
const errorLink = ivyEnabled ? '. Find more at https://angular.io/errors/NG0200' : '';
expect(() => createInjector(AModule))
.toThrowError(`${
errorCode}Circular dependency in DI detected for AModule. Dependency path: AModule > BModule > AModule`);
errorCode}Circular dependency in DI detected for AModule. Dependency path: AModule > BModule > AModule${
errorLink}`);
});
it('merges imports and exports', () => {
@@ -147,6 +147,8 @@ function toQueryMetadata<TExpression>(obj: AstObject<R3DeclareQueryMetadata, TEx
first: obj.has('first') ? obj.getBoolean('first') : false,
predicate,
descendants: obj.has('descendants') ? obj.getBoolean('descendants') : false,
emitDistinctChangesOnly:
obj.has('emitDistinctChangesOnly') ? obj.getBoolean('emitDistinctChangesOnly') : true,
read: obj.has('read') ? obj.getOpaque('read') : null,
static: obj.has('static') ? obj.getBoolean('static') : false,
};
@@ -46,7 +46,7 @@ export class UmdReflectionHost extends Esm5ReflectionHost {
}
getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null {
// First we try one of the the following:
// First we try one of the following:
// 1. The `exports` identifier - referring to the current file/module.
// 2. An identifier (e.g. `foo`) that refers to an imported UMD module.
// 3. A UMD style export identifier (e.g. the `foo` of `exports.foo`).
@@ -173,7 +173,7 @@ export class EsmRenderingFormatter implements RenderingFormatter {
}
/**
* Rewrite the the IVY switch markers to indicate we are in IVY mode.
* Rewrite the IVY switch markers to indicate we are in IVY mode.
*/
rewriteSwitchableDeclarations(
outputText: MagicString, sourceFile: ts.SourceFile,
@@ -40,7 +40,7 @@ export class NewEntryPointFileWriter extends InPlaceFileWriter {
// The new folder is at the root of the overall package
const entryPoint = bundle.entryPoint;
const ngccFolder = this.fs.join(entryPoint.packagePath, NGCC_DIRECTORY);
this.copyBundle(bundle, entryPoint.packagePath, ngccFolder);
this.copyBundle(bundle, entryPoint.packagePath, ngccFolder, transformedFiles);
transformedFiles.forEach(file => this.writeFile(file, entryPoint.packagePath, ngccFolder));
this.updatePackageJson(entryPoint, formatProperties, ngccFolder);
}
@@ -67,18 +67,56 @@ export class NewEntryPointFileWriter extends InPlaceFileWriter {
}
protected copyBundle(
bundle: EntryPointBundle, packagePath: AbsoluteFsPath, ngccFolder: AbsoluteFsPath) {
bundle: EntryPointBundle, packagePath: AbsoluteFsPath, ngccFolder: AbsoluteFsPath,
transformedFiles: FileToWrite[]) {
const doNotCopy = new Set(transformedFiles.map(f => f.path));
bundle.src.program.getSourceFiles().forEach(sourceFile => {
const relativePath = this.fs.relative(packagePath, absoluteFromSourceFile(sourceFile));
const originalPath = absoluteFromSourceFile(sourceFile);
if (doNotCopy.has(originalPath)) {
return;
}
const relativePath = this.fs.relative(packagePath, originalPath);
const isInsidePackage = isLocalRelativePath(relativePath);
if (!sourceFile.isDeclarationFile && isInsidePackage) {
const newFilePath = this.fs.resolve(ngccFolder, relativePath);
this.fs.ensureDir(this.fs.dirname(newFilePath));
this.fs.copyFile(absoluteFromSourceFile(sourceFile), newFilePath);
const newPath = this.fs.resolve(ngccFolder, relativePath);
this.fs.ensureDir(this.fs.dirname(newPath));
this.fs.copyFile(originalPath, newPath);
this.copyAndUpdateSourceMap(originalPath, newPath);
}
});
}
/**
* If a source file has an associated source-map, then copy this, while updating its sourceRoot
* accordingly.
*
* For now don't try to parse the source for inline source-maps or external source-map links,
* since that is more complex and will slow ngcc down.
* Instead just check for a source-map file residing next to the source file, which is by far
* the most common case.
*
* @param originalSrcPath absolute path to the original source file being copied.
* @param newSrcPath absolute path to where the source will be written.
*/
protected copyAndUpdateSourceMap(originalSrcPath: AbsoluteFsPath, newSrcPath: AbsoluteFsPath):
void {
const sourceMapPath = (originalSrcPath + '.map') as AbsoluteFsPath;
if (this.fs.exists(sourceMapPath)) {
try {
const sourceMap = JSON.parse(this.fs.readFile(sourceMapPath));
const newSourceMapPath = (newSrcPath + '.map') as AbsoluteFsPath;
const relativePath =
this.fs.relative(this.fs.dirname(newSourceMapPath), this.fs.dirname(sourceMapPath));
sourceMap['sourceRoot'] = this.fs.join(relativePath, sourceMap['sourceRoot'] || '.');
this.fs.ensureDir(this.fs.dirname(newSourceMapPath));
this.fs.writeFile(newSourceMapPath, JSON.stringify(sourceMap));
} catch (e) {
this.logger.warn(`Failed to process source-map at ${sourceMapPath}`);
this.logger.warn(e.message ?? e);
}
}
}
protected writeFile(file: FileToWrite, packagePath: AbsoluteFsPath, ngccFolder: AbsoluteFsPath):
void {
if (isDtsPath(file.path.replace(/\.map$/, ''))) {
@@ -570,7 +570,7 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });`
UndecoratedBase.ɵfac = function UndecoratedBase_Factory(t) { return new (t || UndecoratedBase)(); };
// TRANSPILED
UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, viewQuery: function UndecoratedBase_Query(rf, ctx) { if (rf & 1) {
ɵngcc0.ɵɵstaticViewQuery(_c0, true);
ɵngcc0.ɵɵviewQuery(_c0, 3);
} if (rf & 2) {
let _t;
ɵngcc0.ɵɵqueryRefresh(_t = ɵngcc0.ɵɵloadQuery()) && (ctx.test = _t.first);
@@ -51,6 +51,12 @@ runInEachFileSystem(() => {
{name: _('/node_modules/test/esm5.js'), contents: 'export function FooTop() {}'},
{name: _('/node_modules/test/esm5.js.map'), contents: 'ORIGINAL MAPPING DATA'},
{name: _('/node_modules/test/es2015/index.js'), contents: 'export {FooTop} from "./foo";'},
{
name: _('/node_modules/test/es2015/index.js.map'),
contents:
'{"version":3,"file":"index.js","sources":["../src/index.ts"],"mappings":"AAAA"}'
},
{name: _('/node_modules/test/src/index.ts'), contents: 'export {FooTop} from "./foo";'},
{name: _('/node_modules/test/es2015/foo.js'), contents: 'export class FooTop {}'},
{
name: _('/node_modules/test/a/package.json'),
@@ -154,6 +160,85 @@ runInEachFileSystem(() => {
.toEqual('export {FooTop} from "./foo";');
});
it('should copy any source-map for unmodified files in the program (adding missing sourceRoot)',
() => {
// Ensure source-mapping for a non-processed source file `index.js`.
const sourceMap = {
version: 3,
file: 'index.js',
sources: ['../src/index.ts'],
mappings: 'AAAA',
};
loadTestFiles([
{
name: _('/node_modules/test/es2015/index.js.map'),
contents: JSON.stringify(sourceMap)
},
{name: _('/node_modules/test/src/index.ts'), contents: 'export {FooTop} from "./foo";'}
]);
// Simulate that only the `foo.js` file was modified
const modifiedFiles = [{
path: _('/node_modules/test/es2015/foo.js'),
contents: 'export class FooTop {} // MODIFIED'
}];
fileWriter.writeBundle(esm2015bundle, modifiedFiles, ['es2015']);
expect(JSON.parse(fs.readFile(_('/node_modules/test/__ivy_ngcc__/es2015/index.js.map'))))
.toEqual({...sourceMap, sourceRoot: '../../es2015'});
});
it('should copy any source-map for unmodified files in the program (updating sourceRoot)',
() => {
// Ensure source-mapping for a non-processed source file `index.js`.
const sourceMap = {
version: 3,
file: 'index.js',
sourceRoot: '../src',
sources: ['index.ts'],
mappings: 'AAAA',
};
loadTestFiles([
{
name: _('/node_modules/test/es2015/index.js.map'),
contents: JSON.stringify(sourceMap)
},
{name: _('/node_modules/test/src/index.ts'), contents: 'export {FooTop} from "./foo";'}
]);
// Simulate that only the `foo.js` file was modified
const modifiedFiles = [{
path: _('/node_modules/test/es2015/foo.js'),
contents: 'export class FooTop {} // MODIFIED'
}];
fileWriter.writeBundle(esm2015bundle, modifiedFiles, ['es2015']);
expect(JSON.parse(fs.readFile(_('/node_modules/test/__ivy_ngcc__/es2015/index.js.map'))))
.toEqual({...sourceMap, sourceRoot: '../../src'});
});
it('should ignore (with a warning) any invalid source-map for unmodified files in the program',
() => {
// Ensure source-mapping for a non-processed source file `index.js`.
loadTestFiles([
{name: _('/node_modules/test/es2015/index.js.map'), contents: 'INVALID JSON STRING'},
{name: _('/node_modules/test/src/index.ts'), contents: 'export {FooTop} from "./foo";'}
]);
// Simulate that only the `foo.js` file was modified
const modifiedFiles = [{
path: _('/node_modules/test/es2015/foo.js'),
contents: 'export class FooTop {} // MODIFIED'
}];
fileWriter.writeBundle(esm2015bundle, modifiedFiles, ['es2015']);
expect(fs.exists(_('/node_modules/test/__ivy_ngcc__/es2015/index.js.map'))).toBe(false);
expect(logger.logs.warn).toEqual([
[`Failed to process source-map at ${_('/node_modules/test/es2015/index.js.map')}`],
['Unexpected token I in JSON at position 0'],
]);
});
it('should update the package.json properties', () => {
fileWriter.writeBundle(
esm5bundle,
@@ -7,6 +7,7 @@
*/
import {compileDeclareDirectiveFromMetadata, compileDirectiveFromMetadata, ConstantPool, Expression, ExternalExpr, getSafePropertyAccessString, Identifiers, makeBindingParser, ParsedHostBindings, ParseError, parseHostBindings, R3DependencyMetadata, R3DirectiveDef, R3DirectiveMetadata, R3FactoryTarget, R3QueryMetadata, R3ResolvedDependencyType, Statement, verifyHostBindings, WrappedNodeExpr} from '@angular/compiler';
import {emitDistinctChangesOnlyDefaultValue} from '@angular/compiler/src/core';
import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
@@ -436,6 +437,7 @@ export function extractQueryMetadata(
let read: Expression|null = null;
// The default value for descendants is true for every decorator except @ContentChildren.
let descendants: boolean = name !== 'ContentChildren';
let emitDistinctChangesOnly: boolean = emitDistinctChangesOnlyDefaultValue;
if (args.length === 2) {
const optionsExpr = unwrapExpression(args[1]);
if (!ts.isObjectLiteralExpression(optionsExpr)) {
@@ -458,6 +460,17 @@ export function extractQueryMetadata(
descendants = descendantsValue;
}
if (options.has('emitDistinctChangesOnly')) {
const emitDistinctChangesOnlyExpr = options.get('emitDistinctChangesOnly')!;
const emitDistinctChangesOnlyValue = evaluator.evaluate(emitDistinctChangesOnlyExpr);
if (typeof emitDistinctChangesOnlyValue !== 'boolean') {
throw createValueHasWrongTypeError(
emitDistinctChangesOnlyExpr, emitDistinctChangesOnlyValue,
`@${name} options.emitDistinctChangesOnlys must be a boolean`);
}
emitDistinctChangesOnly = emitDistinctChangesOnlyValue;
}
if (options.has('static')) {
const staticValue = evaluator.evaluate(options.get('static')!);
if (typeof staticValue !== 'boolean') {
@@ -480,6 +493,7 @@ export function extractQueryMetadata(
descendants,
read,
static: isStatic,
emitDistinctChangesOnly,
};
}
@@ -11,7 +11,7 @@ import * as ts from 'typescript';
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, NoopReferencesRegistry, PipeDecoratorHandler, ReferencesRegistry} from '../../annotations';
import {CycleAnalyzer, ImportGraph} from '../../cycles';
import {ErrorCode, ngErrorCode} from '../../diagnostics';
import {COMPILER_ERRORS_WITH_GUIDES, ERROR_DETAILS_PAGE_BASE_URL, ErrorCode, ngErrorCode} from '../../diagnostics';
import {checkForPrivateExports, ReferenceGraph} from '../../entry_point';
import {LogicalFileSystem, resolve} from '../../file_system';
import {AbsoluteModuleStrategy, AliasingHost, AliasStrategy, DefaultImportTracker, ImportRewriter, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NoopImportRewriter, PrivateExportAliasingHost, R3SymbolsImportRewriter, Reference, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesAliasingHost, UnifiedModulesStrategy} from '../../imports';
@@ -179,7 +179,8 @@ export class NgCompiler {
* Get all Angular-related diagnostics for this compilation.
*/
getDiagnostics(): ts.Diagnostic[] {
return [...this.getNonTemplateDiagnostics(), ...this.getTemplateDiagnostics()];
return this.addMessageTextDetails(
[...this.getNonTemplateDiagnostics(), ...this.getTemplateDiagnostics()]);
}
/**
@@ -188,10 +189,26 @@ export class NgCompiler {
* If a `ts.SourceFile` is passed, only diagnostics related to that file are returned.
*/
getDiagnosticsForFile(file: ts.SourceFile, optimizeFor: OptimizeFor): ts.Diagnostic[] {
return [
return this.addMessageTextDetails([
...this.getNonTemplateDiagnostics().filter(diag => diag.file === file),
...this.getTemplateDiagnosticsForFile(file, optimizeFor)
];
]);
}
/**
* Add Angular.io error guide links to diagnostics for this compilation.
*/
private addMessageTextDetails(diagnostics: ts.Diagnostic[]): ts.Diagnostic[] {
return diagnostics.map(diag => {
if (diag.code && COMPILER_ERRORS_WITH_GUIDES.has(ngErrorCode(diag.code))) {
return {
...diag,
messageText: diag.messageText +
`. Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/NG${ngErrorCode(diag.code)}`
};
}
return diag;
});
}
/**
@@ -7,5 +7,5 @@
*/
export {FatalDiagnosticError, isFatalDiagnosticError, makeDiagnostic, makeRelatedInformation} from './src/error';
export {ErrorCode, ngErrorCode} from './src/error_code';
export {COMPILER_ERRORS_WITH_GUIDES, ERROR_DETAILS_PAGE_BASE_URL, ErrorCode, ngErrorCode} from './src/error_code';
export {replaceTsWithNgInErrors} from './src/util';
@@ -161,6 +161,27 @@ export enum ErrorCode {
INJECTABLE_DUPLICATE_PROV = 9001,
}
/**
* @internal
* Base URL for the error details page.
* Keep this value in sync with a similar const in
* `packages/core/src/render3/error_code.ts`.
*/
export const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.io/errors';
/**
* @internal
* Contains a set of error messages that have detailed guides at angular.io.
* Full list of available error guides can be found at https://angular.io/errors
*/
export const COMPILER_ERRORS_WITH_GUIDES = new Set([
ErrorCode.DECORATOR_ARG_NOT_LITERAL,
ErrorCode.PARAM_MISSING_TOKEN,
ErrorCode.SCHEMA_INVALID_ELEMENT,
ErrorCode.SCHEMA_INVALID_ATTRIBUTE,
ErrorCode.MISSING_REFERENCE_TARGET,
]);
/**
* @internal
*/
@@ -10,7 +10,7 @@
/**
* A marker that indicates the start of a segment in a mapping.
*
* The end of a segment is indicated by the the first segment-marker of another mapping whose start
* The end of a segment is indicated by the first segment-marker of another mapping whose start
* is greater or equal to this one.
*/
export interface SegmentMarker {
@@ -64,7 +64,7 @@ interface EmitResolver {
* solves it conceptually the same way, but obviously doesn't need to access an `@internal` API.
*
* The set that is returned by this function is meant to be filled with import declaration nodes
* that have been referenced in a value-position by the transform, such the the installed patch can
* that have been referenced in a value-position by the transform, such the installed patch can
* ensure that those import declarations are not elided.
*
* See below. Note that this uses sourcegraph as the TypeScript checker file doesn't display on
@@ -32,7 +32,7 @@ import * as i0 from "@angular/core";
export class ViewQueryComponent {
}
ViewQueryComponent.ɵfac = function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); };
ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, descendants: true }, { propertyName: "someDirs", predicate: SomeDirective, descendants: true }], ngImport: i0, template: `
ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true }, { propertyName: "someDirs", predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true }], ngImport: i0, template: `
<div someDir></div>
`, isInline: true, directives: [{ type: i0.forwardRef(function () { return SomeDirective; }), selector: "[someDir]" }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ViewQueryComponent, [{
@@ -86,7 +86,7 @@ import * as i0 from "@angular/core";
export class ViewQueryComponent {
}
ViewQueryComponent.ɵfac = function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); };
ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], descendants: true }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], descendants: true }], ngImport: i0, template: `
ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], emitDistinctChangesOnly: false, descendants: true }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], emitDistinctChangesOnly: false, descendants: true }], ngImport: i0, template: `
<div #myRef></div>
<div #myRef1></div>
`, isInline: true });
@@ -166,7 +166,7 @@ import * as i0 from "@angular/core";
export class ViewQueryComponent {
}
ViewQueryComponent.ɵfac = function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); };
ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, descendants: true, static: true }, { propertyName: "foo", first: true, predicate: ["foo"], descendants: true }], ngImport: i0, template: `
ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true, static: true }, { propertyName: "foo", first: true, predicate: ["foo"], emitDistinctChangesOnly: false, descendants: true }], ngImport: i0, template: `
<div someDir></div>
`, isInline: true, directives: [{ type: i0.forwardRef(function () { return SomeDirective; }), selector: "[someDir]" }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ViewQueryComponent, [{
@@ -246,7 +246,7 @@ import * as i0 from "@angular/core";
export class ViewQueryComponent {
}
ViewQueryComponent.ɵfac = function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); };
ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], descendants: true, read: TemplateRef }, { propertyName: "someDir", first: true, predicate: SomeDirective, descendants: true, read: ElementRef }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], descendants: true, read: ElementRef }, { propertyName: "someDirs", predicate: SomeDirective, descendants: true, read: TemplateRef }], ngImport: i0, template: `
ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], emitDistinctChangesOnly: false, descendants: true, read: TemplateRef }, { propertyName: "someDir", first: true, predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true, read: ElementRef }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], emitDistinctChangesOnly: false, descendants: true, read: ElementRef }, { propertyName: "someDirs", predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true, read: TemplateRef }], ngImport: i0, template: `
<div someDir></div>
<div #myRef></div>
<div #myRef1></div>
@@ -336,7 +336,7 @@ import * as i0 from "@angular/core";
export class ContentQueryComponent {
}
ContentQueryComponent.ɵfac = function ContentQueryComponent_Factory(t) { return new (t || ContentQueryComponent)(); };
ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, descendants: true }, { propertyName: "someDirList", predicate: SomeDirective }], ngImport: i0, template: `
ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true }, { propertyName: "someDirList", predicate: SomeDirective, emitDistinctChangesOnly: false }], ngImport: i0, template: `
<div><ng-content></ng-content></div>
`, isInline: true });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ContentQueryComponent, [{
@@ -413,7 +413,7 @@ import * as i0 from "@angular/core";
export class ContentQueryComponent {
}
ContentQueryComponent.ɵfac = function ContentQueryComponent_Factory(t) { return new (t || ContentQueryComponent)(); };
ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], descendants: true }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"] }], ngImport: i0, template: `
ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], emitDistinctChangesOnly: false, descendants: true }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], emitDistinctChangesOnly: false }], ngImport: i0, template: `
<div #myRef></div>
<div #myRef1></div>
`, isInline: true });
@@ -493,7 +493,7 @@ import * as i0 from "@angular/core";
export class ContentQueryComponent {
}
ContentQueryComponent.ɵfac = function ContentQueryComponent_Factory(t) { return new (t || ContentQueryComponent)(); };
ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, descendants: true, static: true }, { propertyName: "foo", first: true, predicate: ["foo"], descendants: true }], ngImport: i0, template: `
ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true, static: true }, { propertyName: "foo", first: true, predicate: ["foo"], emitDistinctChangesOnly: false, descendants: true }], ngImport: i0, template: `
<div><ng-content></ng-content></div>
`, isInline: true });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ContentQueryComponent, [{
@@ -596,7 +596,7 @@ import * as i0 from "@angular/core";
export class ContentQueryComponent {
}
ContentQueryComponent.ɵfac = function ContentQueryComponent_Factory(t) { return new (t || ContentQueryComponent)(); };
ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], descendants: true, read: TemplateRef }, { propertyName: "someDir", first: true, predicate: SomeDirective, descendants: true, read: ElementRef }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], read: ElementRef }, { propertyName: "someDirs", predicate: SomeDirective, read: TemplateRef }], ngImport: i0, template: `
ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], emitDistinctChangesOnly: false, descendants: true, read: TemplateRef }, { propertyName: "someDir", first: true, predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true, read: ElementRef }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], emitDistinctChangesOnly: false, read: ElementRef }, { propertyName: "someDirs", predicate: SomeDirective, emitDistinctChangesOnly: false, read: TemplateRef }], ngImport: i0, template: `
<div someDir></div>
<div #myRef></div>
<div #myRef1></div>
@@ -652,3 +652,91 @@ export declare class MyModule {
static ɵinj: i0.ɵɵInjectorDef<MyModule>;
}
/****************************************************************************************************
* PARTIAL FILE: some.directive.js
****************************************************************************************************/
import { Directive } from '@angular/core';
import * as i0 from "@angular/core";
export class SomeDirective {
}
SomeDirective.ɵfac = function SomeDirective_Factory(t) { return new (t || SomeDirective)(); };
SomeDirective.ɵdir = i0.ɵɵngDeclareDirective({ version: "0.0.0-PLACEHOLDER", type: SomeDirective, selector: "[someDir]", ngImport: i0 });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SomeDirective, [{
type: Directive,
args: [{
selector: '[someDir]',
}]
}], null, null); })();
/****************************************************************************************************
* PARTIAL FILE: some.directive.d.ts
****************************************************************************************************/
import * as i0 from "@angular/core";
export declare class SomeDirective {
static ɵfac: i0.ɵɵFactoryDef<SomeDirective, never>;
static ɵdir: i0.ɵɵDirectiveDefWithMeta<SomeDirective, "[someDir]", never, {}, {}, never>;
}
/****************************************************************************************************
* PARTIAL FILE: query_with_emit_distinct_changes_only.js
****************************************************************************************************/
import { Component, ContentChildren, NgModule, ViewChildren } from '@angular/core';
import { SomeDirective } from './some.directive';
import * as i0 from "@angular/core";
export class ContentQueryComponent {
}
ContentQueryComponent.ɵfac = function ContentQueryComponent_Factory(t) { return new (t || ContentQueryComponent)(); };
ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "myRefs", predicate: ["myRef"] }, { propertyName: "oldMyRefs", predicate: ["myRef"], emitDistinctChangesOnly: false }], viewQueries: [{ propertyName: "someDirs", predicate: SomeDirective, descendants: true }, { propertyName: "oldSomeDirs", predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true }], ngImport: i0, template: `
<div someDir></div>
<div #myRef></div>
`, isInline: true });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ContentQueryComponent, [{
type: Component,
args: [{
selector: 'content-query-component',
template: `
<div someDir></div>
<div #myRef></div>
`
}]
}], null, { myRefs: [{
type: ContentChildren,
args: ['myRef', { emitDistinctChangesOnly: true }]
}], oldMyRefs: [{
type: ContentChildren,
args: ['myRef', { emitDistinctChangesOnly: false }]
}], someDirs: [{
type: ViewChildren,
args: [SomeDirective, { emitDistinctChangesOnly: true }]
}], oldSomeDirs: [{
type: ViewChildren,
args: [SomeDirective, { emitDistinctChangesOnly: false }]
}] }); })();
export class MyModule {
}
MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule });
MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } });
(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [ContentQueryComponent] }); })();
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{
type: NgModule,
args: [{ declarations: [ContentQueryComponent] }]
}], null, null); })();
/****************************************************************************************************
* PARTIAL FILE: query_with_emit_distinct_changes_only.d.ts
****************************************************************************************************/
import { ElementRef, QueryList } from '@angular/core';
import * as i0 from "@angular/core";
export declare class ContentQueryComponent {
myRefs: QueryList<ElementRef>;
oldMyRefs: QueryList<ElementRef>;
someDirs: QueryList<any>;
oldSomeDirs: QueryList<any>;
static ɵfac: i0.ɵɵFactoryDef<ContentQueryComponent, never>;
static ɵcmp: i0.ɵɵComponentDefWithMeta<ContentQueryComponent, "content-query-component", never, {}, {}, ["myRefs", "oldMyRefs"], never>;
}
export declare class MyModule {
static ɵmod: i0.ɵɵNgModuleDefWithMeta<MyModule, [typeof ContentQueryComponent], never, never>;
static ɵinj: i0.ɵɵInjectorDef<MyModule>;
}
@@ -118,6 +118,21 @@
]
}
]
},
{
"description": "should support query emitDistinctChangesOnly flag",
"inputFiles": [
"some.directive.ts",
"query_with_emit_distinct_changes_only.ts"
],
"expectations": [
{
"failureMessage": "Invalid ContentQuery declaration",
"files": [
"query_with_emit_distinct_changes_only.js"
]
}
]
}
]
}
@@ -3,8 +3,8 @@ ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
selectors: [["content-query-component"]],
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
if (rf & 1) {
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, true);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, false);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 1);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 0);
}
if (rf & 2) {
let $tmp$;
@@ -5,8 +5,8 @@ ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
// ...
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
if (rf & 1) {
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, true);
$r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, false);
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 1);
$r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 0);
}
if (rf & 2) {
let $tmp$;
@@ -5,10 +5,10 @@ ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
// ...
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
if (rf & 1) {
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, true, TemplateRef);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, true, ElementRef);
$r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, false, ElementRef);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, false, TemplateRef);
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 1, TemplateRef);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 1, ElementRef);
$r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 0, ElementRef);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 0, TemplateRef);
}
if (rf & 2) {
let $tmp$;
@@ -0,0 +1,29 @@
const $e0_attrs$ = ["myRef"];
// ...
ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
// ...
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
if (rf & 1) {
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, __QueryFlags.emitDistinctChangesOnly__);
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, __QueryFlags.none__);
}
if (rf & 2) {
let $tmp$;
$r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.myRefs = $tmp$);
$r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.oldMyRefs = $tmp$);
}
},
// ...
viewQuery: function ContentQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.emitDistinctChangesOnly__|__QueryFlags.descendants__);
$r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.descendants__);
}
if (rf & 2) {
let $tmp$;
$r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.someDirs = $tmp$);
$r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.oldSomeDirs = $tmp$);
}
},
//...
});
@@ -0,0 +1,21 @@
import {Component, ContentChildren, ElementRef, NgModule, QueryList, TemplateRef, ViewChildren} from '@angular/core';
import {SomeDirective} from './some.directive';
@Component({
selector: 'content-query-component',
template: `
<div someDir></div>
<div #myRef></div>
`
})
export class ContentQueryComponent {
@ContentChildren('myRef', {emitDistinctChangesOnly: true}) myRefs!: QueryList<ElementRef>;
@ContentChildren('myRef', {emitDistinctChangesOnly: false}) oldMyRefs!: QueryList<ElementRef>;
@ViewChildren(SomeDirective, {emitDistinctChangesOnly: true}) someDirs!: QueryList<any>;
@ViewChildren(SomeDirective, {emitDistinctChangesOnly: false}) oldSomeDirs!: QueryList<any>;
}
@NgModule({declarations: [ContentQueryComponent]})
export class MyModule {
}
@@ -3,8 +3,9 @@ ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
selectors: [["content-query-component"]],
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
if (rf & 1) {
$r3$.ɵɵstaticContentQuery(dirIndex, SomeDirective, true);
$r3$.ɵɵcontentQuery(dirIndex, $ref0$, true);
$r3$.ɵɵcontentQuery(
dirIndex, SomeDirective, __QueryFlags.isStatic__|__QueryFlags.descendants__);
$r3$.ɵɵcontentQuery(dirIndex, $ref0$, 1);
}
if (rf & 2) {
let $tmp$;
@@ -5,8 +5,8 @@ ViewQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
selectors: [["view-query-component"]],
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵstaticViewQuery(SomeDirective, true);
$r3$.ɵɵviewQuery($refs$, true);
$r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.isStatic__|__QueryFlags.descendants__);
$r3$.ɵɵviewQuery($refs$, 1);
}
if (rf & 2) {
let $tmp$;
@@ -3,8 +3,8 @@ ViewQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
selectors: [["view-query-component"]],
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵviewQuery(SomeDirective, true);
$r3$.ɵɵviewQuery(SomeDirective, true);
$r3$.ɵɵviewQuery(SomeDirective, 1);
$r3$.ɵɵviewQuery(SomeDirective, 1);
}
if (rf & 2) {
let $tmp$;
@@ -5,8 +5,8 @@ ViewQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
// ...
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵviewQuery($e0_attrs$, true);
$r3$.ɵɵviewQuery($e1_attrs$, true);
$r3$.ɵɵviewQuery($e0_attrs$, 1);
$r3$.ɵɵviewQuery($e1_attrs$, 1);
}
if (rf & 2) {
let $tmp$;
@@ -5,10 +5,10 @@ ViewQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
// ...
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵviewQuery($e0_attrs$, true, TemplateRef);
$r3$.ɵɵviewQuery(SomeDirective, true, ElementRef);
$r3$.ɵɵviewQuery($e1_attrs$, true, ElementRef);
$r3$.ɵɵviewQuery(SomeDirective, true, TemplateRef);
$r3$.ɵɵviewQuery($e0_attrs$, 1, TemplateRef);
$r3$.ɵɵviewQuery(SomeDirective, 1, ElementRef);
$r3$.ɵɵviewQuery($e1_attrs$, 1, ElementRef);
$r3$.ɵɵviewQuery(SomeDirective, 1, TemplateRef);
}
if (rf & 2) {
let $tmp$;
@@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AttributeMarker, SelectorFlags} from '@angular/compiler/src/core';
import {QueryFlags} from '@angular/compiler/src/render3/view/compiler';
import {i18nIcuMsg, i18nMsg, i18nMsgWithPostprocess, Placeholder} from './i18n_helpers';
const EXPECTED_FILE_MACROS: [RegExp, (...args: string[]) => string][] = [
@@ -35,6 +36,9 @@ const EXPECTED_FILE_MACROS: [RegExp, (...args: string[]) => string][] = [
// E.g. `__SelectorFlags.ELEMENT__`
flagUnion(/__SelectorFlags\.([^_]+)__/, (_match, member) => getSelectorFlag(member)),
// E.g. `__QueryFlags.ELEMENT__`
flagUnion(/__QueryFlags\.([^_]+)__/, (_match, member) => getQueryFlag(member)),
];
/**
@@ -107,6 +111,21 @@ function getSelectorFlag(member: string): number {
return marker;
}
const QueryFlagsMap: Record<string, QueryFlags> = {
none: QueryFlags.none,
descendants: QueryFlags.descendants,
isStatic: QueryFlags.isStatic,
emitDistinctChangesOnly: QueryFlags.emitDistinctChangesOnly,
};
function getQueryFlag(member: string): number {
const marker = QueryFlagsMap[member];
if (typeof marker !== 'number') {
throw new Error('Unknown SelectorFlag: ' + member);
}
return marker;
}
function stringParam() {
return /'([^']*?[^\\])'/;
}
@@ -1636,8 +1636,8 @@ describe('compiler compliance', () => {
selectors: [["view-query-component"]],
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵviewQuery(SomeDirective, true);
$r3$.ɵɵviewQuery(SomeDirective, true);
$r3$.ɵɵviewQuery(SomeDirective, 1);
$r3$.ɵɵviewQuery(SomeDirective, 1);
}
if (rf & 2) {
let $tmp$;
@@ -1695,8 +1695,8 @@ describe('compiler compliance', () => {
…
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵviewQuery($e0_attrs$, true);
$r3$.ɵɵviewQuery($e1_attrs$, true);
$r3$.ɵɵviewQuery($e0_attrs$, 1);
$r3$.ɵɵviewQuery($e1_attrs$, 1);
}
if (rf & 2) {
let $tmp$;
@@ -1746,8 +1746,8 @@ describe('compiler compliance', () => {
selectors: [["view-query-component"]],
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵstaticViewQuery(SomeDirective, true);
$r3$.ɵɵviewQuery($refs$, true);
$r3$.ɵɵviewQuery(SomeDirective, 3);
$r3$.ɵɵviewQuery($refs$, 1);
}
if (rf & 2) {
let $tmp$;
@@ -1810,10 +1810,10 @@ describe('compiler compliance', () => {
…
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵviewQuery($e0_attrs$, true, TemplateRef);
$r3$.ɵɵviewQuery(SomeDirective, true, ElementRef);
$r3$.ɵɵviewQuery($e1_attrs$, true, ElementRef);
$r3$.ɵɵviewQuery(SomeDirective, true, TemplateRef);
$r3$.ɵɵviewQuery($e0_attrs$, 1, TemplateRef);
$r3$.ɵɵviewQuery(SomeDirective, 1, ElementRef);
$r3$.ɵɵviewQuery($e1_attrs$, 1, ElementRef);
$r3$.ɵɵviewQuery(SomeDirective, 1, TemplateRef);
}
if (rf & 2) {
let $tmp$;
@@ -1873,8 +1873,8 @@ describe('compiler compliance', () => {
selectors: [["content-query-component"]],
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
if (rf & 1) {
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, true);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, false);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 1);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 0);
}
if (rf & 2) {
let $tmp$;
@@ -1933,8 +1933,8 @@ describe('compiler compliance', () => {
…
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
if (rf & 1) {
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, true);
$r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, false);
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 1);
$r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 0);
}
if (rf & 2) {
let $tmp$;
@@ -1992,8 +1992,8 @@ describe('compiler compliance', () => {
selectors: [["content-query-component"]],
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
if (rf & 1) {
$r3$.ɵɵstaticContentQuery(dirIndex, SomeDirective, true);
$r3$.ɵɵcontentQuery(dirIndex, $ref0$, true);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 3);
$r3$.ɵɵcontentQuery(dirIndex, $ref0$, 1);
}
if (rf & 2) {
let $tmp$;
@@ -2057,10 +2057,10 @@ describe('compiler compliance', () => {
…
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
if (rf & 1) {
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, true, TemplateRef);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, true, ElementRef);
$r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, false, ElementRef);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, false, TemplateRef);
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 1, TemplateRef);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 1, ElementRef);
$r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 0, ElementRef);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 0, TemplateRef);
}
if (rf & 2) {
let $tmp$;
+22 -22
View File
@@ -22,14 +22,14 @@ const trim = (input: string): string => input.replace(/\s+/g, ' ').trim();
const varRegExp = (name: string): RegExp => new RegExp(`var \\w+ = \\[\"${name}\"\\];`);
const viewQueryRegExp = (predicate: string, descend: boolean, ref?: string): RegExp => {
const viewQueryRegExp = (predicate: string, flags: number, ref?: string): RegExp => {
const maybeRef = ref ? `, ${ref}` : ``;
return new RegExp(`i0\\.ɵɵviewQuery\\(${predicate}, ${descend}${maybeRef}\\)`);
return new RegExp(`i0\\.ɵɵviewQuery\\(${predicate}, ${flags}${maybeRef}\\)`);
};
const contentQueryRegExp = (predicate: string, descend: boolean, ref?: string): RegExp => {
const contentQueryRegExp = (predicate: string, flags: number, ref?: string): RegExp => {
const maybeRef = ref ? `, ${ref}` : ``;
return new RegExp(`i0\\.ɵɵcontentQuery\\(dirIndex, ${predicate}, ${descend}${maybeRef}\\)`);
return new RegExp(`i0\\.ɵɵcontentQuery\\(dirIndex, ${predicate}, ${flags}${maybeRef}\\)`);
};
const setClassMetadataRegExp = (expectedType: string): RegExp =>
@@ -3093,10 +3093,10 @@ runInEachFileSystem(os => {
expect(jsContents).toMatch(varRegExp('test1'));
expect(jsContents).toMatch(varRegExp('test2'));
expect(jsContents).toMatch(varRegExp('accessor'));
// match `i0.ɵɵcontentQuery(dirIndex, _c1, true, TemplateRef)`
expect(jsContents).toMatch(contentQueryRegExp('\\w+', true, 'TemplateRef'));
// match `i0.ɵɵviewQuery(_c2, true, null)`
expect(jsContents).toMatch(viewQueryRegExp('\\w+', true));
// match `i0.ɵɵcontentQuery(dirIndex, _c1, 1, TemplateRef)`
expect(jsContents).toMatch(contentQueryRegExp('\\w+', 1, 'TemplateRef'));
// match `i0.ɵɵviewQuery(_c2, 1, null)`
expect(jsContents).toMatch(viewQueryRegExp('\\w+', 1));
});
it('should generate queries for directives', () => {
@@ -3125,14 +3125,14 @@ runInEachFileSystem(os => {
expect(jsContents).toMatch(varRegExp('test1'));
expect(jsContents).toMatch(varRegExp('test2'));
expect(jsContents).toMatch(varRegExp('accessor'));
// match `i0.ɵɵcontentQuery(dirIndex, _c1, true, TemplateRef)`
expect(jsContents).toMatch(contentQueryRegExp('\\w+', true, 'TemplateRef'));
// match `i0.ɵɵcontentQuery(dirIndex, _c1, 1, TemplateRef)`
expect(jsContents).toMatch(contentQueryRegExp('\\w+', 1, 'TemplateRef'));
// match `i0.ɵɵviewQuery(_c2, true)`
// match `i0.ɵɵviewQuery(_c2, 1)`
// Note that while ViewQuery doesn't necessarily make sense on a directive,
// because it doesn't have a view, we still need to handle it because a component
// could extend the directive.
expect(jsContents).toMatch(viewQueryRegExp('\\w+', true));
expect(jsContents).toMatch(viewQueryRegExp('\\w+', 1));
});
it('should handle queries that use forwardRef', () => {
@@ -3154,13 +3154,13 @@ runInEachFileSystem(os => {
env.driveMain();
const jsContents = env.getContents('test.js');
// match `i0.ɵɵcontentQuery(dirIndex, TemplateRef, true, null)`
expect(jsContents).toMatch(contentQueryRegExp('TemplateRef', true));
// match `i0.ɵɵcontentQuery(dirIndex, ViewContainerRef, true, null)`
expect(jsContents).toMatch(contentQueryRegExp('ViewContainerRef', true));
// match `i0.ɵɵcontentQuery(dirIndex, _c0, true, null)`
// match `i0.ɵɵcontentQuery(dirIndex, TemplateRef, 1, null)`
expect(jsContents).toMatch(contentQueryRegExp('TemplateRef', 1));
// match `i0.ɵɵcontentQuery(dirIndex, ViewContainerRef, 1, null)`
expect(jsContents).toMatch(contentQueryRegExp('ViewContainerRef', 1));
// match `i0.ɵɵcontentQuery(dirIndex, _c0, 1, null)`
expect(jsContents).toContain('_c0 = ["parens"];');
expect(jsContents).toMatch(contentQueryRegExp('_c0', true));
expect(jsContents).toMatch(contentQueryRegExp('_c0', 1));
});
it('should handle queries that use an InjectionToken', () => {
@@ -3181,10 +3181,10 @@ runInEachFileSystem(os => {
env.driveMain();
const jsContents = env.getContents('test.js');
// match `i0.ɵɵviewQuery(TOKEN, true, null)`
expect(jsContents).toMatch(viewQueryRegExp('TOKEN', true));
// match `i0.ɵɵcontentQuery(dirIndex, TOKEN, true, null)`
expect(jsContents).toMatch(contentQueryRegExp('TOKEN', true));
// match `i0.ɵɵviewQuery(TOKEN, 1, null)`
expect(jsContents).toMatch(viewQueryRegExp('TOKEN', 1));
// match `i0.ɵɵcontentQuery(dirIndex, TOKEN, 1, null)`
expect(jsContents).toMatch(contentQueryRegExp('TOKEN', 1));
});
it('should compile expressions that write keys', () => {

Some files were not shown because too many files have changed in this diff Show More