mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cdc8c7cad0 | |||
| bd7f0d8b70 | |||
| 3a9ce61ea4 | |||
| ae98b24eb7 | |||
| 4997a3f692 | |||
| 6a59e6b009 | |||
| 6f80c55dbd | |||
| 07846e550c | |||
| f18131f372 | |||
| f24b94f5d9 | |||
| 6caafc2062 | |||
| f883aaeb89 | |||
| 2215af252c | |||
| db4147b4d2 | |||
| 2dc6efed4e | |||
| 1d307bc5bf | |||
| aace1e71d8 | |||
| fbfec946a7 | |||
| 0e47334db8 | |||
| e4f62c12f5 | |||
| ea3a14805e | |||
| a96f9c91a2 | |||
| a8c0f4f1ba | |||
| 11ef03b36f | |||
| 8e798effe4 | |||
| 54f73530a8 | |||
| 61080ea4bc | |||
| 4116d5d18f | |||
| 5572278826 | |||
| b442c4991d | |||
| 014763e42c | |||
| 86cfbf8c8a | |||
| 3b92b4abd3 | |||
| dd6e80aac5 | |||
| da50b9b5e7 | |||
| 86e4b3cdf9 | |||
| 3fddcb5364 | |||
| d5cc87dc88 | |||
| b075721a49 | |||
| 3346cb6e4e |
@@ -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
|
||||
|
||||
@@ -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 |
@@ -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
@@ -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 '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user