Compare commits
146 Commits
7.0.1
...
7.1.0-beta
Author | SHA1 | Date | |
---|---|---|---|
67789f10ef | |||
b83f1300bd | |||
6e16a17015 | |||
d744dd70e0 | |||
4e91ead40b | |||
7f39f37003 | |||
bf9ab53f12 | |||
d99f661af7 | |||
5d58a31d86 | |||
07b89902d5 | |||
ce6948fc1b | |||
38d626a3fa | |||
9b8a244a15 | |||
1bbf28ad19 | |||
3b24e0edb6 | |||
b647608c96 | |||
31c462ae3f | |||
da39fd70d2 | |||
ee0b857172 | |||
e0d6782d26 | |||
2f9e957523 | |||
38dbae9ca0 | |||
5a3077f46c | |||
d0cc019c1a | |||
26e8032bd0 | |||
84af7b065d | |||
83dc3c0ee0 | |||
5952775a03 | |||
57531737e5 | |||
3fd50f07fd | |||
4237c34c78 | |||
361eaf1888 | |||
cca89ec36e | |||
e4c7f369f2 | |||
d573a14119 | |||
ff767dd153 | |||
34c6ce6b08 | |||
6737e91974 | |||
1006eab482 | |||
a9f2952882 | |||
100c7eff25 | |||
dc7349915d | |||
213c25fb08 | |||
615a515bba | |||
838a3f204f | |||
15c2467dbd | |||
74ea4e9b5d | |||
4481571999 | |||
2132c8f461 | |||
2b3cac5b31 | |||
8d5e3e6981 | |||
30f1dc002a | |||
5da3b00222 | |||
631998b2df | |||
30d6233e83 | |||
6468711e16 | |||
d698b0eadf | |||
1f3331f5e6 | |||
b6c9678f21 | |||
4c2ce4e8ba | |||
bb0f95b6fb | |||
853faf4c71 | |||
00c7db02d1 | |||
13143b850e | |||
c1724062f1 | |||
7570f7222f | |||
f1e3d5125d | |||
3511f08a81 | |||
982bc7f2aa | |||
3903e5ebe7 | |||
0918adf39d | |||
42c331bbf2 | |||
0bae97a726 | |||
c5949f85ef | |||
24521f549c | |||
4bd9f53e8f | |||
2a78dcbd5a | |||
2ea57cdcc3 | |||
31022cbecf | |||
ce8053103e | |||
0b885ecaf7 | |||
1700bd6f08 | |||
b89bc37170 | |||
39df4dbde7 | |||
716d887e51 | |||
5b4cf38166 | |||
4c0ad5238e | |||
9e8903ada1 | |||
aa55d88408 | |||
07fc4c2464 | |||
b9bd95b3b2 | |||
3f89aeb80a | |||
6c530d3a85 | |||
03bf0d6b41 | |||
d4cee514f6 | |||
331989cea3 | |||
bbf96db2f2 | |||
72c2578d14 | |||
1a666dea61 | |||
7634c1cb31 | |||
95914a0fbf | |||
9c50891d6e | |||
624433c51d | |||
09cc458bfc | |||
efe0c5f371 | |||
d9d226087c | |||
7bad1d356d | |||
0add00a743 | |||
d557f1d9de | |||
665627e254 | |||
a609bf50ed | |||
f8195e5e3d | |||
65b209359a | |||
3a65c9ad4e | |||
77e58c6179 | |||
23d625172a | |||
c3643615fc | |||
638aaecc7d | |||
5a79decba4 | |||
225162aa6c | |||
a43b80a4c9 | |||
beebf7fe14 | |||
0ef1f7ef0d | |||
fc6dad40ac | |||
ecd473bbce | |||
8a3fd58cad | |||
8024857d4c | |||
3fa876c5e7 | |||
516515d9e3 | |||
948f507ba0 | |||
81a8ee1ddb | |||
5e58da14f0 | |||
fa8e633be5 | |||
be337a2e52 | |||
4d164b6ca4 | |||
371ffef4ce | |||
fdfedceeec | |||
9e5d440a0b | |||
0f7d2ca7a8 | |||
81c9720acb | |||
ea2cfbbd2e | |||
2326b9c294 | |||
41de0e0d98 | |||
f6a2dbf4f5 | |||
b115374601 | |||
7d82053117 |
@ -132,8 +132,6 @@ jobs:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
# don't run this job on the patch branch (to preserve resources)
|
||||
- run: circleci step halt
|
||||
- *define_env_vars
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
@ -147,8 +145,6 @@ jobs:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
# don't run this job on the patch branch (to preserve resources)
|
||||
- run: circleci step halt
|
||||
- *define_env_vars
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
|
18
CHANGELOG.md
18
CHANGELOG.md
@ -1,3 +1,21 @@
|
||||
<a name="7.1.0-beta.0"></a>
|
||||
# [7.1.0-beta.0](https://github.com/angular/angular/compare/7.0.0-rc.1...7.1.0-beta.0) (2018-10-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** allow null value for renderer setElement(…) ([#17065](https://github.com/angular/angular/issues/17065)) ([ff15043](https://github.com/angular/angular/commit/ff15043)), closes [#13686](https://github.com/angular/angular/issues/13686)
|
||||
* **router:** fix regression where navigateByUrl promise didn't resolve on CanLoad failure ([#26455](https://github.com/angular/angular/issues/26455)) ([1c9b065](https://github.com/angular/angular/commit/1c9b065)), closes [#26284](https://github.com/angular/angular/issues/26284)
|
||||
* **service-worker:** clean up caches from old SW versions ([#26319](https://github.com/angular/angular/issues/26319)) ([2326b9c](https://github.com/angular/angular/commit/2326b9c))
|
||||
* **upgrade:** properly destroy upgraded component elements and descendants ([#26209](https://github.com/angular/angular/issues/26209)) ([071934e](https://github.com/angular/angular/commit/071934e)), closes [#26208](https://github.com/angular/angular/issues/26208)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **router:** add prioritizedGuardValue operator optimization and allowing UrlTree return from guard ([#26478](https://github.com/angular/angular/issues/26478)) ([fdfedce](https://github.com/angular/angular/commit/fdfedce))
|
||||
|
||||
|
||||
|
||||
<a name="7.0.1"></a>
|
||||
## [7.0.1](https://github.com/angular/angular/compare/7.0.0...7.0.1) (2018-10-24)
|
||||
|
||||
|
@ -76,7 +76,7 @@ See also: [`//.bazelrc`](https://github.com/angular/angular/blob/master/.bazelrc
|
||||
|
||||
- `--config=debug`: build and launch in debug mode (see [debugging](#debugging) instructions below)
|
||||
- `--test_arg=--node_options=--inspect=9228`: change the inspector port.
|
||||
- `--define=compile=<option>` Controls if ivy or legacy mode is enabled. This is done by generating the [`src/ivy_switch.ts`](https://github.com/angular/angular/blob/master/packages/core/src/ivy_switch.ts) file from [`ivy_switch_legacy.ts`](https://github.com/angular/angular/blob/master/packages/core/src/ivy_switch_legacy.ts) (default), [`ivy_switch_jit.ts`](https://github.com/angular/angular/blob/master/packages/core/src/ivy_switch_jit.ts), or [`ivy_switch_local.ts`](https://github.com/angular/angular/blob/master/packages/core/src/ivy_switch_local.ts).
|
||||
- `--define=compile=<option>` Controls if ivy or legacy mode is enabled. This switches which compiler is used (ngc, ngtsc, or a tsc pass-through mode).
|
||||
- `legacy`: (default behavior) compile against View Engine, e.g. `--define=compile=legacy`
|
||||
- `jit`: Compile in ivy JIT mode, e.g. `--define=compile=jit`
|
||||
- `local`: Compile in ivy AOT move, e.g. `--define=compile=local`
|
||||
|
@ -17,10 +17,10 @@ ivy-ngcc
|
||||
ls node_modules/@angular/common | grep __modified_by_ngcc_for_esm2015
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
|
||||
# Did it replace the NGCC_PRE markers correctly?
|
||||
grep "= R3_COMPILE_COMPONENT__POST_NGCC__" node_modules/@angular/core/fesm2015/core.js
|
||||
# Did it replace the PRE_R3 markers correctly?
|
||||
grep "= SWITCH_COMPILE_COMPONENT__POST_R3__" node_modules/@angular/core/fesm2015/core.js
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
grep "= R3_COMPILE_COMPONENT__POST_NGCC__" node_modules/@angular/core/fesm5/core.js
|
||||
grep "= SWITCH_COMPILE_COMPONENT__POST_R3__" node_modules/@angular/core/fesm5/core.js
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
|
||||
# Did it compile @angular/core/ApplicationModule correctly?
|
||||
|
@ -56,15 +56,6 @@ describe('largetable benchmark perf', () => {
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
|
||||
it('should run for render3', done => {
|
||||
runTableBenchmark({
|
||||
id: `largeTable.render3.${worker.id}`,
|
||||
url: 'all/benchmarks/src/largetable/render3/index.html',
|
||||
ignoreBrowserSynchronization: true,
|
||||
worker: worker
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
|
||||
it('should run for iv', done => {
|
||||
runTableBenchmark({
|
||||
id: `largeTable.iv.${worker.id}`,
|
||||
|
@ -25,13 +25,6 @@ describe('largetable benchmark spec', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for render3', () => {
|
||||
testTableBenchmark({
|
||||
url: 'all/benchmarks/src/largetable/render3/index.html',
|
||||
ignoreBrowserSynchronization: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for iv', () => {
|
||||
testTableBenchmark({
|
||||
url: 'all/benchmarks/src/largetable/iv/index.html',
|
||||
|
@ -49,12 +49,6 @@ export const Benchmarks: Benchmark[] = [
|
||||
url: 'all/benchmarks/src/tree/ng2_switch/index.html',
|
||||
buttons: CreateDestroyButtons,
|
||||
},
|
||||
{
|
||||
id: `deepTree.ng2.render3`,
|
||||
url: 'all/benchmarks/src/tree/render3/index.html',
|
||||
buttons: CreateDestroyDetectChangesButtons,
|
||||
ignoreBrowserSynchronization: true,
|
||||
},
|
||||
{
|
||||
id: `deepTree.ng2.render3_function`,
|
||||
url: 'all/benchmarks/src/tree/render3_function/index.html',
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import {$} from 'protractor';
|
||||
|
||||
import {openBrowser} from '../../../e2e_util/e2e_util';
|
||||
import {runBenchmark, verifyNoBrowserErrors} from '../../../e2e_util/perf_util';
|
||||
|
||||
interface Worker {
|
||||
@ -39,9 +40,23 @@ describe('largetable benchmark perf', () => {
|
||||
|
||||
afterEach(verifyNoBrowserErrors);
|
||||
|
||||
it('should render the table for render3', () => {
|
||||
openBrowser({
|
||||
url: '',
|
||||
ignoreBrowserSynchronization: true,
|
||||
params: [{name: 'cols', value: 5}, {name: 'rows', value: 5}],
|
||||
});
|
||||
$('#createDom').click();
|
||||
expect($('#root').getText()).toContain('0/0');
|
||||
$('#createDom').click();
|
||||
expect($('#root').getText()).toContain('A/A');
|
||||
$('#destroyDom').click();
|
||||
expect($('#root').getText() as any).toEqual('');
|
||||
});
|
||||
|
||||
[CreateOnlyWorker, CreateAndDestroyWorker, UpdateWorker].forEach((worker) => {
|
||||
describe(worker.id, () => {
|
||||
it('should run for render3', done => {
|
||||
it('should run benchmark for render3', done => {
|
||||
runTableBenchmark({
|
||||
id: `largeTable.render3.${worker.id}`,
|
||||
url: 'index.html',
|
||||
|
@ -1,10 +1,10 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//tools:defaults.bzl", "ng_rollup_bundle", "ts_library")
|
||||
load("//tools:defaults.bzl", "ng_module", "ng_rollup_bundle")
|
||||
load("//packages/bazel:index.bzl", "protractor_web_test")
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_devserver")
|
||||
|
||||
ts_library(
|
||||
ng_module(
|
||||
name = "largetable_lib",
|
||||
srcs = glob(
|
||||
[
|
||||
@ -12,18 +12,20 @@ ts_library(
|
||||
],
|
||||
exclude = ["protractor.on-prepare.ts"],
|
||||
),
|
||||
tags = ["ivy-only"],
|
||||
deps = [
|
||||
"//modules/benchmarks/src:util_lib",
|
||||
"//modules/benchmarks/src/largetable:util_lib",
|
||||
"//packages:types",
|
||||
"//packages/common",
|
||||
"//packages/core",
|
||||
"@rxjs",
|
||||
],
|
||||
)
|
||||
|
||||
ng_rollup_bundle(
|
||||
name = "bundle",
|
||||
entry_point = "modules/benchmarks/src/largetable/render3/index.js",
|
||||
tags = ["ivy-only"],
|
||||
deps = [
|
||||
":largetable_lib",
|
||||
],
|
||||
@ -44,6 +46,7 @@ ts_devserver(
|
||||
"index.html",
|
||||
":favicon",
|
||||
],
|
||||
tags = ["ivy-only"],
|
||||
)
|
||||
|
||||
protractor_web_test(
|
||||
@ -57,9 +60,9 @@ protractor_web_test(
|
||||
"@ngdeps//reflect-metadata",
|
||||
"@ngdeps//yargs",
|
||||
],
|
||||
on_prepare = ":protractor.on-prepare.js",
|
||||
on_prepare = ":protractor.on_prepare.js",
|
||||
server = ":devserver",
|
||||
tags = ["manual"],
|
||||
tags = ["ivy-only"],
|
||||
deps = [
|
||||
"//modules/benchmarks/src/largetable:perf_lib",
|
||||
],
|
||||
|
@ -28,15 +28,9 @@
|
||||
<script>
|
||||
// TODO(mlaval): remove once we have a proper solution
|
||||
ngDevMode = false;
|
||||
var isBazel = location.pathname.indexOf('/all/') !== 0;
|
||||
// isBazel needed while 'scripts/ci/test-e2e.sh test.e2e.protractor-e2e' is run
|
||||
// on Travis
|
||||
// TODO: port remaining protractor e2e tests to bazel protractor_web_test_suite rule
|
||||
var bazelBundle = document.location.search.endsWith('debug') ? 'bundle.min_debug.js' : 'bundle.min.js';
|
||||
var mainUrl = window.location.search.split(/[?&]main=([^&]+)/)[1]
|
||||
|| '../../bootstrap_ng2.js';
|
||||
document.write('<script src="' + (isBazel ? bazelBundle : mainUrl) + '">\u003c/script>');
|
||||
document.write('<script src="' + bazelBundle + '">\u003c/script>');
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
@ -5,7 +5,7 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import 'reflect-metadata';
|
||||
import {ɵrenderComponent as renderComponent} from '@angular/core';
|
||||
|
||||
import {bindAction, profile} from '../../util';
|
||||
|
@ -1,21 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
const protractorUtils = require('@angular/bazel/protractor-utils');
|
||||
const protractor = require('protractor');
|
||||
|
||||
module.exports = function(config) {
|
||||
return protractorUtils.runServer(config.workspace, config.server, '-port', [])
|
||||
.then(serverSpec => {
|
||||
const serverUrl = `http://localhost:${serverSpec.port}`;
|
||||
// Since the browser restarts in this benchmark we need to set both the browser.baseUrl
|
||||
// for the first test and the protractor config.baseUrl for the subsequent tests
|
||||
protractor.browser.baseUrl = serverUrl;
|
||||
return protractor.browser.getProcessedConfig().then((config) => config.baseUrl = serverUrl);
|
||||
});
|
||||
};
|
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
const protractorUtils = require('@angular/bazel/protractor-utils');
|
||||
const protractor = require('protractor');
|
||||
|
||||
module.exports = async function(config) {
|
||||
const serverSpec = await protractorUtils.runServer(config.workspace, config.server, '-port', []);
|
||||
|
||||
const serverUrl = `http://localhost:${serverSpec.port}`;
|
||||
// Since the browser restarts in this benchmark we need to set both the browser.baseUrl
|
||||
// for the first test and the protractor config.baseUrl for the subsequent tests
|
||||
protractor.browser.baseUrl = serverUrl;
|
||||
|
||||
const processedConfig = await protractor.browser.getProcessedConfig();
|
||||
return processedConfig.baseUrl = serverUrl;
|
||||
};
|
@ -6,76 +6,39 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ɵRenderFlags, ɵbind, ɵcontainer, ɵcontainerRefreshEnd, ɵcontainerRefreshStart, ɵdefineComponent, ɵdetectChanges, ɵelementEnd, ɵelementStart, ɵelementStyleProp, ɵelementStyling, ɵembeddedViewEnd, ɵembeddedViewStart, ɵtext, ɵtextBinding as ɵtextBinding} from '@angular/core';
|
||||
import {ComponentDef} from '@angular/core/src/render3/interfaces/definition';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component, Input, NgModule, ɵdetectChanges} from '@angular/core';
|
||||
|
||||
import {TableCell, buildTable, emptyTable} from '../util';
|
||||
|
||||
const c0 = ['background-color'];
|
||||
@Component({
|
||||
selector: 'largetable',
|
||||
template: `
|
||||
<table>
|
||||
<tbody>
|
||||
<tr *ngFor="let row of data; trackBy: trackByIndex">
|
||||
<td *ngFor="let cell of row; trackBy: trackByIndex" [style.background-color]="getColor(cell.row)">
|
||||
{{cell.value}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
`,
|
||||
})
|
||||
export class LargeTableComponent {
|
||||
@Input()
|
||||
data: TableCell[][] = emptyTable;
|
||||
|
||||
/** @nocollapse */
|
||||
static ngComponentDef: ComponentDef<LargeTableComponent> = ɵdefineComponent({
|
||||
type: LargeTableComponent,
|
||||
selectors: [['largetable']],
|
||||
consts: 3,
|
||||
vars: 0,
|
||||
template: function(rf: ɵRenderFlags, ctx: LargeTableComponent) {
|
||||
if (rf & ɵRenderFlags.Create) {
|
||||
ɵelementStart(0, 'table');
|
||||
{
|
||||
ɵelementStart(1, 'tbody');
|
||||
{ ɵcontainer(2); }
|
||||
ɵelementEnd();
|
||||
}
|
||||
ɵelementEnd();
|
||||
}
|
||||
if (rf & ɵRenderFlags.Update) {
|
||||
ɵcontainerRefreshStart(2);
|
||||
{
|
||||
for (let row of ctx.data) {
|
||||
let rf1 = ɵembeddedViewStart(1, 2, 0);
|
||||
{
|
||||
if (rf1 & ɵRenderFlags.Create) {
|
||||
ɵelementStart(0, 'tr');
|
||||
ɵcontainer(1);
|
||||
ɵelementEnd();
|
||||
}
|
||||
if (rf1 & ɵRenderFlags.Update) {
|
||||
ɵcontainerRefreshStart(1);
|
||||
{
|
||||
for (let cell of row) {
|
||||
let rf2 = ɵembeddedViewStart(2, 2, 1);
|
||||
{
|
||||
if (rf2 & ɵRenderFlags.Create) {
|
||||
ɵelementStart(0, 'td');
|
||||
ɵelementStyling(null, c0);
|
||||
{ ɵtext(1); }
|
||||
ɵelementEnd();
|
||||
}
|
||||
if (rf2 & ɵRenderFlags.Update) {
|
||||
ɵelementStyleProp(0, 0, null, cell.row % 2 ? '' : 'grey');
|
||||
ɵtextBinding(1, ɵbind(cell.value));
|
||||
}
|
||||
}
|
||||
ɵembeddedViewEnd();
|
||||
}
|
||||
}
|
||||
ɵcontainerRefreshEnd();
|
||||
}
|
||||
}
|
||||
ɵembeddedViewEnd();
|
||||
}
|
||||
}
|
||||
ɵcontainerRefreshEnd();
|
||||
}
|
||||
},
|
||||
factory: () => new LargeTableComponent(),
|
||||
inputs: {data: 'data'}
|
||||
});
|
||||
trackByIndex(index: number, item: any) { return index; }
|
||||
|
||||
getColor(row: number) { return row % 2 ? '' : 'grey'; }
|
||||
}
|
||||
|
||||
@NgModule({declarations: [LargeTableComponent], imports: [CommonModule]})
|
||||
class TableModule {
|
||||
}
|
||||
|
||||
|
||||
export function destroyDom(component: LargeTableComponent) {
|
||||
component.data = emptyTable;
|
||||
ɵdetectChanges(component);
|
||||
|
@ -13,3 +13,16 @@ ts_library(
|
||||
"//packages/core",
|
||||
],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "perf_lib",
|
||||
testonly = 1,
|
||||
srcs = [
|
||||
"tree_perf.spec.ts",
|
||||
],
|
||||
deps = [
|
||||
"//modules/e2e_util:lib",
|
||||
"//packages:types",
|
||||
"@ngdeps//protractor",
|
||||
],
|
||||
)
|
||||
|
@ -1,18 +1,67 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//tools:defaults.bzl", "ng_module")
|
||||
load("//tools:defaults.bzl", "ng_module", "ng_rollup_bundle")
|
||||
load("//packages/bazel:index.bzl", "protractor_web_test")
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_devserver")
|
||||
|
||||
ng_module(
|
||||
name = "render3_lib",
|
||||
name = "tree_lib",
|
||||
srcs = glob(
|
||||
[
|
||||
"**/*.ts",
|
||||
],
|
||||
),
|
||||
tags = ["ivy-only"],
|
||||
deps = [
|
||||
"//modules/benchmarks/src/tree:util_lib",
|
||||
"//packages:types",
|
||||
"//packages/common",
|
||||
"//packages/core",
|
||||
"@rxjs",
|
||||
],
|
||||
)
|
||||
|
||||
ng_rollup_bundle(
|
||||
name = "bundle",
|
||||
entry_point = "modules/benchmarks/src/tree/render3/index.js",
|
||||
tags = ["ivy-only"],
|
||||
deps = [
|
||||
":tree_lib",
|
||||
],
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "favicon",
|
||||
srcs = ["//modules/benchmarks:favicon"],
|
||||
outs = ["favicon.ico"],
|
||||
cmd = "cp $< $@",
|
||||
)
|
||||
|
||||
ts_devserver(
|
||||
name = "devserver",
|
||||
static_files = [
|
||||
":bundle.min_debug.js",
|
||||
":bundle.min.js",
|
||||
"index.html",
|
||||
":favicon",
|
||||
],
|
||||
tags = ["ivy-only"],
|
||||
)
|
||||
|
||||
protractor_web_test(
|
||||
name = "perf",
|
||||
configuration = "//:protractor-perf.conf.js",
|
||||
data = [
|
||||
"//packages/bazel/src/protractor/utils",
|
||||
"//packages/benchpress",
|
||||
],
|
||||
on_prepare = ":protractor.on_prepare.js",
|
||||
server = ":devserver",
|
||||
tags = ["ivy-only"],
|
||||
deps = [
|
||||
"//modules/benchmarks/src/tree:perf_lib",
|
||||
"@ngdeps//node-uuid",
|
||||
"@ngdeps//protractor",
|
||||
"@ngdeps//reflect-metadata",
|
||||
"@ngdeps//yargs",
|
||||
],
|
||||
)
|
||||
|
@ -30,9 +30,8 @@
|
||||
<script>
|
||||
// TODO(mlaval): remove once we have a proper solution
|
||||
ngDevMode = false;
|
||||
var mainUrl = window.location.search.split(/[?&]main=([^&]+)/)[1]
|
||||
|| '../../bootstrap_ng2.js';
|
||||
document.write('<script src="' + mainUrl + '">\u003c/script>');
|
||||
var bazelBundle = document.location.search.endsWith('debug') ? 'bundle.min_debug.js' : 'bundle.min.js';
|
||||
document.write('<script src="' + bazelBundle + '">\u003c/script>');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -6,6 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import 'reflect-metadata';
|
||||
import {ɵrenderComponent as renderComponent} from '@angular/core';
|
||||
import {bindAction, profile} from '../../util';
|
||||
import {TreeComponent, createDom, destroyDom, detectChanges} from './tree';
|
||||
@ -27,3 +28,5 @@ export function main() {
|
||||
profile(() => createDom(component), () => destroyDom(component), 'create'));
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
22
modules/benchmarks/src/tree/render3/protractor.on_prepare.js
Normal file
22
modules/benchmarks/src/tree/render3/protractor.on_prepare.js
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
const protractorUtils = require('@angular/bazel/protractor-utils');
|
||||
const protractor = require('protractor');
|
||||
|
||||
module.exports = async function(config) {
|
||||
const serverSpec = await protractorUtils.runServer(config.workspace, config.server, '-port', []);
|
||||
|
||||
const serverUrl = `http://localhost:${serverSpec.port}`;
|
||||
// Since the browser restarts in this benchmark we need to set both the browser.baseUrl
|
||||
// for the first test and the protractor config.baseUrl for the subsequent tests
|
||||
protractor.browser.baseUrl = serverUrl;
|
||||
|
||||
const processedConfig = await protractor.browser.getProcessedConfig();
|
||||
return processedConfig.baseUrl = serverUrl;
|
||||
};
|
@ -6,7 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ɵRenderFlags, ɵbind, ɵcontainer, ɵcontainerRefreshEnd, ɵcontainerRefreshStart, ɵdefineComponent, ɵdetectChanges, ɵelementEnd, ɵelementProperty, ɵelementStart, ɵelementStyleProp, ɵelementStyling as s, ɵembeddedViewEnd, ɵembeddedViewStart, ɵinterpolation1, ɵtext, ɵtextBinding as ɵtextBinding} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component, NgModule, ɵdetectChanges} from '@angular/core';
|
||||
|
||||
import {TreeNode, buildTree, emptyTree} from '../util';
|
||||
|
||||
@ -30,122 +31,17 @@ export function detectChanges(component: TreeComponent) {
|
||||
numberOfChecksEl.textContent = `${detectChangesRuns}`;
|
||||
}
|
||||
|
||||
const c0 = ['background-color'];
|
||||
@Component({
|
||||
selector: 'tree',
|
||||
inputs: ['data'],
|
||||
template:
|
||||
`<span [style.backgroundColor]="bgColor"> {{data.value}} </span><tree *ngIf='data.right != null' [data]='data.right'></tree><tree *ngIf='data.left != null' [data]='data.left'></tree>`
|
||||
})
|
||||
export class TreeComponent {
|
||||
data: TreeNode = emptyTree;
|
||||
|
||||
/** @nocollapse */
|
||||
static ngComponentDef = ɵdefineComponent({
|
||||
type: TreeComponent,
|
||||
selectors: [['tree']],
|
||||
consts: 4,
|
||||
vars: 1,
|
||||
template: function(rf: ɵRenderFlags, ctx: TreeComponent) {
|
||||
if (rf & ɵRenderFlags.Create) {
|
||||
ɵelementStart(0, 'span');
|
||||
s(null, c0);
|
||||
{ ɵtext(1); }
|
||||
ɵelementEnd();
|
||||
ɵcontainer(2);
|
||||
ɵcontainer(3);
|
||||
}
|
||||
if (rf & ɵRenderFlags.Update) {
|
||||
ɵelementStyleProp(0, 0, ctx.data.depth % 2 ? '' : 'grey');
|
||||
ɵtextBinding(1, ɵinterpolation1(' ', ctx.data.value, ' '));
|
||||
ɵcontainerRefreshStart(2);
|
||||
{
|
||||
if (ctx.data.left != null) {
|
||||
let rf0 = ɵembeddedViewStart(0, 1, 1);
|
||||
{
|
||||
if (rf0 & ɵRenderFlags.Create) {
|
||||
ɵelementStart(0, 'tree');
|
||||
ɵelementEnd();
|
||||
}
|
||||
if (rf0 & ɵRenderFlags.Update) {
|
||||
ɵelementProperty(0, 'data', ɵbind(ctx.data.left));
|
||||
}
|
||||
}
|
||||
ɵembeddedViewEnd();
|
||||
}
|
||||
}
|
||||
ɵcontainerRefreshEnd();
|
||||
ɵcontainerRefreshStart(3);
|
||||
{
|
||||
if (ctx.data.right != null) {
|
||||
let rf0 = ɵembeddedViewStart(0, 1, 1);
|
||||
{
|
||||
if (rf0 & ɵRenderFlags.Create) {
|
||||
ɵelementStart(0, 'tree');
|
||||
ɵelementEnd();
|
||||
}
|
||||
if (rf0 & ɵRenderFlags.Update) {
|
||||
ɵelementProperty(0, 'data', ɵbind(ctx.data.right));
|
||||
}
|
||||
}
|
||||
ɵembeddedViewEnd();
|
||||
}
|
||||
}
|
||||
ɵcontainerRefreshEnd();
|
||||
}
|
||||
},
|
||||
factory: () => new TreeComponent,
|
||||
inputs: {data: 'data'},
|
||||
directives: () => [TreeComponent]
|
||||
});
|
||||
data: any = emptyTree;
|
||||
get bgColor() { return this.data.depth % 2 ? '' : 'grey'; }
|
||||
}
|
||||
|
||||
export class TreeFunction {
|
||||
data: TreeNode = emptyTree;
|
||||
|
||||
/** @nocollapse */
|
||||
static ngComponentDef = ɵdefineComponent({
|
||||
type: TreeFunction,
|
||||
selectors: [['tree']],
|
||||
consts: 5,
|
||||
vars: 1,
|
||||
template: function(rf: ɵRenderFlags, ctx: TreeFunction) {
|
||||
// bit of a hack
|
||||
TreeTpl(rf, ctx.data);
|
||||
},
|
||||
factory: () => new TreeFunction,
|
||||
inputs: {data: 'data'}
|
||||
});
|
||||
}
|
||||
|
||||
const c1 = ['background-color'];
|
||||
export function TreeTpl(rf: ɵRenderFlags, ctx: TreeNode) {
|
||||
if (rf & ɵRenderFlags.Create) {
|
||||
ɵelementStart(0, 'tree');
|
||||
{
|
||||
ɵelementStart(1, 'span');
|
||||
s(null, c1);
|
||||
{ ɵtext(2); }
|
||||
ɵelementEnd();
|
||||
ɵcontainer(3);
|
||||
ɵcontainer(4);
|
||||
}
|
||||
ɵelementEnd();
|
||||
}
|
||||
if (rf & ɵRenderFlags.Update) {
|
||||
ɵelementStyleProp(1, 0, ctx.depth % 2 ? '' : 'grey');
|
||||
ɵtextBinding(2, ɵinterpolation1(' ', ctx.value, ' '));
|
||||
ɵcontainerRefreshStart(3);
|
||||
{
|
||||
if (ctx.left != null) {
|
||||
let rf0 = ɵembeddedViewStart(0, 5, 1);
|
||||
{ TreeTpl(rf0, ctx.left); }
|
||||
ɵembeddedViewEnd();
|
||||
}
|
||||
}
|
||||
ɵcontainerRefreshEnd();
|
||||
ɵcontainerRefreshStart(4);
|
||||
{
|
||||
if (ctx.right != null) {
|
||||
let rf0 = ɵembeddedViewStart(0, 5, 1);
|
||||
{ TreeTpl(rf0, ctx.right); }
|
||||
ɵembeddedViewEnd();
|
||||
}
|
||||
}
|
||||
ɵcontainerRefreshEnd();
|
||||
}
|
||||
@NgModule({declarations: [TreeComponent], imports: [CommonModule]})
|
||||
export class TreeModule {
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ ng_module(
|
||||
),
|
||||
deps = [
|
||||
"//modules/benchmarks/src/tree:util_lib",
|
||||
"//modules/benchmarks/src/tree/render3:render3_lib",
|
||||
"//modules/benchmarks/src/tree/render3:tree_lib",
|
||||
"//packages:types",
|
||||
"//packages/core",
|
||||
"@rxjs",
|
||||
|
@ -6,9 +6,11 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ɵrenderComponent as renderComponent} from '@angular/core';
|
||||
import {ɵRenderFlags, ɵcontainer, ɵcontainerRefreshEnd, ɵcontainerRefreshStart, ɵdefineComponent, ɵelementEnd, ɵelementStart, ɵelementStyleProp, ɵelementStyling, ɵembeddedViewEnd, ɵembeddedViewStart, ɵinterpolation1, ɵrenderComponent as renderComponent, ɵtext, ɵtextBinding} from '@angular/core';
|
||||
|
||||
import {bindAction, profile} from '../../util';
|
||||
import {TreeFunction, createDom, destroyDom, detectChanges} from '../render3/tree';
|
||||
import {createDom, destroyDom, detectChanges} from '../render3/tree';
|
||||
import {TreeNode, emptyTree} from '../util';
|
||||
|
||||
function noop() {}
|
||||
|
||||
@ -16,14 +18,71 @@ export function main() {
|
||||
let component: TreeFunction;
|
||||
if (typeof window !== 'undefined') {
|
||||
component = renderComponent(TreeFunction);
|
||||
bindAction('#createDom', () => createDom(component));
|
||||
bindAction('#destroyDom', () => destroyDom(component));
|
||||
bindAction('#detectChanges', () => detectChanges(component));
|
||||
bindAction('#createDom', () => createDom(component as any));
|
||||
bindAction('#destroyDom', () => destroyDom(component as any));
|
||||
bindAction('#detectChanges', () => detectChanges(component as any));
|
||||
bindAction(
|
||||
'#detectChangesProfile', profile(() => detectChanges(component), noop, 'detectChanges'));
|
||||
bindAction('#updateDomProfile', profile(() => createDom(component), noop, 'update'));
|
||||
'#detectChangesProfile',
|
||||
profile(() => detectChanges(component as any), noop, 'detectChanges'));
|
||||
bindAction('#updateDomProfile', profile(() => createDom(component as any), noop, 'update'));
|
||||
bindAction(
|
||||
'#createDomProfile',
|
||||
profile(() => createDom(component), () => destroyDom(component), 'create'));
|
||||
profile(() => createDom(component as any), () => destroyDom(component as any), 'create'));
|
||||
}
|
||||
}
|
||||
|
||||
export class TreeFunction {
|
||||
data: TreeNode = emptyTree;
|
||||
|
||||
/** @nocollapse */
|
||||
static ngComponentDef = ɵdefineComponent({
|
||||
type: TreeFunction,
|
||||
selectors: [['tree']],
|
||||
consts: 5,
|
||||
vars: 1,
|
||||
template: function(rf: ɵRenderFlags, ctx: TreeFunction) {
|
||||
// bit of a hack
|
||||
TreeTpl(rf, ctx.data);
|
||||
},
|
||||
factory: () => new TreeFunction,
|
||||
inputs: {data: 'data'}
|
||||
});
|
||||
}
|
||||
|
||||
const c1 = ['background-color'];
|
||||
export function TreeTpl(rf: ɵRenderFlags, ctx: TreeNode) {
|
||||
if (rf & ɵRenderFlags.Create) {
|
||||
ɵelementStart(0, 'tree');
|
||||
{
|
||||
ɵelementStart(1, 'span');
|
||||
ɵelementStyling(null, c1);
|
||||
{ ɵtext(2); }
|
||||
ɵelementEnd();
|
||||
ɵcontainer(3);
|
||||
ɵcontainer(4);
|
||||
}
|
||||
ɵelementEnd();
|
||||
}
|
||||
if (rf & ɵRenderFlags.Update) {
|
||||
ɵelementStyleProp(1, 0, ctx.depth % 2 ? '' : 'grey');
|
||||
ɵtextBinding(2, ɵinterpolation1(' ', ctx.value, ' '));
|
||||
ɵcontainerRefreshStart(3);
|
||||
{
|
||||
if (ctx.left != null) {
|
||||
let rf0 = ɵembeddedViewStart(0, 5, 1);
|
||||
{ TreeTpl(rf0, ctx.left); }
|
||||
ɵembeddedViewEnd();
|
||||
}
|
||||
}
|
||||
ɵcontainerRefreshEnd();
|
||||
ɵcontainerRefreshStart(4);
|
||||
{
|
||||
if (ctx.right != null) {
|
||||
let rf0 = ɵembeddedViewStart(0, 5, 1);
|
||||
{ TreeTpl(rf0, ctx.right); }
|
||||
ɵembeddedViewEnd();
|
||||
}
|
||||
}
|
||||
ɵcontainerRefreshEnd();
|
||||
}
|
||||
}
|
||||
|
101
modules/benchmarks/src/tree/tree_perf.spec.ts
Normal file
101
modules/benchmarks/src/tree/tree_perf.spec.ts
Normal file
@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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 {$, browser} from 'protractor';
|
||||
|
||||
import {openBrowser} from '../../../e2e_util/e2e_util';
|
||||
import {runBenchmark} from '../../../e2e_util/perf_util';
|
||||
|
||||
describe('benchmark render', () => {
|
||||
it('should work for createDestroy', () => {
|
||||
openTreeBenchmark();
|
||||
$('#createDom').click();
|
||||
expect($('#root').getText()).toContain('0');
|
||||
$('#destroyDom').click();
|
||||
expect($('#root').getText() as any).toEqual('');
|
||||
});
|
||||
|
||||
it('should work for update', () => {
|
||||
openTreeBenchmark();
|
||||
$('#createDom').click();
|
||||
$('#createDom').click();
|
||||
expect($('#root').getText()).toContain('A');
|
||||
});
|
||||
|
||||
it('should work for detectChanges', () => {
|
||||
openTreeBenchmark();
|
||||
$('#detectChanges').click();
|
||||
expect($('#numberOfChecks').getText()).toContain('10');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('benchmarks', () => {
|
||||
|
||||
it('should work for createOnly', done => {
|
||||
runTreeBenchmark({
|
||||
id: 'createOnly',
|
||||
prepare: () => $('#destroyDom').click(),
|
||||
work: () => $('#createDom').click()
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
|
||||
it('should work for destroy', done => {
|
||||
runTreeBenchmark({
|
||||
id: 'createOnly',
|
||||
prepare: () => $('#createDom').click(),
|
||||
work: () => $('#destroyDom').click()
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
|
||||
it('should work for createDestroy', done => {
|
||||
runTreeBenchmark({
|
||||
id: 'createDestroy',
|
||||
work: () => {
|
||||
$('#destroyDom').click();
|
||||
$('#createDom').click();
|
||||
}
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
|
||||
it('should work for update', done => {
|
||||
runTreeBenchmark({id: 'update', work: () => $('#createDom').click()}).then(done, done.fail);
|
||||
});
|
||||
|
||||
it('should work for detectChanges', done => {
|
||||
runTreeBenchmark({
|
||||
id: 'detectChanges',
|
||||
work: () => $('#detectChanges').click(),
|
||||
setup: () => $('#destroyDom').click()
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function runTreeBenchmark({id, prepare, setup, work}:
|
||||
{id: string; prepare ? () : void; setup ? () : void; work(): void;}) {
|
||||
browser.rootEl = '#root';
|
||||
return runBenchmark({
|
||||
id: id,
|
||||
url: '',
|
||||
ignoreBrowserSynchronization: true,
|
||||
params: [{name: 'depth', value: 11}],
|
||||
work: work,
|
||||
prepare: prepare,
|
||||
setup: setup
|
||||
});
|
||||
}
|
||||
|
||||
function openTreeBenchmark() {
|
||||
browser.rootEl = '#root';
|
||||
openBrowser({
|
||||
url: '',
|
||||
ignoreBrowserSynchronization: true,
|
||||
params: [{name: 'depth', value: 4}],
|
||||
});
|
||||
}
|
@ -40,6 +40,7 @@ export function runBenchmark(config: {
|
||||
if (config.setup) {
|
||||
config.setup();
|
||||
}
|
||||
if (!cmdArgs) readCommandLine();
|
||||
const description: {[key: string]: any} = {'bundles': cmdArgs.bundles};
|
||||
config.params.forEach((param) => { description[param.name] = param.value; });
|
||||
return runner.sample({
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "7.0.1",
|
||||
"version": "7.1.0-beta.0",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import * as ng from '@angular/compiler-cli';
|
||||
import {BazelOptions, CachedFileLoader, CompilerHost, FileCache, FileLoader, UncachedFileLoader, constructManifest, debug, fixUmdModuleDeclarations, parseTsconfig, resolveNormalizedPath, runAsWorker, runWorkerLoop} from '@bazel/typescript';
|
||||
import {BazelOptions, CachedFileLoader, CompilerHost, FileCache, FileLoader, UncachedFileLoader, constructManifest, debug, parseTsconfig, resolveNormalizedPath, runAsWorker, runWorkerLoop} from '@bazel/typescript';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as tsickle from 'tsickle';
|
||||
@ -291,10 +291,7 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
|
||||
program, bazelHost, bazelHost, compilerOpts, targetSourceFile, writeFile,
|
||||
cancellationToken, emitOnlyDtsFiles, {
|
||||
beforeTs: customTransformers.before,
|
||||
afterTs: [
|
||||
...(customTransformers.after || []),
|
||||
fixUmdModuleDeclarations((sf: ts.SourceFile) => bazelHost.amdModuleName(sf)),
|
||||
],
|
||||
afterTs: customTransformers.after,
|
||||
});
|
||||
|
||||
if (!gatherDiagnostics) {
|
||||
|
@ -26,8 +26,9 @@ ts_library(
|
||||
"//packages/compiler",
|
||||
"//packages/compiler-cli/src/ngtsc/annotations",
|
||||
"//packages/compiler-cli/src/ngtsc/diagnostics",
|
||||
"//packages/compiler-cli/src/ngtsc/factories",
|
||||
"//packages/compiler-cli/src/ngtsc/metadata",
|
||||
"//packages/compiler-cli/src/ngtsc/shims",
|
||||
"//packages/compiler-cli/src/ngtsc/switch",
|
||||
"//packages/compiler-cli/src/ngtsc/transform",
|
||||
"//packages/compiler-cli/src/ngtsc/typecheck",
|
||||
"@ngdeps//@bazel/typescript",
|
||||
|
@ -9,8 +9,8 @@ import * as ts from 'typescript';
|
||||
import {ReflectionHost} from '../../../ngtsc/host';
|
||||
import {DecoratedFile} from './decorated_file';
|
||||
|
||||
export const PRE_NGCC_MARKER = '__PRE_NGCC__';
|
||||
export const POST_NGCC_MARKER = '__POST_NGCC__';
|
||||
export const PRE_NGCC_MARKER = '__PRE_R3__';
|
||||
export const POST_NGCC_MARKER = '__POST_R3__';
|
||||
|
||||
export type SwitchableVariableDeclaration = ts.VariableDeclaration & {initializer: ts.Identifier};
|
||||
export function isSwitchableVariableDeclaration(node: ts.Node):
|
||||
|
@ -30,15 +30,15 @@ const TEST_PROGRAM = [
|
||||
name: 'b.js',
|
||||
contents: `
|
||||
export const b = 42;
|
||||
var factoryB = factory__PRE_NGCC__;
|
||||
var factoryB = factory__PRE_R3__;
|
||||
`
|
||||
},
|
||||
{
|
||||
name: 'c.js',
|
||||
contents: `
|
||||
export const c = 'So long, and thanks for all the fish!';
|
||||
var factoryC = factory__PRE_NGCC__;
|
||||
var factoryD = factory__PRE_NGCC__;
|
||||
var factoryC = factory__PRE_R3__;
|
||||
var factoryD = factory__PRE_R3__;
|
||||
`
|
||||
},
|
||||
];
|
||||
@ -62,14 +62,14 @@ describe('SwitchMarkerAnalyzer', () => {
|
||||
expect(analysis.has(b)).toBe(true);
|
||||
expect(analysis.get(b) !.sourceFile).toBe(b);
|
||||
expect(analysis.get(b) !.declarations.map(decl => decl.getText())).toEqual([
|
||||
'factoryB = factory__PRE_NGCC__'
|
||||
'factoryB = factory__PRE_R3__'
|
||||
]);
|
||||
|
||||
expect(analysis.has(c)).toBe(true);
|
||||
expect(analysis.get(c) !.sourceFile).toBe(c);
|
||||
expect(analysis.get(c) !.declarations.map(decl => decl.getText())).toEqual([
|
||||
'factoryC = factory__PRE_NGCC__',
|
||||
'factoryD = factory__PRE_NGCC__',
|
||||
'factoryC = factory__PRE_R3__',
|
||||
'factoryD = factory__PRE_R3__',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -35,15 +35,15 @@ const CLASSES = [
|
||||
const MARKER_FILE = {
|
||||
name: '/marker.js',
|
||||
contents: `
|
||||
let compileNgModuleFactory = compileNgModuleFactory__PRE_NGCC__;
|
||||
let compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;
|
||||
|
||||
function compileNgModuleFactory__PRE_NGCC__(injector, options, moduleType) {
|
||||
function compileNgModuleFactory__PRE_R3__(injector, options, moduleType) {
|
||||
const compilerFactory = injector.get(CompilerFactory);
|
||||
const compiler = compilerFactory.createCompiler([options]);
|
||||
return compiler.compileModuleAsync(moduleType);
|
||||
}
|
||||
|
||||
function compileNgModuleFactory__POST_NGCC__(injector, options, moduleType) {
|
||||
function compileNgModuleFactory__POST_R3__(injector, options, moduleType) {
|
||||
ngDevMode && assertNgModuleType(moduleType);
|
||||
return Promise.resolve(new R3NgModuleFactory(moduleType));
|
||||
}
|
||||
@ -113,7 +113,7 @@ describe('Esm2015ReflectionHost', () => {
|
||||
const file = program.getSourceFile(MARKER_FILE.name) !;
|
||||
const declarations = host.getSwitchableDeclarations(file);
|
||||
expect(declarations.map(d => [d.name.getText(), d.initializer !.getText()])).toEqual([
|
||||
['compileNgModuleFactory', 'compileNgModuleFactory__PRE_NGCC__']
|
||||
['compileNgModuleFactory', 'compileNgModuleFactory__PRE_R3__']
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -377,15 +377,15 @@ const FUNCTION_BODY_FILE = {
|
||||
const MARKER_FILE = {
|
||||
name: '/marker.js',
|
||||
contents: `
|
||||
var compileNgModuleFactory = compileNgModuleFactory__PRE_NGCC__;
|
||||
var compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;
|
||||
|
||||
function compileNgModuleFactory__PRE_NGCC__(injector, options, moduleType) {
|
||||
function compileNgModuleFactory__PRE_R3__(injector, options, moduleType) {
|
||||
var compilerFactory = injector.get(CompilerFactory);
|
||||
var compiler = compilerFactory.createCompiler([options]);
|
||||
return compiler.compileModuleAsync(moduleType);
|
||||
}
|
||||
|
||||
function compileNgModuleFactory__POST_NGCC__(injector, options, moduleType) {
|
||||
function compileNgModuleFactory__POST_R3__(injector, options, moduleType) {
|
||||
ngDevMode && assertNgModuleType(moduleType);
|
||||
return Promise.resolve(new R3NgModuleFactory(moduleType));
|
||||
}
|
||||
@ -1179,7 +1179,7 @@ describe('Fesm2015ReflectionHost', () => {
|
||||
const file = program.getSourceFile(MARKER_FILE.name) !;
|
||||
const declarations = host.getSwitchableDeclarations(file);
|
||||
expect(declarations.map(d => [d.name.getText(), d.initializer !.getText()])).toEqual([
|
||||
['compileNgModuleFactory', 'compileNgModuleFactory__PRE_NGCC__']
|
||||
['compileNgModuleFactory', 'compileNgModuleFactory__PRE_R3__']
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -48,16 +48,16 @@ export class C {}
|
||||
C.decorators = [
|
||||
{ type: Directive, args: [{ selector: '[c]' }] },
|
||||
];
|
||||
let compileNgModuleFactory = compileNgModuleFactory__PRE_NGCC__;
|
||||
let badlyFormattedVariable = __PRE_NGCC__badlyFormattedVariable;
|
||||
let compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;
|
||||
let badlyFormattedVariable = __PRE_R3__badlyFormattedVariable;
|
||||
|
||||
function compileNgModuleFactory__PRE_NGCC__(injector, options, moduleType) {
|
||||
function compileNgModuleFactory__PRE_R3__(injector, options, moduleType) {
|
||||
const compilerFactory = injector.get(CompilerFactory);
|
||||
const compiler = compilerFactory.createCompiler([options]);
|
||||
return compiler.compileModuleAsync(moduleType);
|
||||
}
|
||||
|
||||
function compileNgModuleFactory__POST_NGCC__(injector, options, moduleType) {
|
||||
function compileNgModuleFactory__POST_R3__(injector, options, moduleType) {
|
||||
ngDevMode && assertNgModuleType(moduleType);
|
||||
return Promise.resolve(new R3NgModuleFactory(moduleType));
|
||||
}
|
||||
@ -146,17 +146,15 @@ export class A {}`);
|
||||
renderer.rewriteSwitchableDeclarations(
|
||||
output, file, switchMarkerAnalyses.get(sourceFile) !.declarations);
|
||||
expect(output.toString())
|
||||
.not.toContain(`let compileNgModuleFactory = compileNgModuleFactory__PRE_NGCC__;`);
|
||||
.not.toContain(`let compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;`);
|
||||
expect(output.toString())
|
||||
.toContain(`let badlyFormattedVariable = __PRE_NGCC__badlyFormattedVariable;`);
|
||||
.toContain(`let badlyFormattedVariable = __PRE_R3__badlyFormattedVariable;`);
|
||||
expect(output.toString())
|
||||
.toContain(`let compileNgModuleFactory = compileNgModuleFactory__POST_NGCC__;`);
|
||||
.toContain(`let compileNgModuleFactory = compileNgModuleFactory__POST_R3__;`);
|
||||
expect(output.toString())
|
||||
.toContain(
|
||||
`function compileNgModuleFactory__PRE_NGCC__(injector, options, moduleType) {`);
|
||||
.toContain(`function compileNgModuleFactory__PRE_R3__(injector, options, moduleType) {`);
|
||||
expect(output.toString())
|
||||
.toContain(
|
||||
`function compileNgModuleFactory__POST_NGCC__(injector, options, moduleType) {`);
|
||||
.toContain(`function compileNgModuleFactory__POST_R3__(injector, options, moduleType) {`);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -55,15 +55,15 @@ var C = (function() {
|
||||
return C;
|
||||
}());
|
||||
|
||||
var compileNgModuleFactory = compileNgModuleFactory__PRE_NGCC__;
|
||||
var badlyFormattedVariable = __PRE_NGCC__badlyFormattedVariable;
|
||||
function compileNgModuleFactory__PRE_NGCC__(injector, options, moduleType) {
|
||||
var compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;
|
||||
var badlyFormattedVariable = __PRE_R3__badlyFormattedVariable;
|
||||
function compileNgModuleFactory__PRE_R3__(injector, options, moduleType) {
|
||||
const compilerFactory = injector.get(CompilerFactory);
|
||||
const compiler = compilerFactory.createCompiler([options]);
|
||||
return compiler.compileModuleAsync(moduleType);
|
||||
}
|
||||
|
||||
function compileNgModuleFactory__POST_NGCC__(injector, options, moduleType) {
|
||||
function compileNgModuleFactory__POST_R3__(injector, options, moduleType) {
|
||||
ngDevMode && assertNgModuleType(moduleType);
|
||||
return Promise.resolve(new R3NgModuleFactory(moduleType));
|
||||
}
|
||||
@ -166,17 +166,15 @@ var A = (function() {`);
|
||||
renderer.rewriteSwitchableDeclarations(
|
||||
output, file, switchMarkerAnalyses.get(sourceFile) !.declarations);
|
||||
expect(output.toString())
|
||||
.not.toContain(`var compileNgModuleFactory = compileNgModuleFactory__PRE_NGCC__;`);
|
||||
.not.toContain(`var compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;`);
|
||||
expect(output.toString())
|
||||
.toContain(`var badlyFormattedVariable = __PRE_NGCC__badlyFormattedVariable;`);
|
||||
.toContain(`var badlyFormattedVariable = __PRE_R3__badlyFormattedVariable;`);
|
||||
expect(output.toString())
|
||||
.toContain(`var compileNgModuleFactory = compileNgModuleFactory__POST_NGCC__;`);
|
||||
.toContain(`var compileNgModuleFactory = compileNgModuleFactory__POST_R3__;`);
|
||||
expect(output.toString())
|
||||
.toContain(
|
||||
`function compileNgModuleFactory__PRE_NGCC__(injector, options, moduleType) {`);
|
||||
.toContain(`function compileNgModuleFactory__PRE_R3__(injector, options, moduleType) {`);
|
||||
expect(output.toString())
|
||||
.toContain(
|
||||
`function compileNgModuleFactory__POST_NGCC__(injector, options, moduleType) {`);
|
||||
.toContain(`function compileNgModuleFactory__POST_R3__(injector, options, moduleType) {`);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,63 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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 path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
const TS_DTS_SUFFIX = /(\.d)?\.ts$/;
|
||||
|
||||
/**
|
||||
* Generates ts.SourceFiles which contain variable declarations for NgFactories for every exported
|
||||
* class of an input ts.SourceFile.
|
||||
*/
|
||||
export class FactoryGenerator {
|
||||
factoryFor(original: ts.SourceFile, genFilePath: string): ts.SourceFile {
|
||||
const relativePathToSource =
|
||||
'./' + path.posix.basename(original.fileName).replace(TS_DTS_SUFFIX, '');
|
||||
// Collect a list of classes that need to have factory types emitted for them.
|
||||
const symbolNames = original
|
||||
.statements
|
||||
// Pick out top level class declarations...
|
||||
.filter(ts.isClassDeclaration)
|
||||
// which are named, exported, and have decorators.
|
||||
.filter(
|
||||
decl => isExported(decl) && decl.decorators !== undefined &&
|
||||
decl.name !== undefined)
|
||||
// Grab the symbol name.
|
||||
.map(decl => decl.name !.text);
|
||||
|
||||
// For each symbol name, generate a constant export of the corresponding NgFactory.
|
||||
// This will encompass a lot of symbols which don't need factories, but that's okay
|
||||
// because it won't miss any that do.
|
||||
const varLines = symbolNames.map(
|
||||
name => `export const ${name}NgFactory = new i0.ɵNgModuleFactory(${name});`);
|
||||
const sourceText = [
|
||||
// This might be incorrect if the current package being compiled is Angular core, but it's
|
||||
// okay to leave in at type checking time. TypeScript can handle this reference via its path
|
||||
// mapping, but downstream bundlers can't. If the current package is core itself, this will be
|
||||
// replaced in the factory transformer before emit.
|
||||
`import * as i0 from '@angular/core';`,
|
||||
`import {${symbolNames.join(', ')}} from '${relativePathToSource}';`,
|
||||
...varLines,
|
||||
].join('\n');
|
||||
return ts.createSourceFile(
|
||||
genFilePath, sourceText, original.languageVersion, true, ts.ScriptKind.TS);
|
||||
}
|
||||
|
||||
computeFactoryFileMap(files: ReadonlyArray<string>): Map<string, string> {
|
||||
const map = new Map<string, string>();
|
||||
files.filter(sourceFile => !sourceFile.endsWith('.d.ts'))
|
||||
.forEach(sourceFile => map.set(sourceFile.replace(/\.ts$/, '.ngfactory.ts'), sourceFile));
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
function isExported(decl: ts.Declaration): boolean {
|
||||
return decl.modifiers !== undefined &&
|
||||
decl.modifiers.some(mod => mod.kind == ts.SyntaxKind.ExportKeyword);
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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 ts from 'typescript';
|
||||
|
||||
import {relativePathBetween} from '../../util/src/path';
|
||||
|
||||
const STRIP_NG_FACTORY = /(.*)NgFactory$/;
|
||||
|
||||
export interface FactoryInfo {
|
||||
sourceFilePath: string;
|
||||
moduleSymbolNames: Set<string>;
|
||||
}
|
||||
|
||||
export function generatedFactoryTransform(
|
||||
factoryMap: Map<string, FactoryInfo>,
|
||||
coreImportsFrom: ts.SourceFile | null): ts.TransformerFactory<ts.SourceFile> {
|
||||
return (context: ts.TransformationContext): ts.Transformer<ts.SourceFile> => {
|
||||
return (file: ts.SourceFile): ts.SourceFile => {
|
||||
return transformFactorySourceFile(factoryMap, context, coreImportsFrom, file);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function transformFactorySourceFile(
|
||||
factoryMap: Map<string, FactoryInfo>, context: ts.TransformationContext,
|
||||
coreImportsFrom: ts.SourceFile | null, file: ts.SourceFile): ts.SourceFile {
|
||||
// If this is not a generated file, it won't have factory info associated with it.
|
||||
if (!factoryMap.has(file.fileName)) {
|
||||
// Don't transform non-generated code.
|
||||
return file;
|
||||
}
|
||||
|
||||
const {moduleSymbolNames, sourceFilePath} = factoryMap.get(file.fileName) !;
|
||||
|
||||
const clone = ts.getMutableClone(file);
|
||||
|
||||
const transformedStatements = file.statements.map(stmt => {
|
||||
if (coreImportsFrom !== null && ts.isImportDeclaration(stmt) &&
|
||||
ts.isStringLiteral(stmt.moduleSpecifier) && stmt.moduleSpecifier.text === '@angular/core') {
|
||||
const path = relativePathBetween(sourceFilePath, coreImportsFrom.fileName);
|
||||
if (path !== null) {
|
||||
return ts.updateImportDeclaration(
|
||||
stmt, stmt.decorators, stmt.modifiers, stmt.importClause, ts.createStringLiteral(path));
|
||||
} else {
|
||||
return ts.createNotEmittedStatement(stmt);
|
||||
}
|
||||
} else if (ts.isVariableStatement(stmt) && stmt.declarationList.declarations.length === 1) {
|
||||
const decl = stmt.declarationList.declarations[0];
|
||||
if (ts.isIdentifier(decl.name)) {
|
||||
const match = STRIP_NG_FACTORY.exec(decl.name.text);
|
||||
if (match === null || !moduleSymbolNames.has(match[1])) {
|
||||
// Remove the given factory as it wasn't actually for an NgModule.
|
||||
return ts.createNotEmittedStatement(stmt);
|
||||
}
|
||||
}
|
||||
return stmt;
|
||||
} else {
|
||||
return stmt;
|
||||
}
|
||||
});
|
||||
if (!transformedStatements.some(ts.isVariableStatement)) {
|
||||
// If the resulting file has no factories, include an empty export to
|
||||
// satisfy closure compiler.
|
||||
transformedStatements.push(ts.createVariableStatement(
|
||||
[ts.createModifier(ts.SyntaxKind.ExportKeyword)],
|
||||
ts.createVariableDeclarationList(
|
||||
[ts.createVariableDeclaration('ɵNonEmptyModule', undefined, ts.createTrue())],
|
||||
ts.NodeFlags.Const)));
|
||||
}
|
||||
clone.statements = ts.createNodeArray(transformedStatements);
|
||||
return clone;
|
||||
}
|
@ -15,9 +15,10 @@ import {nocollapseHack} from '../transformers/nocollapse_hack';
|
||||
|
||||
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ResourceLoader, SelectorScopeRegistry} from './annotations';
|
||||
import {BaseDefDecoratorHandler} from './annotations/src/base_def';
|
||||
import {FactoryGenerator, FactoryInfo, GeneratedFactoryHostWrapper, generatedFactoryTransform} from './factories';
|
||||
import {TypeScriptReflectionHost} from './metadata';
|
||||
import {FileResourceLoader, HostResourceLoader} from './resource_loader';
|
||||
import {FactoryGenerator, FactoryInfo, GeneratedShimsHostWrapper, SummaryGenerator, generatedFactoryTransform} from './shims';
|
||||
import {ivySwitchTransform} from './switch';
|
||||
import {IvyCompilation, ivyTransformFactory} from './transform';
|
||||
import {TypeCheckContext, TypeCheckProgramHost} from './typecheck';
|
||||
|
||||
@ -50,13 +51,16 @@ export class NgtscProgram implements api.Program {
|
||||
this.resourceLoader = host.readResource !== undefined ?
|
||||
new HostResourceLoader(host.readResource.bind(host)) :
|
||||
new FileResourceLoader();
|
||||
const shouldGenerateFactories = options.allowEmptyCodegenFiles || false;
|
||||
const shouldGenerateShims = options.allowEmptyCodegenFiles || false;
|
||||
this.host = host;
|
||||
let rootFiles = [...rootNames];
|
||||
if (shouldGenerateFactories) {
|
||||
const generator = new FactoryGenerator();
|
||||
const factoryFileMap = generator.computeFactoryFileMap(rootNames);
|
||||
rootFiles.push(...Array.from(factoryFileMap.keys()));
|
||||
if (shouldGenerateShims) {
|
||||
// Summary generation.
|
||||
const summaryGenerator = SummaryGenerator.forRootFiles(rootNames);
|
||||
|
||||
// Factory generation.
|
||||
const factoryGenerator = FactoryGenerator.forRootFiles(rootNames);
|
||||
const factoryFileMap = factoryGenerator.factoryFileMap;
|
||||
this.factoryToSourceInfo = new Map<string, FactoryInfo>();
|
||||
this.sourceToFactorySymbols = new Map<string, Set<string>>();
|
||||
factoryFileMap.forEach((sourceFilePath, factoryPath) => {
|
||||
@ -64,7 +68,10 @@ export class NgtscProgram implements api.Program {
|
||||
this.sourceToFactorySymbols !.set(sourceFilePath, moduleSymbolNames);
|
||||
this.factoryToSourceInfo !.set(factoryPath, {sourceFilePath, moduleSymbolNames});
|
||||
});
|
||||
this.host = new GeneratedFactoryHostWrapper(host, generator, factoryFileMap);
|
||||
|
||||
const factoryFileNames = Array.from(factoryFileMap.keys());
|
||||
rootFiles.push(...factoryFileNames, ...summaryGenerator.getSummaryFileNames());
|
||||
this.host = new GeneratedShimsHostWrapper(host, [summaryGenerator, factoryGenerator]);
|
||||
}
|
||||
|
||||
this.tsProgram =
|
||||
@ -177,6 +184,9 @@ export class NgtscProgram implements api.Program {
|
||||
if (this.factoryToSourceInfo !== null) {
|
||||
transforms.push(generatedFactoryTransform(this.factoryToSourceInfo, this.coreImportsFrom));
|
||||
}
|
||||
if (this.isCore) {
|
||||
transforms.push(ivySwitchTransform);
|
||||
}
|
||||
// Run the emit, including a custom transformer that will downlevel the Ivy decorators in code.
|
||||
const emitResult = emitCallback({
|
||||
program: this.tsProgram,
|
||||
|
@ -3,12 +3,12 @@ package(default_visibility = ["//visibility:public"])
|
||||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "factories",
|
||||
name = "shims",
|
||||
srcs = glob([
|
||||
"index.ts",
|
||||
"src/**/*.ts",
|
||||
]),
|
||||
module_name = "@angular/compiler-cli/src/ngtsc/factories",
|
||||
module_name = "@angular/compiler-cli/src/ngtsc/shims",
|
||||
deps = [
|
||||
"//packages/compiler",
|
||||
"//packages/compiler-cli/src/ngtsc/host",
|
@ -8,6 +8,6 @@
|
||||
|
||||
/// <reference types="node" />
|
||||
|
||||
export {FactoryGenerator} from './src/generator';
|
||||
export {GeneratedFactoryHostWrapper} from './src/host';
|
||||
export {FactoryInfo, generatedFactoryTransform} from './src/transform';
|
||||
export {FactoryGenerator, FactoryInfo, generatedFactoryTransform} from './src/factory_generator';
|
||||
export {GeneratedShimsHostWrapper} from './src/host';
|
||||
export {SummaryGenerator} from './src/summary_generator';
|
144
packages/compiler-cli/src/ngtsc/shims/src/factory_generator.ts
Normal file
144
packages/compiler-cli/src/ngtsc/shims/src/factory_generator.ts
Normal file
@ -0,0 +1,144 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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 path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {relativePathBetween} from '../../util/src/path';
|
||||
|
||||
import {ShimGenerator} from './host';
|
||||
import {isNonDeclarationTsFile} from './util';
|
||||
|
||||
const TS_DTS_SUFFIX = /(\.d)?\.ts$/;
|
||||
const STRIP_NG_FACTORY = /(.*)NgFactory$/;
|
||||
|
||||
/**
|
||||
* Generates ts.SourceFiles which contain variable declarations for NgFactories for every exported
|
||||
* class of an input ts.SourceFile.
|
||||
*/
|
||||
export class FactoryGenerator implements ShimGenerator {
|
||||
private constructor(private map: Map<string, string>) {}
|
||||
|
||||
get factoryFileMap(): Map<string, string> { return this.map; }
|
||||
|
||||
getOriginalSourceOfShim(fileName: string): string|null { return this.map.get(fileName) || null; }
|
||||
|
||||
generate(original: ts.SourceFile, genFilePath: string): ts.SourceFile {
|
||||
const relativePathToSource =
|
||||
'./' + path.posix.basename(original.fileName).replace(TS_DTS_SUFFIX, '');
|
||||
// Collect a list of classes that need to have factory types emitted for them. This list is
|
||||
// overly broad as at this point the ts.TypeChecker hasn't been created, and can't be used to
|
||||
// semantically understand which decorated types are actually decorated with Angular decorators.
|
||||
//
|
||||
// The exports generated here are pruned in the factory transform during emit.
|
||||
const symbolNames = original
|
||||
.statements
|
||||
// Pick out top level class declarations...
|
||||
.filter(ts.isClassDeclaration)
|
||||
// which are named, exported, and have decorators.
|
||||
.filter(
|
||||
decl => isExported(decl) && decl.decorators !== undefined &&
|
||||
decl.name !== undefined)
|
||||
// Grab the symbol name.
|
||||
.map(decl => decl.name !.text);
|
||||
|
||||
// For each symbol name, generate a constant export of the corresponding NgFactory.
|
||||
// This will encompass a lot of symbols which don't need factories, but that's okay
|
||||
// because it won't miss any that do.
|
||||
const varLines = symbolNames.map(
|
||||
name => `export const ${name}NgFactory = new i0.ɵNgModuleFactory(${name});`);
|
||||
const sourceText = [
|
||||
// This might be incorrect if the current package being compiled is Angular core, but it's
|
||||
// okay to leave in at type checking time. TypeScript can handle this reference via its path
|
||||
// mapping, but downstream bundlers can't. If the current package is core itself, this will be
|
||||
// replaced in the factory transformer before emit.
|
||||
`import * as i0 from '@angular/core';`,
|
||||
`import {${symbolNames.join(', ')}} from '${relativePathToSource}';`,
|
||||
...varLines,
|
||||
].join('\n');
|
||||
return ts.createSourceFile(
|
||||
genFilePath, sourceText, original.languageVersion, true, ts.ScriptKind.TS);
|
||||
}
|
||||
|
||||
static forRootFiles(files: ReadonlyArray<string>): FactoryGenerator {
|
||||
const map = new Map<string, string>();
|
||||
files.filter(sourceFile => isNonDeclarationTsFile(sourceFile))
|
||||
.forEach(sourceFile => map.set(sourceFile.replace(/\.ts$/, '.ngfactory.ts'), sourceFile));
|
||||
return new FactoryGenerator(map);
|
||||
}
|
||||
}
|
||||
|
||||
function isExported(decl: ts.Declaration): boolean {
|
||||
return decl.modifiers !== undefined &&
|
||||
decl.modifiers.some(mod => mod.kind == ts.SyntaxKind.ExportKeyword);
|
||||
}
|
||||
|
||||
export interface FactoryInfo {
|
||||
sourceFilePath: string;
|
||||
moduleSymbolNames: Set<string>;
|
||||
}
|
||||
|
||||
export function generatedFactoryTransform(
|
||||
factoryMap: Map<string, FactoryInfo>,
|
||||
coreImportsFrom: ts.SourceFile | null): ts.TransformerFactory<ts.SourceFile> {
|
||||
return (context: ts.TransformationContext): ts.Transformer<ts.SourceFile> => {
|
||||
return (file: ts.SourceFile): ts.SourceFile => {
|
||||
return transformFactorySourceFile(factoryMap, context, coreImportsFrom, file);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function transformFactorySourceFile(
|
||||
factoryMap: Map<string, FactoryInfo>, context: ts.TransformationContext,
|
||||
coreImportsFrom: ts.SourceFile | null, file: ts.SourceFile): ts.SourceFile {
|
||||
// If this is not a generated file, it won't have factory info associated with it.
|
||||
if (!factoryMap.has(file.fileName)) {
|
||||
// Don't transform non-generated code.
|
||||
return file;
|
||||
}
|
||||
|
||||
const {moduleSymbolNames, sourceFilePath} = factoryMap.get(file.fileName) !;
|
||||
|
||||
const clone = ts.getMutableClone(file);
|
||||
|
||||
const transformedStatements = file.statements.map(stmt => {
|
||||
if (coreImportsFrom !== null && ts.isImportDeclaration(stmt) &&
|
||||
ts.isStringLiteral(stmt.moduleSpecifier) && stmt.moduleSpecifier.text === '@angular/core') {
|
||||
const path = relativePathBetween(sourceFilePath, coreImportsFrom.fileName);
|
||||
if (path !== null) {
|
||||
return ts.updateImportDeclaration(
|
||||
stmt, stmt.decorators, stmt.modifiers, stmt.importClause, ts.createStringLiteral(path));
|
||||
} else {
|
||||
return ts.createNotEmittedStatement(stmt);
|
||||
}
|
||||
} else if (ts.isVariableStatement(stmt) && stmt.declarationList.declarations.length === 1) {
|
||||
const decl = stmt.declarationList.declarations[0];
|
||||
if (ts.isIdentifier(decl.name)) {
|
||||
const match = STRIP_NG_FACTORY.exec(decl.name.text);
|
||||
if (match === null || !moduleSymbolNames.has(match[1])) {
|
||||
// Remove the given factory as it wasn't actually for an NgModule.
|
||||
return ts.createNotEmittedStatement(stmt);
|
||||
}
|
||||
}
|
||||
return stmt;
|
||||
} else {
|
||||
return stmt;
|
||||
}
|
||||
});
|
||||
if (!transformedStatements.some(ts.isVariableStatement)) {
|
||||
// If the resulting file has no factories, include an empty export to
|
||||
// satisfy closure compiler.
|
||||
transformedStatements.push(ts.createVariableStatement(
|
||||
[ts.createModifier(ts.SyntaxKind.ExportKeyword)],
|
||||
ts.createVariableDeclarationList(
|
||||
[ts.createVariableDeclaration('ɵNonEmptyModule', undefined, ts.createTrue())],
|
||||
ts.NodeFlags.Const)));
|
||||
}
|
||||
clone.statements = ts.createNodeArray(transformedStatements);
|
||||
return clone;
|
||||
}
|
@ -9,15 +9,26 @@
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {FactoryGenerator} from './generator';
|
||||
export interface ShimGenerator {
|
||||
/**
|
||||
* Get the original source file for the given shim path, the contents of which determine the
|
||||
* contents of the shim file.
|
||||
*
|
||||
* If this returns `null` then the given file was not a shim file handled by this generator.
|
||||
*/
|
||||
getOriginalSourceOfShim(fileName: string): string|null;
|
||||
|
||||
/**
|
||||
* Generate a shim's `ts.SourceFile` for the given original file.
|
||||
*/
|
||||
generate(original: ts.SourceFile, genFileName: string): ts.SourceFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around a `ts.CompilerHost` which supports generated files.
|
||||
*/
|
||||
export class GeneratedFactoryHostWrapper implements ts.CompilerHost {
|
||||
constructor(
|
||||
private delegate: ts.CompilerHost, private generator: FactoryGenerator,
|
||||
private factoryToSourceMap: Map<string, string>) {
|
||||
export class GeneratedShimsHostWrapper implements ts.CompilerHost {
|
||||
constructor(private delegate: ts.CompilerHost, private shimGenerators: ShimGenerator[]) {
|
||||
if (delegate.resolveTypeReferenceDirectives) {
|
||||
// Backward compatibility with TypeScript 2.9 and older since return
|
||||
// type has changed from (ts.ResolvedTypeReferenceDirective | undefined)[]
|
||||
@ -38,14 +49,20 @@ export class GeneratedFactoryHostWrapper implements ts.CompilerHost {
|
||||
onError?: ((message: string) => void)|undefined,
|
||||
shouldCreateNewSourceFile?: boolean|undefined): ts.SourceFile|undefined {
|
||||
const canonical = this.getCanonicalFileName(fileName);
|
||||
if (this.factoryToSourceMap.has(canonical)) {
|
||||
const sourceFileName = this.getCanonicalFileName(this.factoryToSourceMap.get(canonical) !);
|
||||
const sourceFile = this.delegate.getSourceFile(
|
||||
sourceFileName, languageVersion, onError, shouldCreateNewSourceFile);
|
||||
if (sourceFile === undefined) {
|
||||
return undefined;
|
||||
for (let i = 0; i < this.shimGenerators.length; i++) {
|
||||
const generator = this.shimGenerators[i];
|
||||
const originalFile = generator.getOriginalSourceOfShim(canonical);
|
||||
if (originalFile !== null) {
|
||||
// This shim generator has recognized the filename being requested, and is now responsible
|
||||
// for generating its contents, based on the contents of the original file it has requested.
|
||||
const originalSource = this.delegate.getSourceFile(
|
||||
originalFile, languageVersion, onError, shouldCreateNewSourceFile);
|
||||
if (originalSource === undefined) {
|
||||
// The original requested file doesn't exist, so the shim cannot exist either.
|
||||
return undefined;
|
||||
}
|
||||
return generator.generate(originalSource, fileName);
|
||||
}
|
||||
return this.generator.factoryFor(sourceFile, fileName);
|
||||
}
|
||||
return this.delegate.getSourceFile(
|
||||
fileName, languageVersion, onError, shouldCreateNewSourceFile);
|
||||
@ -75,7 +92,11 @@ export class GeneratedFactoryHostWrapper implements ts.CompilerHost {
|
||||
getNewLine(): string { return this.delegate.getNewLine(); }
|
||||
|
||||
fileExists(fileName: string): boolean {
|
||||
return this.factoryToSourceMap.has(fileName) || this.delegate.fileExists(fileName);
|
||||
const canonical = this.getCanonicalFileName(fileName);
|
||||
// Consider the file as existing whenever 1) it really does exist in the delegate host, or
|
||||
// 2) at least one of the shim generators recognizes it.
|
||||
return this.delegate.fileExists(fileName) ||
|
||||
this.shimGenerators.some(gen => gen.getOriginalSourceOfShim(canonical) !== null);
|
||||
}
|
||||
|
||||
readFile(fileName: string): string|undefined { return this.delegate.readFile(fileName); }
|
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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 ts from 'typescript';
|
||||
|
||||
import {ShimGenerator} from './host';
|
||||
import {isNonDeclarationTsFile} from './util';
|
||||
|
||||
export class SummaryGenerator implements ShimGenerator {
|
||||
private constructor(private map: Map<string, string>) {}
|
||||
|
||||
getSummaryFileNames(): string[] { return Array.from(this.map.keys()); }
|
||||
|
||||
getOriginalSourceOfShim(fileName: string): string|null { return this.map.get(fileName) || null; }
|
||||
|
||||
generate(original: ts.SourceFile, genFilePath: string): ts.SourceFile {
|
||||
// Collect a list of classes that need to have factory types emitted for them. This list is
|
||||
// overly broad as at this point the ts.TypeChecker has not been created and so it can't be used
|
||||
// to semantically understand which decorators are Angular decorators. It's okay to output an
|
||||
// overly broad set of summary exports as the exports are no-ops anyway, and summaries are a
|
||||
// compatibility layer which will be removed after Ivy is enabled.
|
||||
const symbolNames = original
|
||||
.statements
|
||||
// Pick out top level class declarations...
|
||||
.filter(ts.isClassDeclaration)
|
||||
// which are named, exported, and have decorators.
|
||||
.filter(
|
||||
decl => isExported(decl) && decl.decorators !== undefined &&
|
||||
decl.name !== undefined)
|
||||
// Grab the symbol name.
|
||||
.map(decl => decl.name !.text);
|
||||
|
||||
const varLines = symbolNames.map(name => `export const ${name}NgSummary: any = null;`);
|
||||
|
||||
if (varLines.length === 0) {
|
||||
// In the event there are no other exports, add an empty export to ensure the generated
|
||||
// summary file is still an ES module.
|
||||
varLines.push(`export const ɵempty = null;`);
|
||||
}
|
||||
const sourceText = varLines.join('\n');
|
||||
return ts.createSourceFile(
|
||||
genFilePath, sourceText, original.languageVersion, true, ts.ScriptKind.TS);
|
||||
}
|
||||
|
||||
static forRootFiles(files: ReadonlyArray<string>): SummaryGenerator {
|
||||
const map = new Map<string, string>();
|
||||
files.filter(sourceFile => isNonDeclarationTsFile(sourceFile))
|
||||
.forEach(sourceFile => map.set(sourceFile.replace(/\.ts$/, '.ngsummary.ts'), sourceFile));
|
||||
return new SummaryGenerator(map);
|
||||
}
|
||||
}
|
||||
|
||||
function isExported(decl: ts.Declaration): boolean {
|
||||
return decl.modifiers !== undefined &&
|
||||
decl.modifiers.some(mod => mod.kind == ts.SyntaxKind.ExportKeyword);
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
@ -7,4 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export * from './ivy_switch_on';
|
||||
const TS_FILE = /\.tsx?$/;
|
||||
const D_TS_FILE = /\.d\.ts$/;
|
||||
|
||||
export function isNonDeclarationTsFile(file: string): boolean {
|
||||
return TS_FILE.exec(file) !== null && D_TS_FILE.exec(file) === null;
|
||||
}
|
19
packages/compiler-cli/src/ngtsc/switch/BUILD.bazel
Normal file
19
packages/compiler-cli/src/ngtsc/switch/BUILD.bazel
Normal file
@ -0,0 +1,19 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "switch",
|
||||
srcs = glob([
|
||||
"index.ts",
|
||||
"src/**/*.ts",
|
||||
]),
|
||||
module_name = "@angular/compiler-cli/src/ngtsc/switch",
|
||||
deps = [
|
||||
"//packages/compiler",
|
||||
"//packages/compiler-cli/src/ngtsc/host",
|
||||
"//packages/compiler-cli/src/ngtsc/metadata",
|
||||
"//packages/compiler-cli/src/ngtsc/util",
|
||||
"@ngdeps//typescript",
|
||||
],
|
||||
)
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
@ -7,4 +6,4 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export * from './ivy_switch_on';
|
||||
export {ivySwitchTransform} from './src/switch';
|
152
packages/compiler-cli/src/ngtsc/switch/src/switch.ts
Normal file
152
packages/compiler-cli/src/ngtsc/switch/src/switch.ts
Normal file
@ -0,0 +1,152 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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 ts from 'typescript';
|
||||
|
||||
const IVY_SWITCH_PRE_SUFFIX = '__PRE_R3__';
|
||||
const IVY_SWITCH_POST_SUFFIX = '__POST_R3__';
|
||||
|
||||
export function ivySwitchTransform(_: ts.TransformationContext): ts.Transformer<ts.SourceFile> {
|
||||
return flipIvySwitchInFile;
|
||||
}
|
||||
|
||||
function flipIvySwitchInFile(sf: ts.SourceFile): ts.SourceFile {
|
||||
// To replace the statements array, it must be copied. This only needs to happen if a statement
|
||||
// must actually be replaced within the array, so the newStatements array is lazily initialized.
|
||||
let newStatements: ts.Statement[]|undefined = undefined;
|
||||
|
||||
// Iterate over the statements in the file.
|
||||
for (let i = 0; i < sf.statements.length; i++) {
|
||||
const statement = sf.statements[i];
|
||||
|
||||
// Skip over everything that isn't a variable statement.
|
||||
if (!ts.isVariableStatement(statement) || !hasIvySwitches(statement)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// This statement needs to be replaced. Check if the newStatements array needs to be lazily
|
||||
// initialized to a copy of the original statements.
|
||||
if (newStatements === undefined) {
|
||||
newStatements = [...sf.statements];
|
||||
}
|
||||
|
||||
// Flip any switches in the VariableStatement. If there were any, a new statement will be
|
||||
// returned; otherwise the old statement will be.
|
||||
newStatements[i] = flipIvySwitchesInVariableStatement(statement, sf.statements);
|
||||
}
|
||||
|
||||
// Only update the statements in the SourceFile if any have changed.
|
||||
if (newStatements !== undefined) {
|
||||
sf.statements = ts.createNodeArray(newStatements);
|
||||
}
|
||||
return sf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for the ts.Identifier of a ts.Declaration with this name.
|
||||
*
|
||||
* The real identifier is needed (rather than fabricating one) as TypeScript decides how to
|
||||
* reference this identifier based on information stored against its node in the AST, which a
|
||||
* synthetic node would not have. In particular, since the post-switch variable is often exported,
|
||||
* TypeScript needs to know this so it can write `exports.VAR` instead of just `VAR` when emitting
|
||||
* code.
|
||||
*
|
||||
* Only variable, function, and class declarations are currently searched.
|
||||
*/
|
||||
function findPostSwitchIdentifier(
|
||||
statements: ReadonlyArray<ts.Statement>, name: string): ts.Identifier|null {
|
||||
for (const stmt of statements) {
|
||||
if (ts.isVariableStatement(stmt)) {
|
||||
const decl = stmt.declarationList.declarations.find(
|
||||
decl => ts.isIdentifier(decl.name) && decl.name.text === name);
|
||||
if (decl !== undefined) {
|
||||
return decl.name as ts.Identifier;
|
||||
}
|
||||
} else if (ts.isFunctionDeclaration(stmt) || ts.isClassDeclaration(stmt)) {
|
||||
if (stmt.name !== undefined && ts.isIdentifier(stmt.name) && stmt.name.text === name) {
|
||||
return stmt.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flip any Ivy switches which are discovered in the given ts.VariableStatement.
|
||||
*/
|
||||
function flipIvySwitchesInVariableStatement(
|
||||
stmt: ts.VariableStatement, statements: ReadonlyArray<ts.Statement>): ts.VariableStatement {
|
||||
// Build a new list of variable declarations. Specific declarations that are initialized to a
|
||||
// pre-switch identifier will be replaced with a declaration initialized to the post-switch
|
||||
// identifier.
|
||||
const newDeclarations = [...stmt.declarationList.declarations];
|
||||
for (let i = 0; i < newDeclarations.length; i++) {
|
||||
const decl = newDeclarations[i];
|
||||
|
||||
// Skip declarations that aren't initialized to an identifier.
|
||||
if (decl.initializer === undefined || !ts.isIdentifier(decl.initializer)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip declarations that aren't Ivy switches.
|
||||
if (!decl.initializer.text.endsWith(IVY_SWITCH_PRE_SUFFIX)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine the name of the post-switch variable.
|
||||
const postSwitchName =
|
||||
decl.initializer.text.replace(IVY_SWITCH_PRE_SUFFIX, IVY_SWITCH_POST_SUFFIX);
|
||||
|
||||
// Find the post-switch variable identifier. If one can't be found, it's an error. This is
|
||||
// reported as a thrown error and not a diagnostic as transformers cannot output diagnostics.
|
||||
let newIdentifier = findPostSwitchIdentifier(statements, postSwitchName);
|
||||
if (newIdentifier === null) {
|
||||
throw new Error(
|
||||
`Unable to find identifier ${postSwitchName} in ${stmt.getSourceFile().fileName} for the Ivy switch.`);
|
||||
}
|
||||
|
||||
// Copy the identifier with updateIdentifier(). This copies the internal information which
|
||||
// allows TS to write a correct reference to the identifier.
|
||||
newIdentifier = ts.updateIdentifier(newIdentifier);
|
||||
|
||||
newDeclarations[i] = ts.updateVariableDeclaration(
|
||||
/* node */ decl,
|
||||
/* name */ decl.name,
|
||||
/* type */ decl.type,
|
||||
/* initializer */ newIdentifier);
|
||||
|
||||
// Keeping parent pointers up to date is important for emit.
|
||||
newIdentifier.parent = newDeclarations[i];
|
||||
}
|
||||
|
||||
const newDeclList = ts.updateVariableDeclarationList(
|
||||
/* declarationList */ stmt.declarationList,
|
||||
/* declarations */ newDeclarations);
|
||||
|
||||
const newStmt = ts.updateVariableStatement(
|
||||
/* statement */ stmt,
|
||||
/* modifiers */ stmt.modifiers,
|
||||
/* declarationList */ newDeclList);
|
||||
|
||||
// Keeping parent pointers up to date is important for emit.
|
||||
for (const decl of newDeclarations) {
|
||||
decl.parent = newDeclList;
|
||||
}
|
||||
newDeclList.parent = newStmt;
|
||||
newStmt.parent = stmt.parent;
|
||||
return newStmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given VariableStatement has any Ivy switch variables.
|
||||
*/
|
||||
function hasIvySwitches(stmt: ts.VariableStatement) {
|
||||
return stmt.declarationList.declarations.some(
|
||||
decl => decl.initializer !== undefined && ts.isIdentifier(decl.initializer) &&
|
||||
decl.initializer.text.endsWith(IVY_SWITCH_PRE_SUFFIX));
|
||||
}
|
@ -10,11 +10,15 @@ import {GeneratedFile} from '@angular/compiler';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ivySwitchTransform} from '../ngtsc/switch';
|
||||
import * as api from '../transformers/api';
|
||||
|
||||
|
||||
/**
|
||||
* An implementation of the `Program` API which behaves like plain `tsc` and does not include any
|
||||
* Angular-specific behavior whatsoever.
|
||||
* An implementation of the `Program` API which behaves similarly to plain `tsc`.
|
||||
*
|
||||
* The only Angular specific behavior included in this `Program` is the operation of the Ivy
|
||||
* switch to turn on render3 behavior.
|
||||
*
|
||||
* This allows `ngc` to behave like `tsc` in cases where JIT code needs to be tested.
|
||||
*/
|
||||
@ -95,6 +99,7 @@ export class TscPassThroughProgram implements api.Program {
|
||||
host: this.host,
|
||||
options: this.options,
|
||||
emitOnlyDtsFiles: false,
|
||||
customTransformers: {before: [ivySwitchTransform]},
|
||||
});
|
||||
return emitResult;
|
||||
}
|
||||
|
@ -572,6 +572,38 @@ describe('ngtsc behavioral tests', () => {
|
||||
expect(emptyFactory).toContain(`export var ɵNonEmptyModule = true;`);
|
||||
});
|
||||
|
||||
it('should generate a summary stub for decorated classes in the input file only', () => {
|
||||
env.tsconfig({'allowEmptyCodegenFiles': true});
|
||||
|
||||
env.write('test.ts', `
|
||||
import {Injectable, NgModule} from '@angular/core';
|
||||
|
||||
export class NotAModule {}
|
||||
|
||||
@NgModule({})
|
||||
export class TestModule {}
|
||||
`);
|
||||
|
||||
env.driveMain();
|
||||
|
||||
const summaryContents = env.getContents('test.ngsummary.js');
|
||||
expect(summaryContents).toEqual(`export var TestModuleNgSummary = null;\n`);
|
||||
});
|
||||
|
||||
it('it should generate empty export when there are no other summary symbols, to ensure the output is a valid ES module',
|
||||
() => {
|
||||
env.tsconfig({'allowEmptyCodegenFiles': true});
|
||||
env.write('empty.ts', `
|
||||
export class NotAModule {}
|
||||
`);
|
||||
|
||||
env.driveMain();
|
||||
|
||||
const emptySummary = env.getContents('empty.ngsummary.js');
|
||||
// The empty export ensures this js file is still an ES module.
|
||||
expect(emptySummary).toEqual(`export var ɵempty = null;\n`);
|
||||
});
|
||||
|
||||
it('should compile a banana-in-a-box inside of a template', () => {
|
||||
env.tsconfig();
|
||||
env.write('test.ts', `
|
||||
|
@ -9,14 +9,7 @@ ng_module(
|
||||
"*.ts",
|
||||
"src/**/*.ts",
|
||||
],
|
||||
exclude = [
|
||||
"src/ivy_switch/compiler/index.ts",
|
||||
"src/ivy_switch/runtime/index.ts",
|
||||
],
|
||||
) + [
|
||||
":ivy_switch_compiler",
|
||||
":ivy_switch_runtime",
|
||||
],
|
||||
),
|
||||
module_name = "@angular/core",
|
||||
deps = [
|
||||
"//packages:types",
|
||||
@ -42,30 +35,3 @@ ng_package(
|
||||
"//packages/core/testing",
|
||||
],
|
||||
)
|
||||
|
||||
## Controls if Ivy is enabled. (Temporary target until we permanently switch over to Ivy)
|
||||
##
|
||||
## This file generates the `src/ivy_switch/compiler/index.ts` and `src/ivy_switch/runtime/index.ts` files which
|
||||
## reexport symbols for `ViewEngine` or `Ivy.`
|
||||
## - append `--define=compile=legacy` (default) to `bazel` command to reexport `./legacy` from each folder
|
||||
## in the 'ivy_switch' directory and use `ViewEngine`
|
||||
## - append `--define=compile=jit` to `bazel` command to rexport `./jit` from each folder in the `ivy_switch`
|
||||
## directory and use `Ivy`
|
||||
## - append `--define=compile=local` to `bazel` command to rexport `./ivy_switch/compiler/jit` and use `Ivy`
|
||||
## in the local analysis mode. (run as part of `ngtsc`)
|
||||
##
|
||||
## NOTE: `--define=compile=jit` works with any `bazel` command or target across the repo.
|
||||
##
|
||||
## See: `//tools/bazel.rc` where `--define=ivy=false` is defined as default.
|
||||
## See: `./src/ivy_switch/compiler/index.ts` for more details.
|
||||
genrule(
|
||||
name = "ivy_switch_compiler",
|
||||
outs = ["src/ivy_switch/compiler/index.ts"],
|
||||
cmd = "echo export '*' from \"'./$(compile)';\" > $@",
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "ivy_switch_runtime",
|
||||
outs = ["src/ivy_switch/runtime/index.ts"],
|
||||
cmd = "echo export '*' from \"'./$(compile)';\" > $@",
|
||||
)
|
||||
|
@ -33,9 +33,9 @@ let _platform: PlatformRef;
|
||||
|
||||
let compileNgModuleFactory:
|
||||
<M>(injector: Injector, options: CompilerOptions, moduleType: Type<M>) =>
|
||||
Promise<NgModuleFactory<M>> = compileNgModuleFactory__PRE_NGCC__;
|
||||
Promise<NgModuleFactory<M>> = compileNgModuleFactory__PRE_R3__;
|
||||
|
||||
function compileNgModuleFactory__PRE_NGCC__<M>(
|
||||
function compileNgModuleFactory__PRE_R3__<M>(
|
||||
injector: Injector, options: CompilerOptions,
|
||||
moduleType: Type<M>): Promise<NgModuleFactory<M>> {
|
||||
const compilerFactory: CompilerFactory = injector.get(CompilerFactory);
|
||||
@ -43,7 +43,7 @@ function compileNgModuleFactory__PRE_NGCC__<M>(
|
||||
return compiler.compileModuleAsync(moduleType);
|
||||
}
|
||||
|
||||
export function compileNgModuleFactory__POST_NGCC__<M>(
|
||||
export function compileNgModuleFactory__POST_R3__<M>(
|
||||
injector: Injector, options: CompilerOptions,
|
||||
moduleType: Type<M>): Promise<NgModuleFactory<M>> {
|
||||
ngDevMode && assertNgModuleType(moduleType);
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {R3_CHANGE_DETECTOR_REF_FACTORY} from '../ivy_switch/runtime/index';
|
||||
import {injectChangeDetectorRef as render3InjectChangeDetectorRef} from '../render3/view_engine_compatibility';
|
||||
|
||||
/**
|
||||
* Base class for Angular Views, provides change detection functionality.
|
||||
@ -108,5 +108,12 @@ export abstract class ChangeDetectorRef {
|
||||
abstract reattach(): void;
|
||||
|
||||
/** @internal */
|
||||
static __NG_ELEMENT_ID__: () => ChangeDetectorRef = () => R3_CHANGE_DETECTOR_REF_FACTORY();
|
||||
static __NG_ELEMENT_ID__: () => ChangeDetectorRef = () => SWITCH_CHANGE_DETECTOR_REF_FACTORY();
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const SWITCH_CHANGE_DETECTOR_REF_FACTORY__POST_R3__ = render3InjectChangeDetectorRef;
|
||||
const SWITCH_CHANGE_DETECTOR_REF_FACTORY__PRE_R3__ = (...args: any[]): any => {};
|
||||
const SWITCH_CHANGE_DETECTOR_REF_FACTORY: typeof render3InjectChangeDetectorRef =
|
||||
SWITCH_CHANGE_DETECTOR_REF_FACTORY__PRE_R3__;
|
||||
|
@ -16,7 +16,7 @@ export {Console as ɵConsole} from './console';
|
||||
export {InjectableDef as ɵInjectableDef, InjectorDef as ɵInjectorDef, getInjectableDef as ɵgetInjectableDef} from './di/defs';
|
||||
export {inject as ɵinject, setCurrentInjector as ɵsetCurrentInjector} from './di/injector';
|
||||
export {APP_ROOT as ɵAPP_ROOT} from './di/scope';
|
||||
export {ivyEnabled as ɵivyEnabled} from './ivy_switch/compiler/index';
|
||||
export {ivyEnabled as ɵivyEnabled} from './ivy_switch';
|
||||
export {ComponentFactory as ɵComponentFactory} from './linker/component_factory';
|
||||
export {CodegenComponentFactoryResolver as ɵCodegenComponentFactoryResolver} from './linker/component_factory_resolver';
|
||||
export {resolveComponentResources as ɵresolveComponentResources} from './metadata/resource_loading';
|
||||
|
@ -129,12 +129,17 @@ export { Render3DebugRendererFactory2 as ɵRender3DebugRendererFactory2 } from
|
||||
|
||||
|
||||
export {
|
||||
R3_COMPILE_NGMODULE_DEFS as ɵcompileNgModuleDefs,
|
||||
R3_PATCH_COMPONENT_DEF_WTIH_SCOPE as ɵpatchComponentDefWithScope,
|
||||
R3_COMPILE_COMPONENT as ɵcompileComponent,
|
||||
R3_COMPILE_DIRECTIVE as ɵcompileDirective,
|
||||
R3_COMPILE_PIPE as ɵcompilePipe,
|
||||
} from './ivy_switch/compiler/ivy_switch_on';
|
||||
compileComponent as ɵcompileComponent,
|
||||
compileDirective as ɵcompileDirective,
|
||||
} from './render3/jit/directive';
|
||||
export {
|
||||
compileNgModule as ɵcompileNgModule,
|
||||
compileNgModuleDefs as ɵcompileNgModuleDefs,
|
||||
patchComponentDefWithScope as ɵpatchComponentDefWithScope,
|
||||
} from './render3/jit/module';
|
||||
export {
|
||||
compilePipe as ɵcompilePipe,
|
||||
} from './render3/jit/pipe';
|
||||
|
||||
export {
|
||||
NgModuleDef as ɵNgModuleDef,
|
||||
@ -186,24 +191,37 @@ export {
|
||||
//
|
||||
// no code actually imports these symbols from the @angular/core entry point
|
||||
export {
|
||||
compileNgModuleFactory__POST_NGCC__ as ɵcompileNgModuleFactory__POST_NGCC__
|
||||
compileNgModuleFactory__POST_R3__ as ɵcompileNgModuleFactory__POST_R3__
|
||||
} from './application_ref';
|
||||
export {
|
||||
R3_COMPILE_COMPONENT__POST_NGCC__ as ɵR3_COMPILE_COMPONENT__POST_NGCC__,
|
||||
R3_COMPILE_DIRECTIVE__POST_NGCC__ as ɵR3_COMPILE_DIRECTIVE__POST_NGCC__,
|
||||
R3_COMPILE_INJECTABLE__POST_NGCC__ as ɵR3_COMPILE_INJECTABLE__POST_NGCC__,
|
||||
R3_COMPILE_NGMODULE__POST_NGCC__ as ɵR3_COMPILE_NGMODULE__POST_NGCC__,
|
||||
R3_COMPILE_PIPE__POST_NGCC__ as ɵR3_COMPILE_PIPE__POST_NGCC__,
|
||||
ivyEnable__POST_NGCC__ as ɵivyEnable__POST_NGCC__,
|
||||
} from './ivy_switch/compiler/legacy';
|
||||
|
||||
SWITCH_COMPILE_COMPONENT__POST_R3__ as ɵSWITCH_COMPILE_COMPONENT__POST_R3__,
|
||||
SWITCH_COMPILE_DIRECTIVE__POST_R3__ as ɵSWITCH_COMPILE_DIRECTIVE__POST_R3__,
|
||||
SWITCH_COMPILE_PIPE__POST_R3__ as ɵSWITCH_COMPILE_PIPE__POST_R3__,
|
||||
} from './metadata/directives';
|
||||
export {
|
||||
R3_ELEMENT_REF_FACTORY__POST_NGCC__ as ɵR3_ELEMENT_REF_FACTORY__POST_NGCC__,
|
||||
R3_TEMPLATE_REF_FACTORY__POST_NGCC__ as ɵR3_TEMPLATE_REF_FACTORY__POST_NGCC__,
|
||||
R3_CHANGE_DETECTOR_REF_FACTORY__POST_NGCC__ as ɵR3_CHANGE_DETECTOR_REF_FACTORY__POST_NGCC__,
|
||||
R3_VIEW_CONTAINER_REF_FACTORY__POST_NGCC__ as ɵR3_VIEW_CONTAINER_REF_FACTORY__POST_NGCC__,
|
||||
R3_RENDERER2_FACTORY__POST_NGCC__ as ɵR3_RENDERER2_FACTORY__POST_NGCC__,
|
||||
} from './ivy_switch/runtime/legacy';
|
||||
SWITCH_COMPILE_NGMODULE__POST_R3__ as ɵSWITCH_COMPILE_NGMODULE__POST_R3__,
|
||||
} from './metadata/ng_module';
|
||||
export {
|
||||
SWITCH_COMPILE_INJECTABLE__POST_R3__ as ɵSWITCH_COMPILE_INJECTABLE__POST_R3__,
|
||||
} from './di/injectable';
|
||||
export {
|
||||
SWITCH_IVY_ENABLED__POST_R3__ as ɵSWITCH_IVY_ENABLED__POST_R3__,
|
||||
} from './ivy_switch';
|
||||
export {
|
||||
SWITCH_CHANGE_DETECTOR_REF_FACTORY__POST_R3__ as ɵSWITCH_CHANGE_DETECTOR_REF_FACTORY__POST_R3__,
|
||||
} from './change_detection/change_detector_ref';
|
||||
export {
|
||||
SWITCH_ELEMENT_REF_FACTORY__POST_R3__ as ɵSWITCH_ELEMENT_REF_FACTORY__POST_R3__,
|
||||
} from './linker/element_ref';
|
||||
export {
|
||||
SWITCH_TEMPLATE_REF_FACTORY__POST_R3__ as ɵSWITCH_TEMPLATE_REF_FACTORY__POST_R3__,
|
||||
} from './linker/template_ref';
|
||||
export {
|
||||
SWITCH_VIEW_CONTAINER_REF_FACTORY__POST_R3__ as ɵSWITCH_VIEW_CONTAINER_REF_FACTORY__POST_R3__,
|
||||
} from './linker/view_container_ref';
|
||||
export {
|
||||
SWITCH_RENDERER2_FACTORY__POST_R3__ as ɵSWITCH_RENDERER2_FACTORY__POST_R3__,
|
||||
} from './render/api';
|
||||
|
||||
|
||||
// clang-format on
|
||||
|
@ -6,12 +6,14 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {R3_COMPILE_INJECTABLE} from '../ivy_switch/compiler/index';
|
||||
import {compileInjectable as render3CompileInjectable} from '../render3/jit/injectable';
|
||||
import {Type} from '../type';
|
||||
import {makeDecorator} from '../util/decorators';
|
||||
|
||||
import {InjectableDef, InjectableType} from './defs';
|
||||
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './provider';
|
||||
import {InjectableDef, InjectableType, defineInjectable, getInjectableDef} from './defs';
|
||||
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueSansProvider} from './provider';
|
||||
import {convertInjectableProviderToFactory} from './util';
|
||||
|
||||
|
||||
/**
|
||||
* Injectable providers used in `@Injectable` decorator.
|
||||
@ -64,7 +66,7 @@ export interface Injectable { providedIn?: Type<any>|'root'|null; }
|
||||
*/
|
||||
export const Injectable: InjectableDecorator = makeDecorator(
|
||||
'Injectable', undefined, undefined, undefined,
|
||||
(type: Type<any>, meta: Injectable) => R3_COMPILE_INJECTABLE(type, meta));
|
||||
(type: Type<any>, meta: Injectable) => SWITCH_COMPILE_INJECTABLE(type as any, meta));
|
||||
|
||||
/**
|
||||
* Type representing injectable service.
|
||||
@ -72,3 +74,22 @@ export const Injectable: InjectableDecorator = makeDecorator(
|
||||
* @publicApi
|
||||
*/
|
||||
export interface InjectableType<T> extends Type<T> { ngInjectableDef: InjectableDef<T>; }
|
||||
|
||||
/**
|
||||
* Supports @Injectable() in JIT mode for Render2.
|
||||
*/
|
||||
function render2CompileInjectable(
|
||||
injectableType: InjectableType<any>,
|
||||
options: {providedIn?: Type<any>| 'root' | null} & InjectableProvider): void {
|
||||
if (options && options.providedIn !== undefined && !getInjectableDef(injectableType)) {
|
||||
injectableType.ngInjectableDef = defineInjectable({
|
||||
providedIn: options.providedIn,
|
||||
factory: convertInjectableProviderToFactory(injectableType, options),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const SWITCH_COMPILE_INJECTABLE__POST_R3__ = render3CompileInjectable;
|
||||
const SWITCH_COMPILE_INJECTABLE__PRE_R3__ = render2CompileInjectable;
|
||||
const SWITCH_COMPILE_INJECTABLE: typeof render3CompileInjectable =
|
||||
SWITCH_COMPILE_INJECTABLE__PRE_R3__;
|
||||
|
55
packages/core/src/di/util.ts
Normal file
55
packages/core/src/di/util.ts
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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 {ReflectionCapabilities} from '../reflection/reflection_capabilities';
|
||||
import {Type} from '../type';
|
||||
import {getClosureSafeProperty} from '../util/property';
|
||||
|
||||
import {inject, injectArgs} from './injector';
|
||||
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './provider';
|
||||
|
||||
const USE_VALUE =
|
||||
getClosureSafeProperty<ValueProvider>({provide: String, useValue: getClosureSafeProperty});
|
||||
const EMPTY_ARRAY: any[] = [];
|
||||
|
||||
export function convertInjectableProviderToFactory(
|
||||
type: Type<any>, provider?: ValueSansProvider | ExistingSansProvider | StaticClassSansProvider |
|
||||
ConstructorSansProvider | FactorySansProvider | ClassSansProvider): () => any {
|
||||
if (!provider) {
|
||||
const reflectionCapabilities = new ReflectionCapabilities();
|
||||
const deps = reflectionCapabilities.parameters(type);
|
||||
// TODO - convert to flags.
|
||||
return () => new type(...injectArgs(deps as any[]));
|
||||
}
|
||||
|
||||
if (USE_VALUE in provider) {
|
||||
const valueProvider = (provider as ValueSansProvider);
|
||||
return () => valueProvider.useValue;
|
||||
} else if ((provider as ExistingSansProvider).useExisting) {
|
||||
const existingProvider = (provider as ExistingSansProvider);
|
||||
return () => inject(existingProvider.useExisting);
|
||||
} else if ((provider as FactorySansProvider).useFactory) {
|
||||
const factoryProvider = (provider as FactorySansProvider);
|
||||
return () => factoryProvider.useFactory(...injectArgs(factoryProvider.deps || EMPTY_ARRAY));
|
||||
} else if ((provider as StaticClassSansProvider | ClassSansProvider).useClass) {
|
||||
const classProvider = (provider as StaticClassSansProvider | ClassSansProvider);
|
||||
let deps = (provider as StaticClassSansProvider).deps;
|
||||
if (!deps) {
|
||||
const reflectionCapabilities = new ReflectionCapabilities();
|
||||
deps = reflectionCapabilities.parameters(type);
|
||||
}
|
||||
return () => new classProvider.useClass(...injectArgs(deps));
|
||||
} else {
|
||||
let deps = (provider as ConstructorSansProvider).deps;
|
||||
if (!deps) {
|
||||
const reflectionCapabilities = new ReflectionCapabilities();
|
||||
deps = reflectionCapabilities.parameters(type);
|
||||
}
|
||||
return () => new type(...injectArgs(deps !));
|
||||
}
|
||||
}
|
@ -6,4 +6,6 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export * from './ivy_switch_on';
|
||||
export const SWITCH_IVY_ENABLED__POST_R3__ = true;
|
||||
const SWITCH_IVY_ENABLED__PRE_R3__ = false;
|
||||
export const ivyEnabled = SWITCH_IVY_ENABLED__PRE_R3__;
|
@ -1,17 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file is used to control if the default rendering pipeline should be `ViewEngine` or `Ivy`.
|
||||
*
|
||||
* For more information on how to run and debug tests with either Ivy or View Engine (legacy),
|
||||
* please see [BAZEL.md](./docs/BAZEL.md).
|
||||
*/
|
||||
export * from './legacy';
|
||||
|
||||
// TODO(alxhub): debug why metadata doesn't properly propagate through this file.
|
@ -1,21 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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 {compileComponent, compileDirective} from '../../render3/jit/directive';
|
||||
import {compileInjectable} from '../../render3/jit/injectable';
|
||||
import {compileNgModule, compileNgModuleDefs, patchComponentDefWithScope} from '../../render3/jit/module';
|
||||
import {compilePipe} from '../../render3/jit/pipe';
|
||||
|
||||
export const ivyEnabled = true;
|
||||
export const R3_COMPILE_COMPONENT = compileComponent;
|
||||
export const R3_COMPILE_DIRECTIVE = compileDirective;
|
||||
export const R3_COMPILE_INJECTABLE = compileInjectable;
|
||||
export const R3_COMPILE_NGMODULE = compileNgModule;
|
||||
export const R3_COMPILE_PIPE = compilePipe;
|
||||
export const R3_COMPILE_NGMODULE_DEFS = compileNgModuleDefs;
|
||||
export const R3_PATCH_COMPONENT_DEF_WTIH_SCOPE = patchComponentDefWithScope;
|
@ -1,126 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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 {InjectableType, InjectorType, defineInjectable, defineInjector, getInjectableDef} from '../../di/defs';
|
||||
import {InjectableProvider} from '../../di/injectable';
|
||||
import {inject, injectArgs} from '../../di/injector';
|
||||
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from '../../di/provider';
|
||||
import {NgModule} from '../../metadata';
|
||||
import {ReflectionCapabilities} from '../../reflection/reflection_capabilities';
|
||||
import {Type} from '../../type';
|
||||
import {getClosureSafeProperty} from '../../util/property';
|
||||
|
||||
import * as ivyOn from './ivy_switch_on';
|
||||
|
||||
function noop() {}
|
||||
|
||||
export interface DirectiveCompiler { (type: any, meta: any): void; }
|
||||
|
||||
export const R3_COMPILE_COMPONENT__POST_NGCC__: DirectiveCompiler = ivyOn.R3_COMPILE_COMPONENT;
|
||||
export const R3_COMPILE_DIRECTIVE__POST_NGCC__: DirectiveCompiler = ivyOn.R3_COMPILE_DIRECTIVE;
|
||||
export const R3_COMPILE_INJECTABLE__POST_NGCC__: DirectiveCompiler = ivyOn.R3_COMPILE_INJECTABLE;
|
||||
export const R3_COMPILE_NGMODULE__POST_NGCC__: DirectiveCompiler = ivyOn.R3_COMPILE_NGMODULE;
|
||||
export const R3_COMPILE_PIPE__POST_NGCC__: DirectiveCompiler = ivyOn.R3_COMPILE_PIPE;
|
||||
export const R3_COMPILE_NGMODULE_DEFS__POST_NGCC__: DirectiveCompiler =
|
||||
ivyOn.R3_COMPILE_NGMODULE_DEFS;
|
||||
export const R3_PATCH_COMPONENT_DEF_WTIH_SCOPE__POST_NGCC__: DirectiveCompiler =
|
||||
ivyOn.R3_PATCH_COMPONENT_DEF_WTIH_SCOPE;
|
||||
|
||||
export const ivyEnable__POST_NGCC__: boolean = ivyOn.ivyEnabled;
|
||||
|
||||
const R3_COMPILE_COMPONENT__PRE_NGCC__: DirectiveCompiler = noop;
|
||||
const R3_COMPILE_DIRECTIVE__PRE_NGCC__: DirectiveCompiler = noop;
|
||||
const R3_COMPILE_INJECTABLE__PRE_NGCC__: DirectiveCompiler = preR3InjectableCompile;
|
||||
const R3_COMPILE_NGMODULE__PRE_NGCC__: DirectiveCompiler = preR3NgModuleCompile;
|
||||
const R3_COMPILE_PIPE__PRE_NGCC__: DirectiveCompiler = noop;
|
||||
const R3_COMPILE_NGMODULE_DEFS__PRE_NGCC__: DirectiveCompiler = noop;
|
||||
const R3_PATCH_COMPONENT_DEF_WTIH_SCOPE__PRE_NGCC__: DirectiveCompiler = noop;
|
||||
|
||||
const ivyEnable__PRE_NGCC__ = false;
|
||||
|
||||
export const ivyEnabled = ivyEnable__PRE_NGCC__;
|
||||
export let R3_COMPILE_COMPONENT: DirectiveCompiler = R3_COMPILE_COMPONENT__PRE_NGCC__;
|
||||
export let R3_COMPILE_DIRECTIVE: DirectiveCompiler = R3_COMPILE_DIRECTIVE__PRE_NGCC__;
|
||||
export let R3_COMPILE_INJECTABLE: DirectiveCompiler = R3_COMPILE_INJECTABLE__PRE_NGCC__;
|
||||
export let R3_COMPILE_NGMODULE: DirectiveCompiler = R3_COMPILE_NGMODULE__PRE_NGCC__;
|
||||
export let R3_COMPILE_PIPE: DirectiveCompiler = R3_COMPILE_PIPE__PRE_NGCC__;
|
||||
export let R3_COMPILE_NGMODULE_DEFS: DirectiveCompiler = R3_COMPILE_NGMODULE_DEFS__PRE_NGCC__;
|
||||
export let R3_PATCH_COMPONENT_DEF_WTIH_SCOPE: DirectiveCompiler =
|
||||
R3_PATCH_COMPONENT_DEF_WTIH_SCOPE__PRE_NGCC__;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Glue code which should be removed after Ivy is default //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
function preR3NgModuleCompile(moduleType: InjectorType<any>, metadata: NgModule): void {
|
||||
let imports = (metadata && metadata.imports) || [];
|
||||
if (metadata && metadata.exports) {
|
||||
imports = [...imports, metadata.exports];
|
||||
}
|
||||
|
||||
moduleType.ngInjectorDef = defineInjector({
|
||||
factory: convertInjectableProviderToFactory(moduleType, {useClass: moduleType}),
|
||||
providers: metadata && metadata.providers,
|
||||
imports: imports,
|
||||
});
|
||||
}
|
||||
|
||||
const USE_VALUE =
|
||||
getClosureSafeProperty<ValueProvider>({provide: String, useValue: getClosureSafeProperty});
|
||||
const EMPTY_ARRAY: any[] = [];
|
||||
|
||||
function convertInjectableProviderToFactory(type: Type<any>, provider?: InjectableProvider): () =>
|
||||
any {
|
||||
if (!provider) {
|
||||
const reflectionCapabilities = new ReflectionCapabilities();
|
||||
const deps = reflectionCapabilities.parameters(type);
|
||||
// TODO - convert to flags.
|
||||
return () => new type(...injectArgs(deps as any[]));
|
||||
}
|
||||
|
||||
if (USE_VALUE in provider) {
|
||||
const valueProvider = (provider as ValueSansProvider);
|
||||
return () => valueProvider.useValue;
|
||||
} else if ((provider as ExistingSansProvider).useExisting) {
|
||||
const existingProvider = (provider as ExistingSansProvider);
|
||||
return () => inject(existingProvider.useExisting);
|
||||
} else if ((provider as FactorySansProvider).useFactory) {
|
||||
const factoryProvider = (provider as FactorySansProvider);
|
||||
return () => factoryProvider.useFactory(...injectArgs(factoryProvider.deps || EMPTY_ARRAY));
|
||||
} else if ((provider as StaticClassSansProvider | ClassSansProvider).useClass) {
|
||||
const classProvider = (provider as StaticClassSansProvider | ClassSansProvider);
|
||||
let deps = (provider as StaticClassSansProvider).deps;
|
||||
if (!deps) {
|
||||
const reflectionCapabilities = new ReflectionCapabilities();
|
||||
deps = reflectionCapabilities.parameters(type);
|
||||
}
|
||||
return () => new classProvider.useClass(...injectArgs(deps));
|
||||
} else {
|
||||
let deps = (provider as ConstructorSansProvider).deps;
|
||||
if (!deps) {
|
||||
const reflectionCapabilities = new ReflectionCapabilities();
|
||||
deps = reflectionCapabilities.parameters(type);
|
||||
}
|
||||
return () => new type(...injectArgs(deps !));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports @Injectable() in JIT mode for Render2.
|
||||
*/
|
||||
function preR3InjectableCompile(
|
||||
injectableType: InjectableType<any>,
|
||||
options: {providedIn?: Type<any>| 'root' | null} & InjectableProvider): void {
|
||||
if (options && options.providedIn !== undefined && !getInjectableDef(injectableType)) {
|
||||
injectableType.ngInjectableDef = defineInjectable({
|
||||
providedIn: options.providedIn,
|
||||
factory: convertInjectableProviderToFactory(injectableType, options),
|
||||
});
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file is used to control if the default rendering pipeline should be `ViewEngine` or `Ivy`.
|
||||
*
|
||||
* For more information on how to run and debug tests with either Ivy or View Engine (legacy),
|
||||
* please see [BAZEL.md](./docs/BAZEL.md).
|
||||
*/
|
||||
export * from './legacy';
|
@ -1,15 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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 {injectChangeDetectorRef, injectElementRef, injectRenderer2, injectTemplateRef, injectViewContainerRef} from '../../render3/view_engine_compatibility';
|
||||
|
||||
export const R3_ELEMENT_REF_FACTORY = injectElementRef;
|
||||
export const R3_TEMPLATE_REF_FACTORY = injectTemplateRef;
|
||||
export const R3_CHANGE_DETECTOR_REF_FACTORY = injectChangeDetectorRef;
|
||||
export const R3_VIEW_CONTAINER_REF_FACTORY = injectViewContainerRef;
|
||||
export const R3_RENDERER2_FACTORY = injectRenderer2;
|
@ -1,34 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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 ivyOn from './ivy_switch_on';
|
||||
|
||||
function noopFactory(...tokens: any[]): any {}
|
||||
|
||||
type FactoryFunction<T = any> = (...tokens: any[]) => T;
|
||||
|
||||
export const R3_ELEMENT_REF_FACTORY__POST_NGCC__: FactoryFunction = ivyOn.R3_ELEMENT_REF_FACTORY;
|
||||
export const R3_TEMPLATE_REF_FACTORY__POST_NGCC__: FactoryFunction = ivyOn.R3_TEMPLATE_REF_FACTORY;
|
||||
export const R3_CHANGE_DETECTOR_REF_FACTORY__POST_NGCC__: FactoryFunction =
|
||||
ivyOn.R3_CHANGE_DETECTOR_REF_FACTORY;
|
||||
export const R3_VIEW_CONTAINER_REF_FACTORY__POST_NGCC__: FactoryFunction =
|
||||
ivyOn.R3_VIEW_CONTAINER_REF_FACTORY;
|
||||
export const R3_RENDERER2_FACTORY__POST_NGCC__: FactoryFunction = ivyOn.R3_RENDERER2_FACTORY;
|
||||
|
||||
|
||||
export const R3_ELEMENT_REF_FACTORY__PRE_NGCC__ = noopFactory;
|
||||
export const R3_TEMPLATE_REF_FACTORY__PRE_NGCC__ = noopFactory;
|
||||
export const R3_CHANGE_DETECTOR_REF_FACTORY__PRE_NGCC__ = noopFactory;
|
||||
export const R3_VIEW_CONTAINER_REF_FACTORY__PRE_NGCC__ = noopFactory;
|
||||
export const R3_RENDERER2_FACTORY__PRE_NGCC__ = noopFactory;
|
||||
|
||||
export let R3_ELEMENT_REF_FACTORY = R3_ELEMENT_REF_FACTORY__PRE_NGCC__;
|
||||
export let R3_TEMPLATE_REF_FACTORY = R3_TEMPLATE_REF_FACTORY__PRE_NGCC__;
|
||||
export let R3_CHANGE_DETECTOR_REF_FACTORY = R3_CHANGE_DETECTOR_REF_FACTORY__PRE_NGCC__;
|
||||
export let R3_VIEW_CONTAINER_REF_FACTORY = R3_VIEW_CONTAINER_REF_FACTORY__PRE_NGCC__;
|
||||
export let R3_RENDERER2_FACTORY = R3_RENDERER2_FACTORY__PRE_NGCC__;
|
@ -6,7 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {R3_ELEMENT_REF_FACTORY} from '../ivy_switch/runtime/index';
|
||||
import {injectElementRef as render3InjectElementRef} from '../render3/view_engine_compatibility';
|
||||
import {noop} from '../util/noop';
|
||||
|
||||
/**
|
||||
* A wrapper around a native element inside of a View.
|
||||
@ -50,5 +51,10 @@ export class ElementRef<T = any> {
|
||||
constructor(nativeElement: T) { this.nativeElement = nativeElement; }
|
||||
|
||||
/** @internal */
|
||||
static __NG_ELEMENT_ID__: () => ElementRef = () => R3_ELEMENT_REF_FACTORY(ElementRef);
|
||||
static __NG_ELEMENT_ID__: () => ElementRef = () => SWITCH_ELEMENT_REF_FACTORY(ElementRef);
|
||||
}
|
||||
|
||||
export const SWITCH_ELEMENT_REF_FACTORY__POST_R3__ = render3InjectElementRef;
|
||||
const SWITCH_ELEMENT_REF_FACTORY__PRE_R3__ = noop;
|
||||
const SWITCH_ELEMENT_REF_FACTORY: typeof render3InjectElementRef =
|
||||
SWITCH_ELEMENT_REF_FACTORY__PRE_R3__;
|
||||
|
@ -6,7 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {R3_TEMPLATE_REF_FACTORY} from '../ivy_switch/runtime/index';
|
||||
import {injectTemplateRef as render3InjectTemplateRef} from '../render3/view_engine_compatibility';
|
||||
import {noop} from '../util/noop';
|
||||
|
||||
import {ElementRef} from './element_ref';
|
||||
import {EmbeddedViewRef} from './view_ref';
|
||||
@ -54,5 +55,10 @@ export abstract class TemplateRef<C> {
|
||||
|
||||
/** @internal */
|
||||
static __NG_ELEMENT_ID__:
|
||||
() => TemplateRef<any> = () => R3_TEMPLATE_REF_FACTORY(TemplateRef, ElementRef)
|
||||
() => TemplateRef<any> = () => SWITCH_TEMPLATE_REF_FACTORY(TemplateRef, ElementRef)
|
||||
}
|
||||
|
||||
export const SWITCH_TEMPLATE_REF_FACTORY__POST_R3__ = render3InjectTemplateRef;
|
||||
const SWITCH_TEMPLATE_REF_FACTORY__PRE_R3__ = noop;
|
||||
const SWITCH_TEMPLATE_REF_FACTORY: typeof render3InjectTemplateRef =
|
||||
SWITCH_TEMPLATE_REF_FACTORY__PRE_R3__;
|
||||
|
@ -7,7 +7,8 @@
|
||||
*/
|
||||
|
||||
import {Injector} from '../di/injector';
|
||||
import {R3_VIEW_CONTAINER_REF_FACTORY} from '../ivy_switch/runtime/index';
|
||||
import {injectViewContainerRef as render3InjectViewContainerRef} from '../render3/view_engine_compatibility';
|
||||
import {noop} from '../util/noop';
|
||||
|
||||
import {ComponentFactory, ComponentRef} from './component_factory';
|
||||
import {ElementRef} from './element_ref';
|
||||
@ -145,5 +146,10 @@ export abstract class ViewContainerRef {
|
||||
|
||||
/** @internal */
|
||||
static __NG_ELEMENT_ID__:
|
||||
() => ViewContainerRef = () => R3_VIEW_CONTAINER_REF_FACTORY(ViewContainerRef, ElementRef)
|
||||
() => ViewContainerRef = () => SWITCH_VIEW_CONTAINER_REF_FACTORY(ViewContainerRef, ElementRef)
|
||||
}
|
||||
|
||||
export const SWITCH_VIEW_CONTAINER_REF_FACTORY__POST_R3__ = render3InjectViewContainerRef;
|
||||
const SWITCH_VIEW_CONTAINER_REF_FACTORY__PRE_R3__ = noop;
|
||||
const SWITCH_VIEW_CONTAINER_REF_FACTORY: typeof render3InjectViewContainerRef =
|
||||
SWITCH_VIEW_CONTAINER_REF_FACTORY__PRE_R3__;
|
||||
|
@ -8,10 +8,12 @@
|
||||
|
||||
import {ChangeDetectionStrategy} from '../change_detection/constants';
|
||||
import {Provider} from '../di';
|
||||
import {R3_COMPILE_COMPONENT, R3_COMPILE_DIRECTIVE, R3_COMPILE_PIPE} from '../ivy_switch/compiler/index';
|
||||
import {NG_BASE_DEF} from '../render3/fields';
|
||||
import {compileComponent as render3CompileComponent, compileDirective as render3CompileDirective} from '../render3/jit/directive';
|
||||
import {compilePipe as render3CompilePipe} from '../render3/jit/pipe';
|
||||
import {Type} from '../type';
|
||||
import {TypeDecorator, makeDecorator, makePropDecorator} from '../util/decorators';
|
||||
import {noop} from '../util/noop';
|
||||
import {fillProperties} from '../util/property';
|
||||
|
||||
import {ViewEncapsulation} from './view';
|
||||
@ -353,7 +355,7 @@ export interface Directive {
|
||||
*/
|
||||
export const Directive: DirectiveDecorator = makeDecorator(
|
||||
'Directive', (dir: Directive = {}) => dir, undefined, undefined,
|
||||
(type: Type<any>, meta: Directive) => R3_COMPILE_DIRECTIVE(type, meta));
|
||||
(type: Type<any>, meta: Directive) => SWITCH_COMPILE_DIRECTIVE(type, meta));
|
||||
|
||||
/**
|
||||
* Component decorator interface
|
||||
@ -639,7 +641,8 @@ export interface Component extends Directive {
|
||||
*/
|
||||
export const Component: ComponentDecorator = makeDecorator(
|
||||
'Component', (c: Component = {}) => ({changeDetection: ChangeDetectionStrategy.Default, ...c}),
|
||||
Directive, undefined, (type: Type<any>, meta: Component) => R3_COMPILE_COMPONENT(type, meta));
|
||||
Directive, undefined,
|
||||
(type: Type<any>, meta: Component) => SWITCH_COMPILE_COMPONENT(type, meta));
|
||||
|
||||
/**
|
||||
* Type of the Pipe decorator / constructor function.
|
||||
@ -688,7 +691,7 @@ export interface Pipe {
|
||||
*/
|
||||
export const Pipe: PipeDecorator = makeDecorator(
|
||||
'Pipe', (p: Pipe) => ({pure: true, ...p}), undefined, undefined,
|
||||
(type: Type<any>, meta: Pipe) => R3_COMPILE_PIPE(type, meta));
|
||||
(type: Type<any>, meta: Pipe) => SWITCH_COMPILE_PIPE(type, meta));
|
||||
|
||||
|
||||
/**
|
||||
@ -962,3 +965,17 @@ export interface HostListener {
|
||||
*/
|
||||
export const HostListener: HostListenerDecorator =
|
||||
makePropDecorator('HostListener', (eventName?: string, args?: string[]) => ({eventName, args}));
|
||||
|
||||
|
||||
|
||||
export const SWITCH_COMPILE_COMPONENT__POST_R3__ = render3CompileComponent;
|
||||
export const SWITCH_COMPILE_DIRECTIVE__POST_R3__ = render3CompileDirective;
|
||||
export const SWITCH_COMPILE_PIPE__POST_R3__ = render3CompilePipe;
|
||||
|
||||
const SWITCH_COMPILE_COMPONENT__PRE_R3__ = noop;
|
||||
const SWITCH_COMPILE_DIRECTIVE__PRE_R3__ = noop;
|
||||
const SWITCH_COMPILE_PIPE__PRE_R3__ = noop;
|
||||
|
||||
const SWITCH_COMPILE_COMPONENT: typeof render3CompileComponent = SWITCH_COMPILE_COMPONENT__PRE_R3__;
|
||||
const SWITCH_COMPILE_DIRECTIVE: typeof render3CompileDirective = SWITCH_COMPILE_DIRECTIVE__PRE_R3__;
|
||||
const SWITCH_COMPILE_PIPE: typeof render3CompilePipe = SWITCH_COMPILE_PIPE__PRE_R3__;
|
||||
|
@ -7,8 +7,10 @@
|
||||
*/
|
||||
|
||||
import {ApplicationRef} from '../application_ref';
|
||||
import {InjectorType, defineInjector} from '../di/defs';
|
||||
import {Provider} from '../di/provider';
|
||||
import {R3_COMPILE_NGMODULE} from '../ivy_switch/compiler/index';
|
||||
import {convertInjectableProviderToFactory} from '../di/util';
|
||||
import {compileNgModule as render3CompileNgModule} from '../render3/jit/module';
|
||||
import {Type} from '../type';
|
||||
import {TypeDecorator, makeDecorator} from '../util/decorators';
|
||||
|
||||
@ -333,7 +335,7 @@ export const NgModule: NgModuleDecorator = makeDecorator(
|
||||
* * The `imports` and `exports` options bring in members from other modules, and make
|
||||
* this module's members available to others.
|
||||
*/
|
||||
(type: Type<any>, meta: NgModule) => R3_COMPILE_NGMODULE(type, meta));
|
||||
(type: Type<any>, meta: NgModule) => SWITCH_COMPILE_NGMODULE(type, meta));
|
||||
|
||||
/**
|
||||
* @description
|
||||
@ -356,3 +358,21 @@ export const NgModule: NgModuleDecorator = makeDecorator(
|
||||
* @publicApi
|
||||
*/
|
||||
export interface DoBootstrap { ngDoBootstrap(appRef: ApplicationRef): void; }
|
||||
|
||||
function preR3NgModuleCompile(moduleType: InjectorType<any>, metadata: NgModule): void {
|
||||
let imports = (metadata && metadata.imports) || [];
|
||||
if (metadata && metadata.exports) {
|
||||
imports = [...imports, metadata.exports];
|
||||
}
|
||||
|
||||
moduleType.ngInjectorDef = defineInjector({
|
||||
factory: convertInjectableProviderToFactory(moduleType, {useClass: moduleType}),
|
||||
providers: metadata && metadata.providers,
|
||||
imports: imports,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export const SWITCH_COMPILE_NGMODULE__POST_R3__ = render3CompileNgModule;
|
||||
const SWITCH_COMPILE_NGMODULE__PRE_R3__ = preR3NgModuleCompile;
|
||||
const SWITCH_COMPILE_NGMODULE: typeof render3CompileNgModule = SWITCH_COMPILE_NGMODULE__PRE_R3__;
|
||||
|
@ -8,8 +8,9 @@
|
||||
|
||||
import {InjectionToken} from '../di/injection_token';
|
||||
import {Injector} from '../di/injector';
|
||||
import {R3_RENDERER2_FACTORY} from '../ivy_switch/runtime/index';
|
||||
import {ViewEncapsulation} from '../metadata/view';
|
||||
import {injectRenderer2 as render3InjectRenderer2} from '../render3/view_engine_compatibility';
|
||||
import {noop} from '../util/noop';
|
||||
|
||||
|
||||
/**
|
||||
@ -373,5 +374,10 @@ export abstract class Renderer2 {
|
||||
callback: (event: any) => boolean | void): () => void;
|
||||
|
||||
/** @internal */
|
||||
static __NG_ELEMENT_ID__: () => Renderer2 = () => R3_RENDERER2_FACTORY();
|
||||
static __NG_ELEMENT_ID__: () => Renderer2 = () => SWITCH_RENDERER2_FACTORY();
|
||||
}
|
||||
|
||||
|
||||
export const SWITCH_RENDERER2_FACTORY__POST_R3__ = render3InjectRenderer2;
|
||||
const SWITCH_RENDERER2_FACTORY__PRE_R3__ = noop;
|
||||
const SWITCH_RENDERER2_FACTORY: typeof render3InjectRenderer2 = SWITCH_RENDERER2_FACTORY__PRE_R3__;
|
||||
|
@ -205,9 +205,15 @@ The goal is for the `@Component` (and friends) to be the compiler of template. S
|
||||
### I18N
|
||||
| Feature | Runtime | Spec | Compiler |
|
||||
| ----------------------------------- | ------- | -------- | -------- |
|
||||
| translate text literals | ❌ | ❌ | ✅ |
|
||||
| rearrange text nodes | ❌ | ❌ | ❌ |
|
||||
| ICU | ❌ | ❌ | ❌ |
|
||||
| i18nStart | ❌ | ✅ | ✅ |
|
||||
| i18nEnd | ❌ | ✅ | ✅ |
|
||||
| i18nAttributes | ❌ | ✅ | ✅ |
|
||||
| i18nExp | ❌ | ✅ | ✅ |
|
||||
| i18nApply | ❌ | ✅ | ✅ |
|
||||
| ICU expressions | ❌ | ✅ | ❌ |
|
||||
| closure support for g3 | ✅ | ✅ | ❌ |
|
||||
| runtime service for external world | ❌ | ❌ | ❌ |
|
||||
| migration tool | ❌ | ❌ | ❌ |
|
||||
|
||||
|
||||
### View Encapsulation
|
||||
|
@ -502,6 +502,12 @@ function executePipeOnDestroys(viewData: LViewData): void {
|
||||
export function getRenderParent(tNode: TNode, currentView: LViewData): RElement|null {
|
||||
if (canInsertNativeNode(tNode, currentView)) {
|
||||
const hostTNode = currentView[HOST_NODE];
|
||||
|
||||
const tNodeParent = tNode.parent;
|
||||
if (tNodeParent != null && tNodeParent.type === TNodeType.ElementContainer) {
|
||||
tNode = getHighestElementContainer(tNodeParent);
|
||||
}
|
||||
|
||||
return tNode.parent == null && hostTNode !.type === TNodeType.View ?
|
||||
getContainerRenderParent(hostTNode as TViewNode, currentView) :
|
||||
getParentNative(tNode, currentView) as RElement;
|
||||
@ -626,8 +632,7 @@ export function appendChild(
|
||||
renderer, lContainer[RENDER_PARENT] !, childEl,
|
||||
getBeforeNodeForView(index, views, lContainer[NATIVE]));
|
||||
} else if (parentTNode.type === TNodeType.ElementContainer) {
|
||||
let elementContainer = getHighestElementContainer(childTNode);
|
||||
let renderParent: RElement = getRenderParent(elementContainer, currentView) !;
|
||||
const renderParent: RElement = getRenderParent(childTNode, currentView) !;
|
||||
nativeInsertBefore(renderer, renderParent, childEl, parentEl);
|
||||
} else {
|
||||
isProceduralRenderer(renderer) ? renderer.appendChild(parentEl !as RElement, childEl) :
|
||||
|
@ -6,4 +6,6 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export * from './ivy_switch_on';
|
||||
export function noop(...args: any[]): any {
|
||||
// Do nothing.
|
||||
}
|
@ -12,7 +12,7 @@ import {InjectableDef, getInjectableDef} from '../di/defs';
|
||||
import {InjectableType} from '../di/injectable';
|
||||
import {ErrorHandler} from '../error_handler';
|
||||
import {isDevMode} from '../is_dev_mode';
|
||||
import {ivyEnabled} from '../ivy_switch/compiler/index';
|
||||
import {ivyEnabled} from '../ivy_switch';
|
||||
import {ComponentFactory} from '../linker/component_factory';
|
||||
import {NgModuleRef} from '../linker/ng_module_factory';
|
||||
import {Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '../render/api';
|
||||
|
@ -37,6 +37,7 @@ js_expected_symbol_test(
|
||||
src = ":bundle.min_debug.js",
|
||||
golden = ":bundle.golden_symbols.json",
|
||||
tags = [
|
||||
"ivy-aot",
|
||||
"ivy-only",
|
||||
],
|
||||
)
|
||||
|
@ -158,15 +158,6 @@
|
||||
{
|
||||
"name": "QUERIES"
|
||||
},
|
||||
{
|
||||
"name": "R3_ELEMENT_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "R3_TEMPLATE_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "R3_VIEW_CONTAINER_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "RENDERER"
|
||||
},
|
||||
@ -185,6 +176,15 @@
|
||||
{
|
||||
"name": "SANITIZER"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_ELEMENT_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_TEMPLATE_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_VIEW_CONTAINER_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SimpleKeyframePlayer"
|
||||
},
|
||||
|
@ -59,6 +59,7 @@ js_expected_symbol_test(
|
||||
src = ":bundle.min_debug.js",
|
||||
golden = ":bundle.golden_symbols.json",
|
||||
tags = [
|
||||
"ivy-aot",
|
||||
"ivy-only",
|
||||
],
|
||||
)
|
||||
|
@ -1250,24 +1250,6 @@
|
||||
{
|
||||
"name": "Quote"
|
||||
},
|
||||
{
|
||||
"name": "R3_CHANGE_DETECTOR_REF_FACTORY$1"
|
||||
},
|
||||
{
|
||||
"name": "R3_COMPILE_INJECTABLE$1"
|
||||
},
|
||||
{
|
||||
"name": "R3_ELEMENT_REF_FACTORY$1"
|
||||
},
|
||||
{
|
||||
"name": "R3_RENDERER2_FACTORY$1"
|
||||
},
|
||||
{
|
||||
"name": "R3_TEMPLATE_REF_FACTORY$1"
|
||||
},
|
||||
{
|
||||
"name": "R3_VIEW_CONTAINER_REF_FACTORY$1"
|
||||
},
|
||||
{
|
||||
"name": "ReadKeyExpr"
|
||||
},
|
||||
@ -1352,6 +1334,24 @@
|
||||
{
|
||||
"name": "STYLE_PREFIX"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_CHANGE_DETECTOR_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_COMPILE_INJECTABLE"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_ELEMENT_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_RENDERER2_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_TEMPLATE_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_VIEW_CONTAINER_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SafeMethodCall"
|
||||
},
|
||||
@ -1599,10 +1599,10 @@
|
||||
"name": "URL_WITH_SCHEMA_REGEXP"
|
||||
},
|
||||
{
|
||||
"name": "USE_VALUE"
|
||||
"name": "USE_VALUE$1"
|
||||
},
|
||||
{
|
||||
"name": "USE_VALUE$3"
|
||||
"name": "USE_VALUE$2"
|
||||
},
|
||||
{
|
||||
"name": "UnsubscriptionError"
|
||||
@ -2460,7 +2460,7 @@
|
||||
"name": "compileNgModuleFactory"
|
||||
},
|
||||
{
|
||||
"name": "compileNgModuleFactory__PRE_NGCC__"
|
||||
"name": "compileNgModuleFactory__PRE_R3__"
|
||||
},
|
||||
{
|
||||
"name": "componentFactoryName"
|
||||
@ -3411,10 +3411,10 @@
|
||||
"name": "noop$1"
|
||||
},
|
||||
{
|
||||
"name": "noop$3"
|
||||
"name": "noop$2"
|
||||
},
|
||||
{
|
||||
"name": "noopFactory"
|
||||
"name": "noop$3"
|
||||
},
|
||||
{
|
||||
"name": "noopScope"
|
||||
@ -3476,9 +3476,6 @@
|
||||
{
|
||||
"name": "platformCoreDynamic"
|
||||
},
|
||||
{
|
||||
"name": "preR3InjectableCompile"
|
||||
},
|
||||
{
|
||||
"name": "preparseElement"
|
||||
},
|
||||
@ -3554,6 +3551,9 @@
|
||||
{
|
||||
"name": "removeWhitespaces"
|
||||
},
|
||||
{
|
||||
"name": "render2CompileInjectable"
|
||||
},
|
||||
{
|
||||
"name": "renderAttachEmbeddedView"
|
||||
},
|
||||
|
@ -60,7 +60,7 @@
|
||||
"name": "THROW_IF_NOT_FOUND"
|
||||
},
|
||||
{
|
||||
"name": "USE_VALUE"
|
||||
"name": "USE_VALUE$1"
|
||||
},
|
||||
{
|
||||
"name": "UnsubscriptionErrorImpl"
|
||||
@ -147,12 +147,12 @@
|
||||
"name": "providerToRecord"
|
||||
},
|
||||
{
|
||||
"name": "resolveForwardRef"
|
||||
"name": "resolveForwardRef$1"
|
||||
},
|
||||
{
|
||||
"name": "setCurrentInjector"
|
||||
},
|
||||
{
|
||||
"name": "stringify"
|
||||
"name": "stringify$1"
|
||||
}
|
||||
]
|
@ -66,6 +66,7 @@ js_expected_symbol_test(
|
||||
src = ":bundle.min_debug.js",
|
||||
golden = ":bundle.golden_symbols.json",
|
||||
tags = [
|
||||
"ivy-aot",
|
||||
"ivy-only",
|
||||
],
|
||||
)
|
||||
|
@ -152,15 +152,6 @@
|
||||
{
|
||||
"name": "QUERIES"
|
||||
},
|
||||
{
|
||||
"name": "R3_ELEMENT_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "R3_TEMPLATE_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "R3_VIEW_CONTAINER_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "RENDERER"
|
||||
},
|
||||
@ -179,6 +170,15 @@
|
||||
{
|
||||
"name": "SANITIZER"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_ELEMENT_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_TEMPLATE_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_VIEW_CONTAINER_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SkipSelf"
|
||||
},
|
||||
|
@ -72,6 +72,7 @@ js_expected_symbol_test(
|
||||
src = ":bundle.min_debug.js",
|
||||
golden = ":bundle.golden_symbols.json",
|
||||
tags = [
|
||||
"ivy-aot",
|
||||
"ivy-only",
|
||||
],
|
||||
)
|
||||
|
@ -140,9 +140,6 @@
|
||||
{
|
||||
"name": "Compiler"
|
||||
},
|
||||
{
|
||||
"name": "CompilerFactory"
|
||||
},
|
||||
{
|
||||
"name": "ComponentFactory"
|
||||
},
|
||||
@ -680,21 +677,6 @@
|
||||
{
|
||||
"name": "R3Injector"
|
||||
},
|
||||
{
|
||||
"name": "R3_CHANGE_DETECTOR_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "R3_ELEMENT_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "R3_RENDERER2_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "R3_TEMPLATE_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "R3_VIEW_CONTAINER_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_EVENT_LISTENER"
|
||||
},
|
||||
@ -758,6 +740,21 @@
|
||||
{
|
||||
"name": "SURROGATE_PAIR_REGEXP"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_CHANGE_DETECTOR_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_ELEMENT_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_RENDERER2_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_TEMPLATE_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SWITCH_VIEW_CONTAINER_REF_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "SafeHtmlImpl"
|
||||
},
|
||||
@ -1281,7 +1278,7 @@
|
||||
"name": "compileNgModuleFactory"
|
||||
},
|
||||
{
|
||||
"name": "compileNgModuleFactory__PRE_NGCC__"
|
||||
"name": "compileNgModuleFactory__POST_R3__"
|
||||
},
|
||||
{
|
||||
"name": "componentRefresh"
|
||||
@ -2157,10 +2154,10 @@
|
||||
"name": "noSideEffects"
|
||||
},
|
||||
{
|
||||
"name": "noop$1"
|
||||
"name": "noop$2"
|
||||
},
|
||||
{
|
||||
"name": "noop$2"
|
||||
"name": "noop$3"
|
||||
},
|
||||
{
|
||||
"name": "noopScope"
|
||||
|
@ -6,9 +6,9 @@ load("//tools:defaults.bzl", "ts_library")
|
||||
ts_library(
|
||||
name = "reflect_metadata",
|
||||
srcs = [
|
||||
"src/reflect_metadata_aot.ts",
|
||||
"src/reflect_metadata_jit.ts",
|
||||
"src/reflect_metadata_legacy.ts",
|
||||
"src/reflect_metadata_local.ts",
|
||||
":metadata_switch",
|
||||
],
|
||||
module_name = "@angular/core/test/bundling/util/src/reflect_metadata",
|
||||
|
@ -943,6 +943,116 @@ describe('render3 integration test', () => {
|
||||
expect(directive !.elRef.nativeElement.nodeType).toBe(Node.COMMENT_NODE);
|
||||
});
|
||||
|
||||
it('should support ViewContainerRef when ng-container is at the root of a view', () => {
|
||||
|
||||
function ContentTemplate(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
text(0, 'Content');
|
||||
}
|
||||
}
|
||||
|
||||
class Directive {
|
||||
contentTpl: TemplateRef<{}>|null = null;
|
||||
|
||||
constructor(private _vcRef: ViewContainerRef) {}
|
||||
|
||||
insertView() { this._vcRef.createEmbeddedView(this.contentTpl as TemplateRef<{}>); }
|
||||
|
||||
clear() { this._vcRef.clear(); }
|
||||
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: Directive,
|
||||
selectors: [['', 'dir', '']],
|
||||
factory: () => directive = new Directive(directiveInject(ViewContainerRef as any)),
|
||||
inputs: {contentTpl: 'contentTpl'},
|
||||
});
|
||||
}
|
||||
|
||||
let directive: Directive;
|
||||
|
||||
/**
|
||||
* <ng-container dir [contentTpl]="content">
|
||||
* <ng-template #content>Content</ng-template>
|
||||
* </ng-container>
|
||||
*/
|
||||
const App = createComponent('app', function(rf: RenderFlags) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementContainerStart(0, [AttributeMarker.SelectOnly, 'dir']);
|
||||
template(1, ContentTemplate, 1, 0, '', null, ['content', ''], templateRefExtractor);
|
||||
elementContainerEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
const content = reference(2) as any;
|
||||
elementProperty(0, 'contentTpl', bind(content));
|
||||
}
|
||||
}, 3, 1, [Directive]);
|
||||
|
||||
|
||||
const fixture = new ComponentFixture(App);
|
||||
expect(fixture.html).toEqual('');
|
||||
|
||||
directive !.insertView();
|
||||
fixture.update();
|
||||
expect(fixture.html).toEqual('Content');
|
||||
|
||||
directive !.clear();
|
||||
fixture.update();
|
||||
expect(fixture.html).toEqual('');
|
||||
});
|
||||
|
||||
it('should support ViewContainerRef on <ng-template> inside <ng-container>', () => {
|
||||
function ContentTemplate(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
text(0, 'Content');
|
||||
}
|
||||
}
|
||||
|
||||
class Directive {
|
||||
constructor(private _tplRef: TemplateRef<{}>, private _vcRef: ViewContainerRef) {}
|
||||
|
||||
insertView() { this._vcRef.createEmbeddedView(this._tplRef); }
|
||||
|
||||
clear() { this._vcRef.clear(); }
|
||||
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: Directive,
|
||||
selectors: [['', 'dir', '']],
|
||||
factory:
|
||||
() => directive = new Directive(
|
||||
directiveInject(TemplateRef as any), directiveInject(ViewContainerRef as any)),
|
||||
});
|
||||
}
|
||||
|
||||
let directive: Directive;
|
||||
|
||||
/**
|
||||
* <ng-container>
|
||||
* <ng-template dir>Content</ng-template>
|
||||
* </ng-container>
|
||||
*/
|
||||
const App = createComponent('app', function(rf: RenderFlags) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementContainerStart(0);
|
||||
template(
|
||||
1, ContentTemplate, 1, 0, '', [AttributeMarker.SelectOnly, 'dir'], [],
|
||||
templateRefExtractor);
|
||||
elementContainerEnd();
|
||||
}
|
||||
}, 2, 0, [Directive]);
|
||||
|
||||
|
||||
const fixture = new ComponentFixture(App);
|
||||
expect(fixture.html).toEqual('');
|
||||
|
||||
directive !.insertView();
|
||||
fixture.update();
|
||||
expect(fixture.html).toEqual('Content');
|
||||
|
||||
directive !.clear();
|
||||
fixture.update();
|
||||
expect(fixture.html).toEqual('');
|
||||
});
|
||||
|
||||
it('should not set any attributes', () => {
|
||||
/**
|
||||
* <div><ng-container id="foo"></ng-container></div>
|
||||
|
@ -11,7 +11,7 @@ import 'reflect-metadata';
|
||||
import {InjectorDef, defineInjectable} from '@angular/core/src/di/defs';
|
||||
import {Injectable} from '@angular/core/src/di/injectable';
|
||||
import {inject, setCurrentInjector} from '@angular/core/src/di/injector';
|
||||
import {ivyEnabled} from '@angular/core/src/ivy_switch/compiler/index';
|
||||
import {ivyEnabled} from '@angular/core/src/ivy_switch';
|
||||
import {Component, HostBinding, HostListener, Input, Output, Pipe} from '@angular/core/src/metadata/directives';
|
||||
import {NgModule, NgModuleDef} from '@angular/core/src/metadata/ng_module';
|
||||
import {ComponentDef, PipeDef} from '@angular/core/src/render3/interfaces/definition';
|
||||
|
@ -13,8 +13,12 @@ import {ViewContainerRef} from '@angular/core/src/linker/view_container_ref';
|
||||
import {Renderer2} from '@angular/core/src/render/api';
|
||||
import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util';
|
||||
|
||||
import {SWITCH_CHANGE_DETECTOR_REF_FACTORY__POST_R3__ as R3_CHANGE_DETECTOR_REF_FACTORY} from '../../src/change_detection/change_detector_ref';
|
||||
import {Injector} from '../../src/di/injector';
|
||||
import {R3_CHANGE_DETECTOR_REF_FACTORY, R3_ELEMENT_REF_FACTORY, R3_RENDERER2_FACTORY, R3_TEMPLATE_REF_FACTORY, R3_VIEW_CONTAINER_REF_FACTORY} from '../../src/ivy_switch/runtime/ivy_switch_on';
|
||||
import {SWITCH_ELEMENT_REF_FACTORY__POST_R3__ as R3_ELEMENT_REF_FACTORY} from '../../src/linker/element_ref';
|
||||
import {SWITCH_TEMPLATE_REF_FACTORY__POST_R3__ as R3_TEMPLATE_REF_FACTORY} from '../../src/linker/template_ref';
|
||||
import {SWITCH_VIEW_CONTAINER_REF_FACTORY__POST_R3__ as R3_VIEW_CONTAINER_REF_FACTORY} from '../../src/linker/view_container_ref';
|
||||
import {SWITCH_RENDERER2_FACTORY__POST_R3__ as R3_RENDERER2_FACTORY} from '../../src/render/api';
|
||||
import {CreateComponentOptions} from '../../src/render3/component';
|
||||
import {discoverDirectives, getContext, isComponentInstance} from '../../src/render3/context_discovery';
|
||||
import {extractDirectiveDef, extractPipeDef} from '../../src/render3/definition';
|
||||
|
52
packages/router/src/operators/prioritized_guard_value.ts
Normal file
52
packages/router/src/operators/prioritized_guard_value.ts
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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 {Observable, OperatorFunction, combineLatest} from 'rxjs';
|
||||
import {filter, scan, startWith, switchMap, take} from 'rxjs/operators';
|
||||
|
||||
import {UrlTree} from '../url_tree';
|
||||
|
||||
const INITIAL_VALUE = Symbol('INITIAL_VALUE');
|
||||
declare type INTERIM_VALUES = typeof INITIAL_VALUE | boolean | UrlTree;
|
||||
|
||||
export function prioritizedGuardValue():
|
||||
OperatorFunction<Observable<boolean|UrlTree>[], boolean|UrlTree> {
|
||||
return switchMap(obs => {
|
||||
return combineLatest(
|
||||
...obs.map(o => o.pipe(take(1), startWith(INITIAL_VALUE as INTERIM_VALUES))))
|
||||
.pipe(
|
||||
scan(
|
||||
(acc: INTERIM_VALUES, list: INTERIM_VALUES[]) => {
|
||||
let isPending = false;
|
||||
return list.reduce((innerAcc, val, i: number) => {
|
||||
if (innerAcc !== INITIAL_VALUE) return innerAcc;
|
||||
|
||||
// Toggle pending flag if any values haven't been set yet
|
||||
if (val === INITIAL_VALUE) isPending = true;
|
||||
|
||||
// Any other return values are only valid if we haven't yet hit a pending call.
|
||||
// This guarantees that in the case of a guard at the bottom of the tree that
|
||||
// returns a redirect, we will wait for the higher priority guard at the top to
|
||||
// finish before performing the redirect.
|
||||
if (!isPending) {
|
||||
// Early return when we hit a `false` value as that should always cancel
|
||||
// navigation
|
||||
if (val === false) return val;
|
||||
|
||||
if (i === list.length - 1 || val instanceof UrlTree) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
return innerAcc;
|
||||
}, acc);
|
||||
},
|
||||
INITIAL_VALUE),
|
||||
filter(item => item !== INITIAL_VALUE), take(1)) as Observable<boolean|UrlTree>;
|
||||
});
|
||||
}
|
@ -16,6 +16,7 @@ ts_library(
|
||||
"//packages/router/testing",
|
||||
"@rxjs",
|
||||
"@rxjs//operators",
|
||||
"@rxjs//testing",
|
||||
],
|
||||
)
|
||||
|
||||
|
182
packages/router/test/operators/prioritized_guard_value.spec.ts
Normal file
182
packages/router/test/operators/prioritized_guard_value.spec.ts
Normal file
@ -0,0 +1,182 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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 {TestBed} from '@angular/core/testing';
|
||||
import {Observable, Observer, of } from 'rxjs';
|
||||
import {every, mergeMap} from 'rxjs/operators';
|
||||
import {TestScheduler} from 'rxjs/testing';
|
||||
|
||||
import {prioritizedGuardValue} from '../../src/operators/prioritized_guard_value';
|
||||
import {Router} from '../../src/router';
|
||||
import {UrlTree} from '../../src/url_tree';
|
||||
import {RouterTestingModule} from '../../testing/src/router_testing_module';
|
||||
|
||||
|
||||
describe('prioritizedGuardValue operator', () => {
|
||||
let testScheduler: TestScheduler;
|
||||
let router: Router;
|
||||
const TF = {T: true, F: false};
|
||||
|
||||
beforeEach(() => { TestBed.configureTestingModule({imports: [RouterTestingModule]}); });
|
||||
beforeEach(() => { testScheduler = new TestScheduler(assertDeepEquals); });
|
||||
beforeEach(() => { router = TestBed.get(Router); });
|
||||
|
||||
it('should return true if all values are true', () => {
|
||||
testScheduler.run(({hot, cold, expectObservable}) => {
|
||||
|
||||
const a = cold(' --(T|)', TF);
|
||||
const b = cold(' ----------(T|)', TF);
|
||||
const c = cold(' ------(T|)', TF);
|
||||
const source = hot('---o--', {o: [a, b, c]});
|
||||
|
||||
const expected = ' -------------T--';
|
||||
|
||||
expectObservable(source.pipe(prioritizedGuardValue()))
|
||||
.toBe(expected, TF, /* an error here maybe */);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return false if observables to the left of false have produced a value', () => {
|
||||
testScheduler.run(({hot, cold, expectObservable}) => {
|
||||
|
||||
const a = cold(' --(T|)', TF);
|
||||
const b = cold(' ----------(T|)', TF);
|
||||
const c = cold(' ------(F|)', TF);
|
||||
const source = hot('---o--', {o: [a, b, c]});
|
||||
|
||||
const expected = ' -------------F--';
|
||||
|
||||
expectObservable(source.pipe(prioritizedGuardValue()))
|
||||
.toBe(expected, TF, /* an error here maybe */);
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore results for unresolved sets of Observables', () => {
|
||||
testScheduler.run(({hot, cold, expectObservable}) => {
|
||||
|
||||
const a = cold(' --(T|)', TF);
|
||||
const b = cold(' -------------(T|)', TF);
|
||||
const c = cold(' ------(F|)', TF);
|
||||
|
||||
const z = cold(' ----(T|)', TF);
|
||||
|
||||
const source = hot('---o----p----', {o: [a, b, c], p: [z]});
|
||||
|
||||
const expected = ' ------------T---';
|
||||
|
||||
expectObservable(source.pipe(prioritizedGuardValue()))
|
||||
.toBe(expected, TF, /* an error here maybe */);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return UrlTree if higher priority guards have resolved', () => {
|
||||
testScheduler.run(({hot, cold, expectObservable}) => {
|
||||
|
||||
const urlTree = router.parseUrl('/');
|
||||
|
||||
const urlLookup = {U: urlTree};
|
||||
|
||||
const a = cold(' --(T|)', TF);
|
||||
const b = cold(' ----------(U|)', urlLookup);
|
||||
const c = cold(' ------(T|)', TF);
|
||||
|
||||
const source = hot('---o---', {o: [a, b, c]});
|
||||
|
||||
const expected = ' -------------U---';
|
||||
|
||||
expectObservable(source.pipe(prioritizedGuardValue()))
|
||||
.toBe(expected, urlLookup, /* an error here maybe */);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return false even with UrlTree if UrlTree is lower priority', () => {
|
||||
testScheduler.run(({hot, cold, expectObservable}) => {
|
||||
|
||||
const urlTree = router.parseUrl('/');
|
||||
|
||||
const urlLookup = {U: urlTree};
|
||||
|
||||
const a = cold(' --(T|)', TF);
|
||||
const b = cold(' ----------(F|)', TF);
|
||||
const c = cold(' ------(U|)', urlLookup);
|
||||
|
||||
const source = hot('---o---', {o: [a, b, c]});
|
||||
|
||||
const expected = ' -------------F---';
|
||||
|
||||
expectObservable(source.pipe(prioritizedGuardValue()))
|
||||
.toBe(expected, TF, /* an error here maybe */);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return UrlTree even after a false if the false is lower priority', () => {
|
||||
testScheduler.run(({hot, cold, expectObservable}) => {
|
||||
|
||||
const urlTree = router.parseUrl('/');
|
||||
|
||||
const urlLookup = {U: urlTree};
|
||||
|
||||
const a = cold(' --(T|)', TF);
|
||||
const b = cold(' ----------(U|)', urlLookup);
|
||||
const c = cold(' ------(F|)', TF);
|
||||
|
||||
const source = hot('---o---', {o: [a, b, c]});
|
||||
|
||||
const expected = ' -------------U----';
|
||||
|
||||
expectObservable(source.pipe(prioritizedGuardValue()))
|
||||
.toBe(expected, urlLookup, /* an error here maybe */);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the highest priority UrlTree', () => {
|
||||
testScheduler.run(({hot, cold, expectObservable}) => {
|
||||
|
||||
const urlTreeU = router.parseUrl('/u');
|
||||
const urlTreeR = router.parseUrl('/r');
|
||||
const urlTreeL = router.parseUrl('/l');
|
||||
|
||||
const urlLookup = {U: urlTreeU, R: urlTreeR, L: urlTreeL};
|
||||
|
||||
const a = cold(' ----------(U|)', urlLookup);
|
||||
const b = cold(' -----(R|)', urlLookup);
|
||||
const c = cold(' --(L|)', urlLookup);
|
||||
|
||||
const source = hot('---o---', {o: [a, b, c]});
|
||||
|
||||
const expected = ' -------------U---';
|
||||
|
||||
expectObservable(source.pipe(prioritizedGuardValue()))
|
||||
.toBe(expected, urlLookup, /* an error here maybe */);
|
||||
});
|
||||
});
|
||||
|
||||
it('should propagate errors', () => {
|
||||
testScheduler.run(({hot, cold, expectObservable}) => {
|
||||
|
||||
const a = cold(' --(T|)', TF);
|
||||
const b = cold(' ------#', TF);
|
||||
const c = cold(' ----------(F|)', TF);
|
||||
const source = hot('---o------', {o: [a, b, c]});
|
||||
|
||||
const expected = ' ---------#';
|
||||
|
||||
expectObservable(source.pipe(prioritizedGuardValue()))
|
||||
.toBe(expected, TF, /* an error here maybe */);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
function assertDeepEquals(a: any, b: any) {
|
||||
return expect(a).toEqual(b);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user