Compare commits

...

40 Commits

Author SHA1 Message Date
Andrew Scott cdc8c7cad0 release: cut the v12.2.0 release (#43047) 2021-08-04 10:24:49 -07:00
Kristiyan Kostadinov bd7f0d8b70 fix(core): incorrect error reported when trying to re-create view which had an error during creation (#43005)
Currently if a view throws an error during creation mode, we mark it as `incompleteFirstPass` so that we can try to recover later. The recovery is only possible inside component views.

The problem is that when this was introduced, I forgot to flip the `firstCreatePass` when an error is thrown which meant that calling `renderView` on the same component again is allowed. It will eventually hit an assertion which can be confusing for the end user. This issue only manifests itself when rendering views "manually" through `ViewContainerRef` (e.g. using `NgIf`).

These changes flip the `firstCreatePass` back to false on errors so that trying to re-render the same view will throw an error which is consistent to the one that broke the view during creation.

Fixes #41383.

PR Close #43005
2021-08-04 08:58:29 -07:00
atscott 3a9ce61ea4 Revert "refactor(ngcc): remove unused import (#42062)" (#43033)
This reverts commit 42265cca1c.

PR Close #43033
2021-08-03 15:39:00 -07:00
atscott ae98b24eb7 Revert "test(compiler-cli): clarify source-map expectations in compliance tests (#42062)" (#43033)
This reverts commit 29f9888a98.

PR Close #43033
2021-08-03 15:39:00 -07:00
atscott 4997a3f692 Revert "refactor(compiler): remove cyclic dependencies (#42062)" (#43033)
This reverts commit 75855196e3.

PR Close #43033
2021-08-03 15:38:59 -07:00
atscott 6a59e6b009 Revert "refactor(compiler): support interpolation tokens when lexing markup (#42062)" (#43033)
This reverts commit c8a46bfdcd.

PR Close #43033
2021-08-03 15:38:59 -07:00
atscott 6f80c55dbd Revert "refactor(compiler): share isQuote() via chars.ts (#42062)" (#43033)
This reverts commit 3d3b69ff81.

PR Close #43033
2021-08-03 15:38:59 -07:00
atscott 07846e550c Revert "refactor(compiler): support interpolation tokens when lexing attribute values (#42062)" (#43033)
This reverts commit c516e252fc.

PR Close #43033
2021-08-03 15:38:59 -07:00
atscott f18131f372 Revert "refactor(compiler): support encoded entity tokens when lexing markup (#42062)" (#43033)
This reverts commit 942b24d5ea.

PR Close #43033
2021-08-03 15:38:59 -07:00
atscott f24b94f5d9 Revert "refactor(compiler): expose token parts in Text nodes (#42062)" (#43033)
This reverts commit 8a54896a91.

PR Close #43033
2021-08-03 15:38:59 -07:00
atscott 6caafc2062 Revert "test(compiler): check fullStart source-span (#42062)" (#43033)
This reverts commit 973f9b8d19.

PR Close #43033
2021-08-03 15:38:59 -07:00
atscott f883aaeb89 Revert "fix(compiler): include leading whitespace in source-spans of i18n messages (#42062)" (#43033)
This reverts commit f08516db09.

PR Close #43033
2021-08-03 15:38:59 -07:00
atscott 2215af252c Revert "refactor(compiler): define interfaces for each lexer token (#42062)" (#43033)
This reverts commit 9b3d4f5575.

PR Close #43033
2021-08-03 15:38:59 -07:00
atscott db4147b4d2 Revert "test(compiler): check that the parser supports prematurely terminated interpolations (#42062)" (#43033)
This reverts commit 11ebe21d0d.

PR Close #43033
2021-08-03 15:38:59 -07:00
atscott 2dc6efed4e Revert "refactor(compiler): use === rather than == in the ml_parser (#42062)" (#43033)
This reverts commit 28b0c45fde.

PR Close #43033
2021-08-03 15:38:58 -07:00
atscott 1d307bc5bf Revert "test(compiler): add a test for parsing multiline expressions in attributes (#42062)" (#43033)
This reverts commit fe12651580.

PR Close #43033
2021-08-03 15:38:58 -07:00
ivanwonder aace1e71d8 fix(language-service): global autocomplete doesn't work when the user tries to modify the symbol (#42923)
When the user tries to trigger suggestions from an interruption,
the LS should provide the global completions. For example,
`[input]="t¦"`, the `t` can be the `true` or the symbol from
the component context.

PR Close #42923
2021-08-02 17:08:30 -07:00
dario-piotrowicz fbfec946a7 docs: amend links in content-projection guide (#43009)
PR Close #43009
2021-08-02 15:36:52 -07:00
Kristiyan Kostadinov 0e47334db8 refactor(core): update peerDependencies to allow rxjs7 (#42991)
We can't update the framework to rxjs7 until version 13, because it contains breaking changes, but we can allow users to opt into it since all of our code should be compatible.

These changes expand the allowed version range of rxjs and add an integration test to verify that we don't get compilation errors. Note that we also have a test that runs the AIO examples against rxjs 7 already (#42660).

Fixes #41897.

PR Close #42991
2021-08-02 13:55:01 -07:00
Pete Bacon Darwin e4f62c12f5 test(compiler): add a test for parsing multiline expressions in attributes (#42062)
This tests a scenario that was failing in an internal project.

PR Close #42062
2021-08-02 09:53:18 -07:00
Pete Bacon Darwin ea3a14805e refactor(compiler): use === rather than == in the ml_parser (#42062)
This is a simple tidy up commit to move to the more specific `===`
comparison operator in the HTML lexer/parser.

PR Close #42062
2021-08-02 09:53:18 -07:00
Pete Bacon Darwin a96f9c91a2 test(compiler): check that the parser supports prematurely terminated interpolations (#42062)
Such interpolations turned up during internal testing at Google, so this
commit adds a test to prevent regressions.

PR Close #42062
2021-08-02 09:53:18 -07:00
Pete Bacon Darwin a8c0f4f1ba refactor(compiler): define interfaces for each lexer token (#42062)
These token interfaces will make it easier to reason about tokens in the
parser and in specs.

Previously, it was never clear what items could appear in the `parts`
array of a token given a particular `TokenType`. Now, each token interface
declares a labelled tuple for the parts, which helps to document the token
better.

PR Close #42062
2021-08-02 09:53:18 -07:00
Pete Bacon Darwin 11ef03b36f fix(compiler): include leading whitespace in source-spans of i18n messages (#42062)
Previously, the way templates were tokenized meant that we lost information
about the location of interpolations if the template contained encoded HTML
entities. This meant that the mapping back to the source interpolated strings
could be offset incorrectly.

Also, the source-span assigned to an i18n message did not include leading
whitespace. This confused the output source-mappings so that the first text
nodes of the message stopped at the first non-whitespace character.

This commit makes use of the previous refactorings, where more fine grain
information was provided in text tokens, to enable the parser to identify
the location of the interpolations in the original source more accurately.

Fixes #41034

PR Close #42062
2021-08-02 09:53:18 -07:00
Pete Bacon Darwin 8e798effe4 test(compiler): check fullStart source-span (#42062)
The tests were checking that the source-span of parsed HTML nodes were
accurate, but they were not checking the span when it includes the
"leading trivia", which are given by the `fullStart` rather than `start`
location.

PR Close #42062
2021-08-02 09:53:18 -07:00
Pete Bacon Darwin 54f73530a8 refactor(compiler): expose token parts in Text nodes (#42062)
When it was tokenized, text content is split into parts that can include
interpolations and encoded entities tokens.

To make this information available to downstream processing, this commit
adds these tokens to the `Text` AST nodes, with suitable processing.

PR Close #42062
2021-08-02 09:53:18 -07:00
Pete Bacon Darwin 61080ea4bc refactor(compiler): support encoded entity tokens when lexing markup (#42062)
The lexer now splits encoded entity tokens out from text and attribute value tokens.

Previously encoded entities would be decoded and the decoded value would be
included as part of the text token of the surrounding text. Now the entities
have their own tokens. There are two scenarios: text and attribute values.

Previously the contents of `<div>Hello &amp; goodbye</div>` would be a single
TEXT token. Now it will be three tokens:

```
TEXT: "Hello "
ENCODED_ENTITY: "&", "&amp;"
TEXT: " goodbye"
```

Previously the attribute value in `<div title="Hello &amp; goodbye">` would be
a single text token. Now it will be three tokens:

```
ATTR_VALUE_TEXT: "Hello "
ENCODED_ENTITY: "&", "&amp;"
ATTR_VALUE_TEXT: " goodbye"
```

- ENCODED_ENTITY tokens have two parts: "decoded" and "encoded".
- ENCODED_ENTITY tokens are always preceded and followed by either TEXT tokens
  or ATTR_VALUE_TEXT tokens, depending upon the context, even if they represent
  an empty string.

The HTML parser has been modified to recombine these tokens to allow this
refactoring to have limited effect in this commit. Further refactorings
to use these new tokens will follow in subsequent commits.

PR Close #42062
2021-08-02 09:53:18 -07:00
Pete Bacon Darwin 4116d5d18f refactor(compiler): support interpolation tokens when lexing attribute values (#42062)
The lexer now splits interpolation tokens out from attribute value tokens.
Previously the attribute value of `<div attr="Hello, {{ name}}">` would be a single
token. Now it will be three tokens:

```
ATTR_VALUE_TEXT: "Hello, "
ATTR_VALUE_INTERPOLATION: "{{", " name", "}}"
ATTR_VALUE_TEXT: ""
```

- ATTR_VALUE_INTERPOLATION tokens have three parts, "start marker",
  "expression" and "end marker".
- ATTR_VALUE_INTERPOLATION tokens are always preceded and followed
  by TEXT tokens, even if they represent an empty string.

The HTML parser has been modified to recombine these tokens to allow this
refactoring to have limited effect in this commit. Further refactorings
to use these new tokens will follow in subsequent commits.

PR Close #42062
2021-08-02 09:53:18 -07:00
Pete Bacon Darwin 5572278826 refactor(compiler): share isQuote() via chars.ts (#42062)
This function is general purpose and by moving it into the
`chars.ts` file along with similar helpers, it can be reused
in the lexer, for instance.

PR Close #42062
2021-08-02 09:53:18 -07:00
Pete Bacon Darwin b442c4991d refactor(compiler): support interpolation tokens when lexing markup (#42062)
The lexer now splits interpolation tokens out from text tokens.

Previously the contents of `<div>Hello, {{ name}}<div>` would be a single
text token. Now it will be three tokens:

```
TEXT: "Hello, "
INTERPOLATION: "{{", " name", "}}"
TEXT: ""
```

- INTERPOLATION tokens have three parts, "start marker", "expression"
  and "end marker".
- INTERPOLATION tokens are always preceded and followed by TEXT tokens,
  even if they represent an empty string.

The HTML parser has been modified to recombine these tokens to allow this
refactoring to have limited effect in this commit. Further refactorings
to use these new tokens will follow in subsequent commits.

PR Close #42062
2021-08-02 09:53:18 -07:00
Pete Bacon Darwin 014763e42c refactor(compiler): remove cyclic dependencies (#42062)
This commit removes 9 cycles in the dependency graph of the compiler code.

PR Close #42062
2021-08-02 09:53:17 -07:00
Pete Bacon Darwin 86cfbf8c8a test(compiler-cli): clarify source-map expectations in compliance tests (#42062)
The compliance tests can check source-map segments against expectations
encoded into the expectation files. Previously, the encoding of the expected
segment was only delimited by whitespace, but this made it difficult to identify
segments that started or ended with whitespace.

Now these segment expectations are wrapped in double-quotes which makes
it easier to read and understand the expectation files.

PR Close #42062
2021-08-02 09:53:17 -07:00
Pete Bacon Darwin 3b92b4abd3 refactor(ngcc): remove unused import (#42062)
This import is not used in the file, so can be removed.

PR Close #42062
2021-08-02 09:53:17 -07:00
Alex Rickabaugh dd6e80aac5 refactor(bazel): extract function to patch fileNameToModuleName on host (#42974)
This commit extracts the patching operation that adds `fileNameToModuleName`
to the Angular compiler's `ts.CompilerHost` into a separate function, so
that it can be invoked in other compilation flows besides the one outlined
in `ngc-wrapped`. This is primarily needed for the xi18n operation in g3.

PR Close #42974
2021-07-30 09:29:45 -07:00
Alex Rickabaugh da50b9b5e7 refactor(compiler): remove unnecessary escapes (#42990)
This commit is part of a larger scale change to eliminate unnecessary
escapes in string literals, in advance of enabling stricter checks in
`ts_library` rules in g3.

PR Close #42990
2021-07-30 09:28:46 -07:00
Paul Gschwendtner 86e4b3cdf9 fix(dev-infra): browser archive rule should handle .dmg files (#42992)
We recently reworked our browser archive extraction to happen
at analysis time for better caching. This resulted in us breaking
the extraction of macOS dmg files so that Firefox is currently
not usable for local testing on macOS. We implement a similar special
logic for `.dmg` files to what has been done within the Bazel
webtesting rules.

PR Close #42992
2021-07-30 09:28:08 -07:00
Nichola Alkhouri 3fddcb5364 docs: Fix wrongly provided/Injected AnimalService (#42988)
In the logical tree example which demonstrate the use of `@SkipSelf` and  `@Host`, the provided and injected `AnimalService` are reversed.
PR Close #42988
2021-07-29 15:05:53 -07:00
Joe Martin d5cc87dc88 docs: Add josmar-crwdstffng to contributors list. (#42972)
Add josmar-crwdstffng to list in contributors.json.  Add josephmmartin
image file.

PR Close #42972
2021-07-28 15:56:02 -07:00
Joey Perrott b075721a49 fix(dev-infra): ensure that building environment stamp fails silently (#42985)
Previously when a failure occurred in part of building the environment stamp, the entire
process errored.  This should instead fail silently providing no value for the stamp.

PR Close #42985
2021-07-28 13:00:09 -07:00
Joey Perrott 3346cb6e4e release: cut the v12.2.0-rc.0 release (#42983) 2021-07-28 11:51:31 -07:00
29 changed files with 514 additions and 194 deletions
+43
View File
@@ -1,3 +1,46 @@
<a name="12.2.0"></a>
# 12.2.0 (2021-08-04)
### core
| Commit | Description |
| -- | -- |
| [bd7f0d8b70](https://github.com/angular/angular/commit/bd7f0d8b70150a7e4e7550f4380a2e731cf2b942) | fix(core): incorrect error reported when trying to re-create view which had an error during creation ([#43005](https://github.com/angular/angular/pull/43005)) |
### language-service
| Commit | Description |
| -- | -- |
| [aace1e71d8](https://github.com/angular/angular/commit/aace1e71d8fb5e1e52fcd86e2a26892d1016c43e) | fix(language-service): global autocomplete doesn't work when the user tries to modify the symbol ([#42923](https://github.com/angular/angular/pull/42923)) |
## Special Thanks:
Alex Rickabaugh, Joe Martin, Joey Perrott, Kristiyan Kostadinov, Nichola Alkhouri, Paul Gschwendtner, Pete Bacon Darwin, atscott, dario-piotrowicz and ivanwonder
<a name="12.2.0-rc.0"></a>
# 12.2.0-rc.0 (2021-07-28)
### compiler-cli
| Commit | Description |
| -- | -- |
| [ed9cfb674f](https://github.com/angular/angular/commit/ed9cfb674f8e52b416ccdaf9aa9c46955b8448f5) | fix(compiler-cli): use correct module resolution context for absolute imports in .d.ts files ([#42879](https://github.com/angular/angular/pull/42879)) |
| [5fb23eccea](https://github.com/angular/angular/commit/5fb23ecceaccf0629308dd50210b65f67d51f024) | perf(compiler-cli): skip analysis in incremental builds for files without Angular behavior ([#42562](https://github.com/angular/angular/pull/42562)) |
### core
| Commit | Description |
| -- | -- |
| [eefe1682e8](https://github.com/angular/angular/commit/eefe1682e8099b73b6e50bb227b5a7f63105c63d) | fix(core): correctly handle `null` or `undefined` in `ErrorHandler#handleError()` ([#42881](https://github.com/angular/angular/pull/42881)) |
### forms
| Commit | Description |
| -- | -- |
| [1d9d02696e](https://github.com/angular/angular/commit/1d9d02696eadbee2c2f719e432efca22f1e494e9) | feat(forms): add hasValidators, addValidators, and removeValidators methods (for both sync and async) ([#42838](https://github.com/angular/angular/pull/42838)) |
| [a502279592](https://github.com/angular/angular/commit/a50227959222f39884aac284544d1626aee5ca64) | feat(forms): allow minLength/maxLength validator to be bound to `null` ([#42565](https://github.com/angular/angular/pull/42565)) |
### language-service
| Commit | Description |
| -- | -- |
| [7c35ca0e00](https://github.com/angular/angular/commit/7c35ca0e0030f2ded12ddca9092e31f510cebeb1) | feat(language-service): support autocomplete string literal union types in templates ([#42729](https://github.com/angular/angular/pull/42729)) |
### router
| Commit | Description |
| -- | -- |
| [0d81b007e4](https://github.com/angular/angular/commit/0d81b007e48a0ac801d2614601fb8180a3517865) | fix(router): add missing outlet events to RouterOutletContract ([#42431](https://github.com/angular/angular/pull/42431)) |
| [dbae00195e](https://github.com/angular/angular/commit/dbae00195e114ac8b967201283962a7e2c0581b4) | feat(router): ability to provide custom route reuse strategy via DI for `RouterTestingModule` ([#42434](https://github.com/angular/angular/pull/42434)) |
## Special Thanks:
Andrew Scott, Daniel Trevino, Dmitrij Kuba, Dylan Hunn, George Kalpakas, Joey Perrott, JoostK, Paul Gschwendtner, Pete Bacon Darwin, Steven Masala, Teri Glover, Vladyslav, Yuvaraj, codebriefcase, iRealNirmal and ivanwonder
<a name="12.1.4"></a>
# 12.1.4 (2021-07-28)
### compiler-cli
+28 -28
View File
@@ -23,43 +23,43 @@ The most basic form of content projection is *single-slot content projection*. S
To create a component that uses single-slot content projection:
1. [Create](guide/component-overview) a component.
1. [Create a component](guide/component-overview#creating-a-component).
1. In the template for your component, add an `ng-content` element where you want the projected content to appear.
1. In the template for your component, add an `<ng-content>` element where you want the projected content to appear.
For example, the following component uses an `ng-content` element to display a message.
For example, the following component uses an `<ng-content>` element to display a message.
<code-example path="content-projection/src/app/zippy-basic/zippy-basic.component.ts" header="content-projection/src/app/zippy-basic/zippy-basic.component.ts"></code-example>
With the `ng-content` element in place, users of this component can now project their own message into the component. For example:
With the `<ng-content>` element in place, users of this component can now project their own message into the component. For example:
<code-example path="content-projection/src/app/app.component.html" header="content-projection/src/app/app.component.html"
region="single-slot"></code-example>
<div class="alert is-helpful">
The `ng-content` element is a placeholder that does not create a real DOM element. Custom attributes applied to `ng-content` are ignored.
The `<ng-content>` element is a placeholder that does not create a real DOM element. Custom attributes applied to `<ng-content>` are ignored.
</div>
{@a multi-slot}
## Multi-slot content projection
A component can have multiple slots. Each slot can specify a CSS selector that determines which content goes into that slot. This pattern is referred to as *multi-slot content projection*. With this pattern, you must specify where you want the projected content to appear. You accomplish this task by using the `select` attribute of `ng-content`.
A component can have multiple slots. Each slot can specify a CSS selector that determines which content goes into that slot. This pattern is referred to as *multi-slot content projection*. With this pattern, you must specify where you want the projected content to appear. You accomplish this task by using the `select` attribute of `<ng-content>`.
To create a component that uses multi-slot content projection:
1. [Create](guide/component-overview) a component.
1. [Create a component](guide/component-overview#creating-a-component).
1. In the template for your component, add an `ng-content` element where you want the projected content to appear.
1. In the template for your component, add an `<ng-content>` element where you want the projected content to appear.
1. Add a `select` attribute to the `ng-content` elements. Angular supports [selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) for any combination of tag name, attribute, CSS class, and the `:not` pseudo-class.
1. Add a `select` attribute to the `<ng-content>` elements. Angular supports [selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) for any combination of tag name, attribute, CSS class, and the `:not` pseudo-class.
For example, the following component uses two `ng-content` elements.
For example, the following component uses two `<ng-content>` elements.
<code-example path="content-projection/src/app/zippy-multislot/zippy-multislot.component.ts" header="content-projection/src/app/zippy-multislot/zippy-multislot.component.ts"></code-example>
Content that uses the `question` attribute is projected into the `ng-content` element with the `select=[question]` attribute.
Content that uses the `question` attribute is projected into the `<ng-content>` element with the `select=[question]` attribute.
<code-example path="content-projection/src/app/app.component.html" header="content-projection/src/app/app.component.html"
region="multi-slot"></code-example>
@@ -68,9 +68,9 @@ region="multi-slot"></code-example>
<header>ng-content without a select attribute</header>
If your component includes an `ng-content` element without a `select` attribute, that instance receives all projected components that do not match any of the other `ng-content` elements.
If your component includes an `<ng-content>` element without a `select` attribute, that instance receives all projected components that do not match any of the other `<ng-content>` elements.
In the preceding example, only the second `ng-content` element defines a `select` attribute. As a result, the first `ng-content` element receives any other content projected into the component.
In the preceding example, only the second `<ng-content>` element defines a `select` attribute. As a result, the first `<ng-content>` element receives any other content projected into the component.
</div>
@@ -78,41 +78,41 @@ In the preceding example, only the second `ng-content` element defines a `select
## Conditional content projection
If your component needs to _conditionally_ render content, or render content multiple times, you should configure that component to accept an `ng-template` element that contains the content you want to conditionally render.
If your component needs to _conditionally_ render content, or render content multiple times, you should configure that component to accept an `<ng-template>` element that contains the content you want to conditionally render.
Using an `ng-content` element in these cases is not recommended, because when the consumer of a component supplies the content, that content is _always_ initialized, even if the component does not define an `ng-content` element or if that `ng-content` element is inside of an `ngIf` statement.
Using an `<ng-content>` element in these cases is not recommended, because when the consumer of a component supplies the content, that content is _always_ initialized, even if the component does not define an `<ng-content>` element or if that `<ng-content>` element is inside of an `ngIf` statement.
With an `ng-template` element, you can have your component explicitly render content based on any condition you want, as many times as you want. Angular will not initialize the content of an `ng-template` element until that element is explicitly rendered.
With an `<ng-template>` element, you can have your component explicitly render content based on any condition you want, as many times as you want. Angular will not initialize the content of an `<ng-template>` element until that element is explicitly rendered.
The following steps demonstrate a typical implementation of conditional content projection using `ng-template`.
The following steps demonstrate a typical implementation of conditional content projection using `<ng-template>`.
1. [Create](guide/component-overview) a component.
1. [Create a component](guide/component-overview#creating-a-component).
1. In the component that accepts an `ng-template` element, use an `ng-container` element to render that template, such as:
1. In the component that accepts an `<ng-template>` element, use an `<ng-container>` element to render that template, such as:
<code-example path="content-projection/src/app/example-zippy.template.html" header="content-projection/src/app/example-zippy.template.html" region="ng-container">
</code-example>
This example uses the `ngTemplateOutlet` directive to render a given `ng-template` element, which you will define in a later step. You can apply an `ngTemplateOutlet` directive to any type of element. This example assigns the directive to an `ng-container` element because the component does not need to render a real DOM element.
This example uses the `ngTemplateOutlet` directive to render a given `<ng-template>` element, which you will define in a later step. You can apply an `ngTemplateOutlet` directive to any type of element. This example assigns the directive to an `<ng-container>` element because the component does not need to render a real DOM element.
1. Wrap the `ng-container` element in another element, such as a `div` element, and apply your conditional logic.
1. Wrap the `<ng-container>` element in another element, such as a `div` element, and apply your conditional logic.
<code-example path="content-projection/src/app/example-zippy.template.html" header="content-projection/src/app/example-zippy.template.html" region="ngif">
</code-example>
1. In the template where you want to project content, wrap the projected content in an `ng-template` element, such as:
1. In the template where you want to project content, wrap the projected content in an `<ng-template>` element, such as:
<code-example path="content-projection/src/app/app.component.html" region="ng-template">
</code-example>
The `ng-template` element defines a block of content that a component can render based on its own logic. A component can get a reference to this template content, or [`TemplateRef`](/api/core/TemplateRef), by using either the [`@ContentChild`](/api/core/ContentChild) or [`@ContentChildren`](/api/core/ContentChildren) decorators. The preceding example creates a custom directive, `appExampleZippyContent`, as an API to mark the `ng-template` for the component's content. With the `TemplateRef`, the component can render the referenced content by using either the [`ngTemplateOutlet`](/api/common/NgTemplateOutlet) directive, or with [`ViewContainerRef.createEmbeddedView`](/api/core/ViewContainerRef#createembeddedview).
The `<ng-template>` element defines a block of content that a component can render based on its own logic. A component can get a reference to this template content, or `TemplateRef`, by using either the `@ContentChild` or `@ContentChildren` decorators. The preceding example creates a custom directive, `appExampleZippyContent`, as an API to mark the `<ng-template>` for the component's content. With the `TemplateRef`, the component can render the referenced content by using either the `ngTemplateOutlet` directive, or with the `ViewContainerRef` method `createEmbeddedView()`.
1. Create a directive with a selector that matches the custom attribute for your template. In this directive, inject a TemplateRef instance.
1. [Create an attribute directive](guide/attribute-directives#building-an-attribute-directive) with a selector that matches the custom attribute for your template. In this directive, inject a TemplateRef instance.
<code-example path="content-projection/src/app/app.component.ts" header="content-projection/src/app/app.component.ts" region="zippycontentdirective">
</code-example>
In the previous step, you added an `ng-template` element with a custom attribute, `appExampleZippyDirective`. This code provides the logic that Angular will use when it encounters that custom attribute. In this case, that logic instructs Angular to instantiate a template reference.
In the previous step, you added an `<ng-template>` element with a custom attribute, `appExampleZippyDirective`. This code provides the logic that Angular will use when it encounters that custom attribute. In this case, that logic instructs Angular to instantiate a template reference.
1. In the component you want to project content into, use `@ContentChild` to get the template of the projected content.
@@ -144,14 +144,14 @@ For instance, consider the following HTML snippet:
<code-example path="content-projection/src/app/app.component.html" header="content-projection/src/app/app.component.html" region="ngprojectas">
</code-example>
This example uses an `ng-container` attribute to simulate projecting a component into a more complex structure.
This example uses an `<ng-container>` attribute to simulate projecting a component into a more complex structure.
<div class="callout is-helpful">
<header>Reminder!</header>
The `ng-container` element is a logical construct that you can use to group other DOM elements; however, the `ng-container` itself is not rendered in the DOM tree.
The `<ng-container>` element is a logical construct that you can use to group other DOM elements; however, the `<ng-container>` itself is not rendered in the DOM tree.
</div>
In this example, the content we want to project resides inside another element. To project this content as intended, the template uses the `ngProjectAs` attribute. With `ngProjectAs`, the entire `ng-container` element is projected into a component using the `[question]` selector.
In this example, the content we want to project resides inside another element. To project this content as intended, the template uses the `ngProjectAs` attribute. With `ngProjectAs`, the entire `<ng-container>` element is projected into a component using the `[question]` selector.
@@ -1021,8 +1021,8 @@ The logical tree representation shows why this is:
@Inject(AnimalService, @Optional)=>"🦔">
<!-- ^^@SkipSelf() starts here, @Host() stops here^^ -->
<app-child>
<#VIEW @Provide(AnimalService="🦔")
@Inject(AnimalService, @SkipSelf, @Host, @Optional)=>"🐶">
<#VIEW @Provide(AnimalService="🐶")
@Inject(AnimalService, @SkipSelf, @Host, @Optional)=>"🦔">
<!-- Add @SkipSelf ^^-->
</#VIEW>
</app-child>
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

+7
View File
@@ -459,6 +459,13 @@
"groups": ["Angular"],
"lead": "igorminar"
},
"josmar-crwdstffng": {
"name": "Joe Martin",
"picture": "josephmmartin.jpg",
"bio": "Joe is a technical writer working on the Angular docs.",
"groups": ["Angular"],
"lead": "aikidave"
},
"jschwarty": {
"name": "Justin Schwartzenberger",
"picture": "justinschwartzenberger.jpg",
@@ -1,11 +1,27 @@
"""Implementation of the `browser_archive` rule."""
def _browser_archive_impl(ctx):
ctx.report_progress("Downloading browser archive from: %s" % ctx.attr.url)
ctx.download_and_extract(
url = ctx.attr.url,
sha256 = ctx.attr.sha256,
)
url = ctx.attr.url
sha256 = ctx.attr.sha256
# If the URL resolves to a `.dmg` file, then we need to convert the file
# to a zip so that we can extract the actual binaries. We use the `convert_dmg`
# script provided by the webtesting Bazel rules.
if url.endswith(".dmg"):
download_file_name = "_download_file_%s.dmg" % ctx.attr.name
result_zip_name = "_converted_file_%s.zip" % ctx.attr.name
ctx.download(url, download_file_name, sha256)
ctx.execute([ctx.path(Label("@io_bazel_rules_webtesting//web/internal:convert_dmg.sh")), download_file_name, result_zip_name])
ctx.extract(result_zip_name)
ctx.delete(result_zip_name)
ctx.delete(download_file_name)
else:
ctx.download_and_extract(
url = url,
sha256 = sha256,
)
# The browser archive has been downloaded and extracted. We now generate a repository
# `BUILD.bazel` file that exposes the archive files, together with the specified
+51 -24
View File
@@ -7746,8 +7746,13 @@ function buildEnvStamp(mode) {
}
/** Whether the repo has local changes. */
function hasLocalChanges() {
const git = GitClient.get();
return git.hasUncommittedChanges();
try {
const git = GitClient.get();
return git.hasUncommittedChanges();
}
catch (_a) {
return true;
}
}
/**
* Get the versions for generated packages.
@@ -7756,41 +7761,63 @@ function hasLocalChanges() {
* In release mode, the version is based on the base package.json version.
*/
function getSCMVersions(mode) {
const git = GitClient.get();
if (mode === 'release') {
const packageJsonPath = path.join(git.baseDir, 'package.json');
const { version } = new semver.SemVer(require(packageJsonPath).version);
const { version: experimentalVersion } = createExperimentalSemver(new semver.SemVer(version));
return { version, experimentalVersion };
try {
const git = GitClient.get();
if (mode === 'snapshot') {
const localChanges = hasLocalChanges() ? '.with-local-changes' : '';
const { stdout: rawVersion } = git.run(['describe', '--match', '*[0-9]*.[0-9]*.[0-9]*', '--abbrev=7', '--tags', 'HEAD~100']);
const { version } = new semver.SemVer(rawVersion);
const { version: experimentalVersion } = createExperimentalSemver(version);
return {
version: `${version.replace(/-([0-9]+)-g/, '+$1.sha-')}${localChanges}`,
experimentalVersion: `${experimentalVersion.replace(/-([0-9]+)-g/, '+$1.sha-')}${localChanges}`,
};
}
else {
const packageJsonPath = path.join(git.baseDir, 'package.json');
const { version } = new semver.SemVer(require(packageJsonPath).version);
const { version: experimentalVersion } = createExperimentalSemver(new semver.SemVer(version));
return { version, experimentalVersion };
}
}
if (mode === 'snapshot') {
const localChanges = hasLocalChanges() ? '.with-local-changes' : '';
const { stdout: rawVersion } = git.run(['describe', '--match', '*[0-9]*.[0-9]*.[0-9]*', '--abbrev=7', '--tags', 'HEAD~100']);
const { version } = new semver.SemVer(rawVersion);
const { version: experimentalVersion } = createExperimentalSemver(version);
catch (_a) {
return {
version: `${version.replace(/-([0-9]+)-g/, '+$1.sha-')}${localChanges}`,
experimentalVersion: `${experimentalVersion.replace(/-([0-9]+)-g/, '+$1.sha-')}${localChanges}`,
version: '',
experimentalVersion: '',
};
}
throw Error('No environment stamp mode was provided.');
}
/** Get the current branch or revision of HEAD. */
function getCurrentBranchOrRevision() {
const git = GitClient.get();
return git.getCurrentBranchOrRevision();
try {
const git = GitClient.get();
return git.getCurrentBranchOrRevision();
}
catch (_a) {
return '';
}
}
/** Get the currently checked out branch. */
function getCurrentBranch() {
const git = GitClient.get();
return git.run(['symbolic-ref', '--short', 'HEAD']).stdout.trim();
try {
const git = GitClient.get();
return git.run(['symbolic-ref', '--short', 'HEAD']).stdout.trim();
}
catch (_a) {
return '';
}
}
/** Get the current git user based on the git config. */
function getCurrentGitUser() {
const git = GitClient.get();
let userName = git.runGraceful(['config', 'user.name']).stdout.trim() || 'Unknown User';
let userEmail = git.runGraceful(['config', 'user.email']).stdout.trim() || 'unknown_email';
return `${userName} <${userEmail}>`;
try {
const git = GitClient.get();
let userName = git.runGraceful(['config', 'user.name']).stdout.trim() || 'Unknown User';
let userEmail = git.runGraceful(['config', 'user.email']).stdout.trim() || 'unknown_email';
return `${userName} <${userEmail}>`;
}
catch (_a) {
return '';
}
}
/**
+48 -27
View File
@@ -37,8 +37,12 @@ export function buildEnvStamp(mode: EnvStampMode) {
/** Whether the repo has local changes. */
function hasLocalChanges() {
const git = GitClient.get();
return git.hasUncommittedChanges();
try {
const git = GitClient.get();
return git.hasUncommittedChanges();
} catch {
return true;
}
}
/**
@@ -48,44 +52,61 @@ function hasLocalChanges() {
* In release mode, the version is based on the base package.json version.
*/
function getSCMVersions(mode: EnvStampMode): {version: string, experimentalVersion: string} {
const git = GitClient.get();
if (mode === 'release') {
const packageJsonPath = join(git.baseDir, 'package.json');
const {version} = new SemVer(require(packageJsonPath).version);
const {version: experimentalVersion} = createExperimentalSemver(new SemVer(version));
return {version, experimentalVersion};
}
if (mode === 'snapshot') {
const localChanges = hasLocalChanges() ? '.with-local-changes' : '';
const {stdout: rawVersion} = git.run(
['describe', '--match', '*[0-9]*.[0-9]*.[0-9]*', '--abbrev=7', '--tags', 'HEAD~100']);
const {version} = new SemVer(rawVersion);
const {version: experimentalVersion} = createExperimentalSemver(version);
try {
const git = GitClient.get();
if (mode === 'snapshot') {
const localChanges = hasLocalChanges() ? '.with-local-changes' : '';
const {stdout: rawVersion} = git.run(
['describe', '--match', '*[0-9]*.[0-9]*.[0-9]*', '--abbrev=7', '--tags', 'HEAD~100']);
const {version} = new SemVer(rawVersion);
const {version: experimentalVersion} = createExperimentalSemver(version);
return {
version: `${version.replace(/-([0-9]+)-g/, '+$1.sha-')}${localChanges}`,
experimentalVersion:
`${experimentalVersion.replace(/-([0-9]+)-g/, '+$1.sha-')}${localChanges}`,
};
} else {
const packageJsonPath = join(git.baseDir, 'package.json');
const {version} = new SemVer(require(packageJsonPath).version);
const {version: experimentalVersion} = createExperimentalSemver(new SemVer(version));
return {version, experimentalVersion};
}
} catch {
return {
version: `${version.replace(/-([0-9]+)-g/, '+$1.sha-')}${localChanges}`,
experimentalVersion:
`${experimentalVersion.replace(/-([0-9]+)-g/, '+$1.sha-')}${localChanges}`,
version: '',
experimentalVersion: '',
};
}
throw Error('No environment stamp mode was provided.');
}
/** Get the current branch or revision of HEAD. */
function getCurrentBranchOrRevision() {
const git = GitClient.get();
return git.getCurrentBranchOrRevision();
try {
const git = GitClient.get();
return git.getCurrentBranchOrRevision();
} catch {
return '';
}
}
/** Get the currently checked out branch. */
function getCurrentBranch() {
const git = GitClient.get();
return git.run(['symbolic-ref', '--short', 'HEAD']).stdout.trim();
try {
const git = GitClient.get();
return git.run(['symbolic-ref', '--short', 'HEAD']).stdout.trim();
} catch {
return '';
}
}
/** Get the current git user based on the git config. */
function getCurrentGitUser() {
const git = GitClient.get();
let userName = git.runGraceful(['config', 'user.name']).stdout.trim() || 'Unknown User';
let userEmail = git.runGraceful(['config', 'user.email']).stdout.trim() || 'unknown_email';
return `${userName} <${userEmail}>`;
try {
const git = GitClient.get();
let userName = git.runGraceful(['config', 'user.name']).stdout.trim() || 'Unknown User';
let userEmail = git.runGraceful(['config', 'user.email']).stdout.trim() || 'unknown_email';
return `${userName} <${userEmail}>`;
} catch {
return '';
}
}
@@ -30,7 +30,7 @@
"master": {
"uncompressed": {
"runtime-es2015": 1190,
"main-es2015": 136546,
"main-es2015": 137055,
"polyfills-es2015": 37641
}
}
+1
View File
@@ -88,6 +88,7 @@ INTEGRATION_TESTS = {
"service-worker-schema": {},
"side-effects": {"tags": ["no-ivy-aot"]},
"terser": {},
"typings_test_rxjs7": {},
"typings_test_ts41": {
# Special case for `typings_test_ts41` test as we want to pin
# `typescript` at version 4.1.x for that test and not link to the
@@ -0,0 +1,69 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as animations from '@angular/animations';
import * as animationsBrowser from '@angular/animations/browser';
import * as animationsBrowserTesting from '@angular/animations/browser/testing';
import * as common from '@angular/common';
import * as commonHttp from '@angular/common/http';
import * as commonTesting from '@angular/common/testing';
import * as commonHttpTesting from '@angular/common/testing';
import * as compiler from '@angular/compiler';
import * as compilerTesting from '@angular/compiler/testing';
import * as core from '@angular/core';
import * as coreTesting from '@angular/core/testing';
import * as elements from '@angular/elements';
import * as forms from '@angular/forms';
import * as platformBrowser from '@angular/platform-browser';
import * as platformBrowserDynamic from '@angular/platform-browser-dynamic';
import * as platformBrowserDynamicTesting from '@angular/platform-browser-dynamic/testing';
import * as platformBrowserAnimations from '@angular/platform-browser/animations';
import * as platformBrowserTesting from '@angular/platform-browser/testing';
import * as platformServer from '@angular/platform-server';
import * as platformServerInit from '@angular/platform-server/init';
import * as platformServerTesting from '@angular/platform-server/testing';
import * as router from '@angular/router';
import * as routerTesting from '@angular/router/testing';
import * as routerUpgrade from '@angular/router/upgrade';
import * as serviceWorker from '@angular/service-worker';
import * as upgrade from '@angular/upgrade';
import * as upgradeStatic from '@angular/upgrade/static';
import * as upgradeTesting from '@angular/upgrade/static/testing';
export default {
animations,
animationsBrowser,
animationsBrowserTesting,
common,
commonTesting,
commonHttp,
commonHttpTesting,
compiler,
compilerTesting,
core,
coreTesting,
elements,
forms,
platformBrowser,
platformBrowserTesting,
platformBrowserDynamic,
platformBrowserDynamicTesting,
platformBrowserAnimations,
platformServer,
platformServerInit,
platformServerTesting,
router,
routerTesting,
routerUpgrade,
serviceWorker,
upgrade,
upgradeStatic,
upgradeTesting,
};
@@ -0,0 +1,28 @@
{
"name": "angular-integration",
"description": "Assert that users with rxjs 7 can type-check an Angular application",
"version": "0.0.0",
"license": "MIT",
"dependencies": {
"@angular/animations": "file:../../dist/packages-dist/animations",
"@angular/common": "file:../../dist/packages-dist/common",
"@angular/compiler": "file:../../dist/packages-dist/compiler",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/core": "file:../../dist/packages-dist/core",
"@angular/elements": "file:../../dist/packages-dist/elements",
"@angular/forms": "file:../../dist/packages-dist/forms",
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
"@angular/router": "file:../../dist/packages-dist/router",
"@angular/service-worker": "file:../../dist/packages-dist/service-worker",
"@angular/upgrade": "file:../../dist/packages-dist/upgrade",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"rxjs": "^7.3.0",
"typescript": "file:../../node_modules/typescript",
"zone.js": "file:../../dist/zone.js-dist/archive/zone.js.tgz"
},
"scripts": {
"test": "tsc"
}
}
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"experimentalDecorators": true,
"module": "commonjs",
"moduleResolution": "node",
"outDir": "./dist/out-tsc",
"rootDir": ".",
"target": "es5",
"lib": [
"es5",
"dom",
"es2015.collection",
"es2015.iterable",
"es2015.promise"
],
"types": [],
},
"files": [
"include-all.ts",
"node_modules/@types/jasmine/index.d.ts"
]
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "12.2.0-next.3",
"version": "12.2.0",
"private": true,
"description": "Angular - a web framework for modern web apps",
"homepage": "https://github.com/angular/angular",
+107 -92
View File
@@ -298,98 +298,8 @@ export function compile({
};
const ngHost = ng.createCompilerHost({options: compilerOpts, tsHost: bazelHost});
const fileNameToModuleNameCache = new Map<string, string>();
ngHost.fileNameToModuleName = (importedFilePath: string, containingFilePath?: string) => {
const cacheKey = `${importedFilePath}:${containingFilePath}`;
// Memoize this lookup to avoid expensive re-parses of the same file
// When run as a worker, the actual ts.SourceFile is cached
// but when we don't run as a worker, there is no cache.
// For one example target in g3, we saw a cache hit rate of 7590/7695
if (fileNameToModuleNameCache.has(cacheKey)) {
return fileNameToModuleNameCache.get(cacheKey);
}
const result = doFileNameToModuleName(importedFilePath, containingFilePath);
fileNameToModuleNameCache.set(cacheKey, result);
return result;
};
function doFileNameToModuleName(importedFilePath: string, containingFilePath?: string): string {
const relativeTargetPath =
relativeToRootDirs(importedFilePath, compilerOpts.rootDirs).replace(EXT, '');
const manifestTargetPath = `${bazelOpts.workspaceName}/${relativeTargetPath}`;
if (useManifestPathsAsModuleName === true) {
return manifestTargetPath;
}
// Unless manifest paths are explicitly enforced, we initially check if a module name is
// set for the given source file. The compiler host from `@bazel/typescript` sets source
// file module names if the compilation targets either UMD or AMD. To ensure that the AMD
// module names match, we first consider those.
try {
const sourceFile = ngHost.getSourceFile(importedFilePath, ts.ScriptTarget.Latest);
if (sourceFile && sourceFile.moduleName) {
return sourceFile.moduleName;
}
} catch (err) {
// File does not exist or parse error. Ignore this case and continue onto the
// other methods of resolving the module below.
}
// It can happen that the ViewEngine compiler needs to write an import in a factory file,
// and is using an ngsummary file to get the symbols.
// The ngsummary comes from an upstream ng_module rule.
// The upstream rule based its imports on ngsummary file which was generated from a
// metadata.json file that was published to npm in an Angular library.
// However, the ngsummary doesn't propagate the 'importAs' from the original metadata.json
// so we would normally not be able to supply the correct module name for it.
// For example, if the rootDir-relative filePath is
// node_modules/@angular/material/toolbar/typings/index
// we would supply a module name
// @angular/material/toolbar/typings/index
// but there is no JavaScript file to load at this path.
// This is a workaround for https://github.com/angular/angular/issues/29454
if (importedFilePath.indexOf('node_modules') >= 0) {
const maybeMetadataFile = importedFilePath.replace(EXT, '') + '.metadata.json';
if (fs.existsSync(maybeMetadataFile)) {
const moduleName = (JSON.parse(fs.readFileSync(maybeMetadataFile, {encoding: 'utf-8'})) as {
importAs: string
}).importAs;
if (moduleName) {
return moduleName;
}
}
}
if ((compilerOpts.module === ts.ModuleKind.UMD || compilerOpts.module === ts.ModuleKind.AMD) &&
ngHost.amdModuleName) {
return ngHost.amdModuleName({fileName: importedFilePath} as ts.SourceFile);
}
// If no AMD module name has been set for the source file by the `@bazel/typescript` compiler
// host, and the target file is not part of a flat module node module package, we use the
// following rules (in order):
// 1. If target file is part of `node_modules/`, we use the package module name.
// 2. If no containing file is specified, or the target file is part of a different
// compilation unit, we use a Bazel manifest path. Relative paths are not possible
// since we don't have a containing file, and the target file could be located in the
// output directory, or in an external Bazel repository.
// 3. If both rules above didn't match, we compute a relative path between the source files
// since they are part of the same compilation unit.
// Note that we don't want to always use (2) because it could mean that compilation outputs
// are always leaking Bazel-specific paths, and the output is not self-contained. This could
// break `esm2015` or `esm5` output for Angular package release output
// Omit the `node_modules` prefix if the module name of an NPM package is requested.
if (relativeTargetPath.startsWith(NODE_MODULES)) {
return relativeTargetPath.substr(NODE_MODULES.length);
} else if (
containingFilePath == null || !bazelOpts.compilationTargetSrc.includes(importedFilePath)) {
return manifestTargetPath;
}
const containingFileDir =
path.dirname(relativeToRootDirs(containingFilePath, compilerOpts.rootDirs));
const relativeImportPath = path.posix.relative(containingFileDir, relativeTargetPath);
return relativeImportPath.startsWith('.') ? relativeImportPath : `./${relativeImportPath}`;
}
patchNgHostWithFileNameToModuleName(
ngHost, compilerOpts, bazelOpts, useManifestPathsAsModuleName);
ngHost.toSummaryFileName = (fileName: string, referringSrcFileName: string) => path.posix.join(
bazelOpts.workspaceName,
@@ -553,3 +463,108 @@ function gatherDiagnosticsForInputsOnly(
if (require.main === module) {
process.exitCode = main(process.argv.slice(2));
}
/**
* Adds support for the optional `fileNameToModuleName` operation to a given `ng.CompilerHost`.
*
* This is used within `ngc-wrapped` and the Bazel compilation flow, but is exported here to allow
* for other consumers of the compiler to access this same logic. For example, the xi18n operation
* in g3 configures its own `ng.CompilerHost` which also requires `fileNameToModuleName` to work
* correctly.
*/
export function patchNgHostWithFileNameToModuleName(
ngHost: ng.CompilerHost, compilerOpts: ng.CompilerOptions, bazelOpts: BazelOptions,
useManifestPathsAsModuleName: boolean): void {
const fileNameToModuleNameCache = new Map<string, string>();
ngHost.fileNameToModuleName = (importedFilePath: string, containingFilePath?: string) => {
const cacheKey = `${importedFilePath}:${containingFilePath}`;
// Memoize this lookup to avoid expensive re-parses of the same file
// When run as a worker, the actual ts.SourceFile is cached
// but when we don't run as a worker, there is no cache.
// For one example target in g3, we saw a cache hit rate of 7590/7695
if (fileNameToModuleNameCache.has(cacheKey)) {
return fileNameToModuleNameCache.get(cacheKey);
}
const result = doFileNameToModuleName(importedFilePath, containingFilePath);
fileNameToModuleNameCache.set(cacheKey, result);
return result;
};
function doFileNameToModuleName(importedFilePath: string, containingFilePath?: string): string {
const relativeTargetPath =
relativeToRootDirs(importedFilePath, compilerOpts.rootDirs).replace(EXT, '');
const manifestTargetPath = `${bazelOpts.workspaceName}/${relativeTargetPath}`;
if (useManifestPathsAsModuleName === true) {
return manifestTargetPath;
}
// Unless manifest paths are explicitly enforced, we initially check if a module name is
// set for the given source file. The compiler host from `@bazel/typescript` sets source
// file module names if the compilation targets either UMD or AMD. To ensure that the AMD
// module names match, we first consider those.
try {
const sourceFile = ngHost.getSourceFile(importedFilePath, ts.ScriptTarget.Latest);
if (sourceFile && sourceFile.moduleName) {
return sourceFile.moduleName;
}
} catch (err) {
// File does not exist or parse error. Ignore this case and continue onto the
// other methods of resolving the module below.
}
// It can happen that the ViewEngine compiler needs to write an import in a factory file,
// and is using an ngsummary file to get the symbols.
// The ngsummary comes from an upstream ng_module rule.
// The upstream rule based its imports on ngsummary file which was generated from a
// metadata.json file that was published to npm in an Angular library.
// However, the ngsummary doesn't propagate the 'importAs' from the original metadata.json
// so we would normally not be able to supply the correct module name for it.
// For example, if the rootDir-relative filePath is
// node_modules/@angular/material/toolbar/typings/index
// we would supply a module name
// @angular/material/toolbar/typings/index
// but there is no JavaScript file to load at this path.
// This is a workaround for https://github.com/angular/angular/issues/29454
if (importedFilePath.indexOf('node_modules') >= 0) {
const maybeMetadataFile = importedFilePath.replace(EXT, '') + '.metadata.json';
if (fs.existsSync(maybeMetadataFile)) {
const moduleName = (JSON.parse(fs.readFileSync(maybeMetadataFile, {encoding: 'utf-8'})) as {
importAs: string
}).importAs;
if (moduleName) {
return moduleName;
}
}
}
if ((compilerOpts.module === ts.ModuleKind.UMD || compilerOpts.module === ts.ModuleKind.AMD) &&
ngHost.amdModuleName) {
return ngHost.amdModuleName({fileName: importedFilePath} as ts.SourceFile);
}
// If no AMD module name has been set for the source file by the `@bazel/typescript` compiler
// host, and the target file is not part of a flat module node module package, we use the
// following rules (in order):
// 1. If target file is part of `node_modules/`, we use the package module name.
// 2. If no containing file is specified, or the target file is part of a different
// compilation unit, we use a Bazel manifest path. Relative paths are not possible
// since we don't have a containing file, and the target file could be located in the
// output directory, or in an external Bazel repository.
// 3. If both rules above didn't match, we compute a relative path between the source files
// since they are part of the same compilation unit.
// Note that we don't want to always use (2) because it could mean that compilation outputs
// are always leaking Bazel-specific paths, and the output is not self-contained. This could
// break `esm2015` or `esm5` output for Angular package release output
// Omit the `node_modules` prefix if the module name of an NPM package is requested.
if (relativeTargetPath.startsWith(NODE_MODULES)) {
return relativeTargetPath.substr(NODE_MODULES.length);
} else if (
containingFilePath == null || !bazelOpts.compilationTargetSrc.includes(importedFilePath)) {
return manifestTargetPath;
}
const containingFileDir =
path.dirname(relativeToRootDirs(containingFilePath, compilerOpts.rootDirs));
const relativeImportPath = path.posix.relative(containingFileDir, relativeTargetPath);
return relativeImportPath.startsWith('.') ? relativeImportPath : `./${relativeImportPath}`;
}
}
+1 -1
View File
@@ -13,7 +13,7 @@
},
"peerDependencies": {
"@angular/core": "0.0.0-PLACEHOLDER",
"rxjs": "^6.5.3"
"rxjs": "^6.5.3 || ^7.0.0"
},
"repository": {
"type": "git",
@@ -22,7 +22,7 @@ import {Subscribable, Unsubscribable} from 'rxjs';
// the implementation does not rely on other methods:
const wrapSubscribable = <T>(input: Subscribable<T>): Subscribable<T> => ({
subscribe(...args: any): Unsubscribable {
const subscription = input.subscribe(...args);
const subscription = input.subscribe.apply(input, args);
return {
unsubscribe() {
subscription.unsubscribe();
@@ -7,7 +7,7 @@
*/
import {TmplAstReference, TmplAstTemplate} from '@angular/compiler';
import {AST, EmptyExpr, LiteralPrimitive, MethodCall, PropertyRead, PropertyWrite, SafeMethodCall, SafePropertyRead, TmplAstNode} from '@angular/compiler/src/compiler';
import {AST, EmptyExpr, ImplicitReceiver, LiteralPrimitive, MethodCall, PropertyRead, PropertyWrite, SafeMethodCall, SafePropertyRead, TmplAstNode} from '@angular/compiler/src/compiler';
import {TextAttribute} from '@angular/compiler/src/render3/r3_ast';
import * as ts from 'typescript';
@@ -91,6 +91,19 @@ export class CompletionEngine {
}
}
if (node instanceof PropertyRead && node.receiver instanceof ImplicitReceiver) {
const nodeLocation = findFirstMatchingNode(this.tcb, {
filter: ts.isPropertyAccessExpression,
withSpan: node.sourceSpan,
});
if (nodeLocation) {
nodeContext = {
shimPath: this.shimPath,
positionInShimFile: nodeLocation.getStart(),
};
}
}
return {
componentContext: this.componentContext,
templateContext,
+1 -1
View File
@@ -624,7 +624,7 @@ const _shadowDOMSelectorsRe = [
// Support for `>>>`, `deep`, `::ng-deep` is then also deprecated and will be removed in the future.
// see https://github.com/angular/angular/pull/17677
const _shadowDeepSelectors = /(?:>>>)|(?:\/deep\/)|(?:::ng-deep)/g;
const _selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$';
const _selectorReSuffix = '([>\\s~+[.,{:][\\s\\S]*)?$';
const _polyfillHostRe = /-shadowcsshost/gim;
const _colonHostRe = /:host/gim;
const _colonHostContextRe = /:host-context/gim;
@@ -24,7 +24,7 @@ import {componentFactoryResolverProviderDef, depDef, lifecycleHookToNodeFlag, pr
const CLASS_ATTR = 'class';
const STYLE_ATTR = 'style';
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
const IMPLICIT_TEMPLATE_VAR = '$implicit';
export class ViewCompileResult {
constructor(public viewClassVar: string, public rendererTypeVar: string) {}
+1 -1
View File
@@ -11,7 +11,7 @@
"tslib": "^2.2.0"
},
"peerDependencies": {
"rxjs": "^6.5.3",
"rxjs": "^6.5.3 || ^7.0.0",
"zone.js": "~0.11.4"
},
"repository": {
@@ -336,6 +336,7 @@ export function renderView<T>(tView: TView, lView: LView, context: T): void {
// an error, mark the view as corrupted so we can try to recover.
if (tView.firstCreatePass) {
tView.incompleteFirstPass = true;
tView.firstCreatePass = false;
}
throw error;
@@ -187,7 +187,7 @@ describe('property bindings', () => {
})
class OtherDir {
@Input() id: number|undefined;
@Output('click') clickStream = new EventEmitter();
@Output('click') clickStream = new EventEmitter<void>();
}
@Directive({
@@ -7,7 +7,7 @@
*/
import {CommonModule} from '@angular/common';
import {ChangeDetectorRef, Component, ComponentFactoryResolver, Directive, EmbeddedViewRef, Injector, Input, NgModule, TemplateRef, ViewChild, ViewContainerRef, ViewRef} from '@angular/core';
import {ChangeDetectorRef, Component, ComponentFactoryResolver, Directive, EmbeddedViewRef, Injectable, Injector, Input, NgModule, TemplateRef, ViewChild, ViewContainerRef, ViewRef} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {onlyInIvy} from '@angular/private/testing';
@@ -886,5 +886,46 @@ describe('view insertion', () => {
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toContain('2');
});
it('should consistently report errors raised by createEmbeddedView', () => {
// Intentionally hasn't been added to `providers` so that it throws a DI error.
@Injectable()
class DoesNotExist {
}
@Directive({selector: 'dir'})
class Dir {
constructor(willCauseError: DoesNotExist) {}
}
@Component({
template: `
<ng-template #broken>
<dir></dir>
</ng-template>
`,
})
class App {
@ViewChild('broken') template !: TemplateRef<unknown>;
constructor(private _viewContainerRef: ViewContainerRef) {}
insertTemplate() {
this._viewContainerRef.createEmbeddedView(this.template);
}
}
TestBed.configureTestingModule({declarations: [App, Dir]});
const fixture = TestBed.createComponent(App);
const tryRender = () => {
fixture.componentInstance.insertTemplate();
fixture.detectChanges();
};
fixture.detectChanges();
// We try to render the same template twice to ensure that we get consistent error messages.
expect(tryRender).toThrowError(/No provider for DoesNotExist/);
expect(tryRender).toThrowError(/No provider for DoesNotExist/);
});
});
});
+1 -1
View File
@@ -13,7 +13,7 @@
"peerDependencies": {
"@angular/core": "0.0.0-PLACEHOLDER",
"@angular/platform-browser": "0.0.0-PLACEHOLDER",
"rxjs": "^6.5.3"
"rxjs": "^6.5.3 || ^7.0.0"
},
"repository": {
"type": "git",
+1 -1
View File
@@ -14,7 +14,7 @@
"@angular/core": "0.0.0-PLACEHOLDER",
"@angular/common": "0.0.0-PLACEHOLDER",
"@angular/platform-browser": "0.0.0-PLACEHOLDER",
"rxjs": "^6.5.3"
"rxjs": "^6.5.3 || ^7.0.0"
},
"repository": {
"type": "git",
@@ -234,6 +234,18 @@ describe('completions', () => {
const {templateFile} = setup(`<input dir [myInput]="">`, '', DIR_WITH_UNION_TYPE_INPUT);
templateFile.moveCursorToText('dir [myInput]="¦">');
const completions = templateFile.getCompletionsAtPosition();
expectContain(completions, ts.ScriptElementKind.string, [`'foo'`, '42']);
expectContain(completions, ts.ScriptElementKind.keyword, ['null']);
expectContain(completions, ts.ScriptElementKind.variableElement, ['undefined']);
expectDoesNotContain(completions, ts.ScriptElementKind.parameterElement, ['ctx']);
});
it('should return completions of string literals, number literals, `true`, `false`, `null` and `undefined` when the user tries to modify the symbol',
() => {
const {templateFile} = setup(`<input dir [myInput]="a">`, '', DIR_WITH_UNION_TYPE_INPUT);
templateFile.moveCursorToText('dir [myInput]="a¦">');
const completions = templateFile.getCompletionsAtPosition();
expectContain(completions, ts.ScriptElementKind.string, [`'foo'`, '42']);
expectContain(completions, ts.ScriptElementKind.keyword, ['null']);
@@ -5,9 +5,9 @@
"author": "angular",
"license": "MIT",
"peerDependencies": {
"@angular/core": "^12.0.0-next.6",
"@angular/common": "^12.0.0-next.6",
"rxjs": "^6.5.3"
"@angular/core": "^12.0.0",
"@angular/common": "^12.0.0",
"rxjs": "^6.5.3 || ^7.0.0"
},
"dependencies": {
"tslib": "^2.2.0"
+1 -1
View File
@@ -27,7 +27,7 @@
"@angular/core": "0.0.0-PLACEHOLDER",
"@angular/common": "0.0.0-PLACEHOLDER",
"@angular/platform-browser": "0.0.0-PLACEHOLDER",
"rxjs": "^6.5.3"
"rxjs": "^6.5.3 || ^7.0.0"
},
"ng-update": {
"packageGroup": "NG_UPDATE_PACKAGE_GROUP"