Compare commits
226 Commits
7.0.2
...
7.1.0-beta
Author | SHA1 | Date | |
---|---|---|---|
ad6771dcd4 | |||
2c25d29b25 | |||
ec29fd3e7b | |||
d042c4afe0 | |||
5d740785a9 | |||
68b2211e64 | |||
332394d87c | |||
62b4ff5250 | |||
634e9e970a | |||
678c28175e | |||
87a7f2dcb9 | |||
8fc4ae51fb | |||
66be3c9f51 | |||
b95089db20 | |||
8a4ec8e565 | |||
95743e3a64 | |||
9ad54d74d2 | |||
0e1cceed50 | |||
23648b052a | |||
91d2368e37 | |||
ede65dbede | |||
578e4c7642 | |||
96770e5c6b | |||
1130e48541 | |||
2a869271f6 | |||
c0bf222a05 | |||
d72d50d83e | |||
ef6f1605db | |||
ea7ec4a6b3 | |||
72f3d99920 | |||
f6991dec3b | |||
0367d14044 | |||
8bf51db3e7 | |||
4f250726aa | |||
86d3099141 | |||
d4c3742e25 | |||
95993e1dd5 | |||
d52d82d744 | |||
d2e6d6978e | |||
da4a28e692 | |||
b876356527 | |||
07509da78d | |||
9ff8155cd9 | |||
56f44be0aa | |||
d50d400231 | |||
19fcfc3d00 | |||
c048358cf9 | |||
8171a2ab94 | |||
2fd4c372d5 | |||
f76ce84ae1 | |||
f3859130f2 | |||
0f274d65c4 | |||
cb71b93351 | |||
4b51d31aea | |||
40bbfbf961 | |||
30f319a11f | |||
5d7ba57dd5 | |||
9cd8327051 | |||
1d8f939c96 | |||
2e28d9f012 | |||
7c730fe5b3 | |||
f233131974 | |||
aefa06f7df | |||
d878f3df93 | |||
f8741c0985 | |||
5e2ce9b2a6 | |||
d725ab5142 | |||
f1a860fbf7 | |||
141f9b2386 | |||
95f852e856 | |||
2c7386c961 | |||
d5cbcef0ea | |||
b0476f308b | |||
9dc52d9d04 | |||
297dc2bc02 | |||
0cc9842bf6 | |||
54ea10288e | |||
1880c9531f | |||
13a803d4f7 | |||
f6c2db818e | |||
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 |
@ -126,8 +126,6 @@ jobs:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
# don't run this job on the patch branch (to preserve resources)
|
||||
- run: circleci step halt
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
@ -143,8 +141,6 @@ jobs:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
# don't run this job on the patch branch (to preserve resources)
|
||||
- run: circleci step halt
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
|
1
.github/angular-robot.yml
vendored
1
.github/angular-robot.yml
vendored
@ -50,6 +50,7 @@ merge:
|
||||
- "**/BUILD.bazel"
|
||||
- "packages/**/integrationtest/**"
|
||||
- "packages/**/test/**"
|
||||
- "packages/compiler-cli/src/ngcc/**"
|
||||
|
||||
# comment that will be added to a PR when there is a conflict, leave empty or set to false to disable
|
||||
mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges.
|
||||
|
32
CHANGELOG.md
32
CHANGELOG.md
@ -1,3 +1,17 @@
|
||||
<a name="7.1.0-beta.1"></a>
|
||||
# [7.1.0-beta.1](https://github.com/angular/angular/compare/7.1.0-beta.0...7.1.0-beta.1) (2018-10-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** generate inputs with aliases properly ([#26774](https://github.com/angular/angular/issues/26774)) ([19fcfc3](https://github.com/angular/angular/commit/19fcfc3))
|
||||
* **compiler:** generate relative paths only in summary file errors ([#26759](https://github.com/angular/angular/issues/26759)) ([56f44be](https://github.com/angular/angular/commit/56f44be))
|
||||
* **core:** ignore comment nodes under unsafe elements ([#25879](https://github.com/angular/angular/issues/25879)) ([d5cbcef](https://github.com/angular/angular/commit/d5cbcef))
|
||||
* **core:** Remove static dependency from [@angular](https://github.com/angular)/core to [@angular](https://github.com/angular)/compiler ([#26734](https://github.com/angular/angular/issues/26734)) ([d042c4a](https://github.com/angular/angular/commit/d042c4a))
|
||||
* **core:** support computed base class in metadata inheritance ([#24014](https://github.com/angular/angular/issues/24014)) ([95743e3](https://github.com/angular/angular/commit/95743e3))
|
||||
|
||||
|
||||
|
||||
<a name="7.0.2"></a>
|
||||
## [7.0.2](https://github.com/angular/angular/compare/7.0.1...7.0.2) (2018-10-31)
|
||||
|
||||
@ -10,6 +24,23 @@
|
||||
|
||||
|
||||
|
||||
<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)
|
||||
|
||||
@ -91,6 +122,7 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
||||
### Bug Fixes
|
||||
|
||||
* **platform-browser:** fix [#22155](https://github.com/angular/angular/issues/22155), destroy hammer manager when `HammerInstance.off()` is run ([#22156](https://github.com/angular/angular/issues/22156)) ([3b4d9dc](https://github.com/angular/angular/commit/3b4d9dc))
|
||||
* **upgrade:** properly destroy upgraded component elements and descendants ([#26209](https://github.com/angular/angular/issues/26209)) ([623adbb](https://github.com/angular/angular/commit/623adbb)), closes [#26208](https://github.com/angular/angular/issues/26208)
|
||||
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@ describe('Security E2E Tests', () => {
|
||||
expect(interpolated.getText())
|
||||
.toContain('Template <script>alert("0wned")</script> <b>Syntax</b>');
|
||||
let bound = element(By.className('e2e-inner-html-bound'));
|
||||
expect(bound.getText()).toContain('Template alert("0wned") Syntax');
|
||||
expect(bound.getText()).toContain('Template Syntax');
|
||||
let bold = element(By.css('.e2e-inner-html-bound b'));
|
||||
expect(bold.getText()).toContain('Syntax');
|
||||
});
|
||||
|
@ -119,7 +119,7 @@ vulnerability. For example, code contained in a `<script>` tag is executed:
|
||||
|
||||
|
||||
Angular recognizes the value as unsafe and automatically sanitizes it, which removes the `<script>`
|
||||
tag but keeps safe content such as the text content of the `<script>` tag and the `<b>` element.
|
||||
tag but keeps safe content such as the `<b>` element.
|
||||
|
||||
|
||||
<figure>
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 27 KiB |
@ -63,7 +63,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`
|
||||
- `aot`: Compile in ivy AOT move, e.g. `--define=compile=aot`
|
||||
|
@ -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,10 +12,12 @@ 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",
|
||||
"@ngdeps//reflect-metadata",
|
||||
],
|
||||
@ -24,6 +26,7 @@ ts_library(
|
||||
ng_rollup_bundle(
|
||||
name = "bundle",
|
||||
entry_point = "modules/benchmarks/src/largetable/render3/index.js",
|
||||
tags = ["ivy-only"],
|
||||
deps = [
|
||||
":largetable_lib",
|
||||
],
|
||||
@ -44,6 +47,7 @@ ts_devserver(
|
||||
"index.html",
|
||||
":favicon",
|
||||
],
|
||||
tags = ["ivy-only"],
|
||||
)
|
||||
|
||||
protractor_web_test(
|
||||
@ -57,7 +61,7 @@ protractor_web_test(
|
||||
"@ngdeps//reflect-metadata",
|
||||
"@ngdeps//yargs",
|
||||
],
|
||||
on_prepare = ":protractor.on-prepare.js",
|
||||
on_prepare = ":protractor.on_prepare.js",
|
||||
server = ":devserver",
|
||||
tags = [
|
||||
"fixme-ivy-aot",
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
@ -5,15 +5,17 @@ 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",
|
||||
"@ngdeps//reflect-metadata",
|
||||
],
|
||||
@ -61,6 +63,7 @@ protractor_web_test(
|
||||
"ivy-only",
|
||||
],
|
||||
deps = [
|
||||
"//modules/benchmarks/src/tree:perf_lib",
|
||||
"@ngdeps//node-uuid",
|
||||
"@ngdeps//protractor",
|
||||
"@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.2",
|
||||
"version": "7.1.0-beta.1",
|
||||
"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';
|
||||
@ -295,10 +295,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) {`);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -128,7 +128,7 @@ describe('Renderer', () => {
|
||||
}));
|
||||
expect(addDefinitionsSpy.calls.first().args[2])
|
||||
.toEqual(
|
||||
`A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""]], factory: function A_Factory(t) { return new (t || A)(); }, features: [ɵngcc0.ɵPublicFeature] });`);
|
||||
`A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""]], factory: function A_Factory(t) { return new (t || A)(); } });`);
|
||||
});
|
||||
|
||||
it('should call removeDecorators with the source code, a map of class decorators that have been analyzed',
|
||||
|
@ -118,6 +118,10 @@ export class ComponentDecoratorHandler implements
|
||||
preserveWhitespaces = value;
|
||||
}
|
||||
|
||||
const viewProviders: Expression|null = component.has('viewProviders') ?
|
||||
new WrappedNodeExpr(component.get('viewProviders') !) :
|
||||
null;
|
||||
|
||||
// Go through the root directories for this project, and select the one with the smallest
|
||||
// relative path representation.
|
||||
const filePath = node.getSourceFile().fileName;
|
||||
@ -200,9 +204,9 @@ export class ComponentDecoratorHandler implements
|
||||
// analyzed and the full compilation scope for the component can be realized.
|
||||
pipes: EMPTY_MAP,
|
||||
directives: EMPTY_MAP,
|
||||
wrapDirectivesInClosure: false, //
|
||||
wrapDirectivesAndPipesInClosure: false, //
|
||||
animations,
|
||||
viewProviders: null,
|
||||
viewProviders
|
||||
},
|
||||
parsedTemplate: template.nodes,
|
||||
},
|
||||
@ -233,8 +237,8 @@ export class ComponentDecoratorHandler implements
|
||||
const {pipes, containsForwardDecls} = scope;
|
||||
const directives = new Map<string, Expression>();
|
||||
scope.directives.forEach((meta, selector) => directives.set(selector, meta.directive));
|
||||
const wrapDirectivesInClosure: boolean = !!containsForwardDecls;
|
||||
metadata = {...metadata, directives, pipes, wrapDirectivesInClosure};
|
||||
const wrapDirectivesAndPipesInClosure: boolean = !!containsForwardDecls;
|
||||
metadata = {...metadata, directives, pipes, wrapDirectivesAndPipesInClosure};
|
||||
}
|
||||
|
||||
const res = compileComponentFromMetadata(metadata, pool, makeBindingParser());
|
||||
|
@ -110,12 +110,14 @@ export function extractDirectiveMetadata(
|
||||
// fields.
|
||||
const inputsFromMeta = parseFieldToPropertyMapping(directive, 'inputs', reflector, checker);
|
||||
const inputsFromFields = parseDecoratedFields(
|
||||
filterToMembersWithDecorator(decoratedElements, 'Input', coreModule), reflector, checker);
|
||||
filterToMembersWithDecorator(decoratedElements, 'Input', coreModule), reflector, checker,
|
||||
resolveInput);
|
||||
|
||||
// And outputs.
|
||||
const outputsFromMeta = parseFieldToPropertyMapping(directive, 'outputs', reflector, checker);
|
||||
const outputsFromFields = parseDecoratedFields(
|
||||
filterToMembersWithDecorator(decoratedElements, 'Output', coreModule), reflector, checker);
|
||||
filterToMembersWithDecorator(decoratedElements, 'Output', coreModule), reflector, checker,
|
||||
resolveOutput) as{[field: string]: string};
|
||||
// Construct the list of queries.
|
||||
const contentChildFromFields = queriesFromFields(
|
||||
filterToMembersWithDecorator(decoratedElements, 'ContentChild', coreModule), reflector,
|
||||
@ -146,6 +148,9 @@ export function extractDirectiveMetadata(
|
||||
|
||||
const host = extractHostBindings(directive, decoratedElements, reflector, checker, coreModule);
|
||||
|
||||
const providers: Expression|null =
|
||||
directive.has('providers') ? new WrappedNodeExpr(directive.get('providers') !) : null;
|
||||
|
||||
// Determine if `ngOnChanges` is a lifecycle hook defined on the component.
|
||||
const usesOnChanges = members.some(
|
||||
member => !member.isStatic && member.kind === ClassMemberKind.Method &&
|
||||
@ -176,8 +181,7 @@ export function extractDirectiveMetadata(
|
||||
outputs: {...outputsFromMeta, ...outputsFromFields}, queries, selector,
|
||||
type: new WrappedNodeExpr(clazz.name !),
|
||||
typeArgumentCount: reflector.getGenericArityOfClass(clazz) || 0,
|
||||
typeSourceSpan: null !, usesInheritance, exportAs,
|
||||
providers: null,
|
||||
typeSourceSpan: null !, usesInheritance, exportAs, providers
|
||||
};
|
||||
return {decoratedElements, decorator: directive, metadata};
|
||||
}
|
||||
@ -328,7 +332,9 @@ function parseFieldToPropertyMapping(
|
||||
*/
|
||||
function parseDecoratedFields(
|
||||
fields: {member: ClassMember, decorators: Decorator[]}[], reflector: ReflectionHost,
|
||||
checker: ts.TypeChecker) {
|
||||
checker: ts.TypeChecker,
|
||||
mapValueResolver: (publicName: string, internalName: string) =>
|
||||
string | [string, string]): {[field: string]: string | [string, string]} {
|
||||
return fields.reduce(
|
||||
(results, field) => {
|
||||
const fieldName = field.member.name;
|
||||
@ -342,7 +348,7 @@ function parseDecoratedFields(
|
||||
if (typeof property !== 'string') {
|
||||
throw new Error(`Decorator argument must resolve to a string`);
|
||||
}
|
||||
results[fieldName] = property;
|
||||
results[fieldName] = mapValueResolver(property, fieldName);
|
||||
} else {
|
||||
// Too many arguments.
|
||||
throw new Error(
|
||||
@ -351,7 +357,7 @@ function parseDecoratedFields(
|
||||
});
|
||||
return results;
|
||||
},
|
||||
{} as{[field: string]: string});
|
||||
{} as{[field: string]: string | [string, string]});
|
||||
}
|
||||
|
||||
function resolveInput(publicName: string, internalName: string): [string, string] {
|
||||
|
@ -64,18 +64,6 @@ export function getConstructorDependencies(
|
||||
ErrorCode.PARAM_MISSING_TOKEN, param.nameNode,
|
||||
`No suitable token for parameter ${param.name || idx} of class ${clazz.name!.text}`);
|
||||
}
|
||||
if (ts.isIdentifier(tokenExpr)) {
|
||||
const importedSymbol = reflector.getImportOfIdentifier(tokenExpr);
|
||||
if (importedSymbol !== null && importedSymbol.from === '@angular/core') {
|
||||
switch (importedSymbol.name) {
|
||||
case 'Injector':
|
||||
resolved = R3ResolvedDependencyType.Injector;
|
||||
break;
|
||||
default:
|
||||
// Leave as a Token or Attribute.
|
||||
}
|
||||
}
|
||||
}
|
||||
const token = new WrappedNodeExpr(tokenExpr);
|
||||
useType.push({token, optional, self, skipSelf, host, resolved});
|
||||
});
|
||||
|
@ -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));
|
||||
}
|
@ -518,7 +518,10 @@ function tcbGetInputBindingExpressions(
|
||||
// is desired. Invert `dir.inputs` into `propMatch` to create this map.
|
||||
const propMatch = new Map<string, string>();
|
||||
const inputs = dir.inputs;
|
||||
Object.keys(inputs).forEach(key => propMatch.set(inputs[key] as string, key));
|
||||
Object.keys(inputs).forEach(key => {
|
||||
Array.isArray(inputs[key]) ? propMatch.set(inputs[key][0], key) :
|
||||
propMatch.set(inputs[key] as string, key);
|
||||
});
|
||||
|
||||
// Add a binding expression to the map for each input of the directive that has a
|
||||
// matching binding.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -415,7 +415,6 @@ describe('compiler compliance', () => {
|
||||
factory: function MyComponent_Factory(t){
|
||||
return new (t || MyComponent)();
|
||||
},
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf,ctx){
|
||||
@ -470,7 +469,6 @@ describe('compiler compliance', () => {
|
||||
type: ChildComponent,
|
||||
selectors: [["child"]],
|
||||
factory: function ChildComponent_Factory(t) { return new (t || ChildComponent)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function ChildComponent_Template(rf, ctx) {
|
||||
@ -485,8 +483,7 @@ describe('compiler compliance', () => {
|
||||
SomeDirective.ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||
type: SomeDirective,
|
||||
selectors: [["", "some-directive", ""]],
|
||||
factory: function SomeDirective_Factory(t) {return new (t || SomeDirective)(); },
|
||||
features: [$r3$.ɵPublicFeature]
|
||||
factory: function SomeDirective_Factory(t) {return new (t || SomeDirective)(); }
|
||||
});
|
||||
`;
|
||||
|
||||
@ -498,7 +495,6 @@ describe('compiler compliance', () => {
|
||||
type: MyComponent,
|
||||
selectors: [["my-component"]],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 2,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
@ -543,8 +539,7 @@ describe('compiler compliance', () => {
|
||||
SomeDirective.ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||
type: SomeDirective,
|
||||
selectors: [["div", "some-directive", "", 8, "foo", 3, "title", "", 9, "baz"]],
|
||||
factory: function SomeDirective_Factory(t) {return new (t || SomeDirective)(); },
|
||||
features: [$r3$.ɵPublicFeature]
|
||||
factory: function SomeDirective_Factory(t) {return new (t || SomeDirective)(); }
|
||||
});
|
||||
`;
|
||||
|
||||
@ -553,8 +548,7 @@ describe('compiler compliance', () => {
|
||||
OtherDirective.ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||
type: OtherDirective,
|
||||
selectors: [["", 5, "span", "title", "", 9, "baz"]],
|
||||
factory: function OtherDirective_Factory(t) {return new (t || OtherDirective)(); },
|
||||
features: [$r3$.ɵPublicFeature]
|
||||
factory: function OtherDirective_Factory(t) {return new (t || OtherDirective)(); }
|
||||
});
|
||||
`;
|
||||
|
||||
@ -590,8 +584,7 @@ describe('compiler compliance', () => {
|
||||
hostBindings: function HostBindingDir_HostBindings(dirIndex, elIndex) {
|
||||
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵload(dirIndex).dirId));
|
||||
},
|
||||
hostVars: 1,
|
||||
features: [$r3$.ɵPublicFeature]
|
||||
hostVars: 1
|
||||
});
|
||||
`;
|
||||
|
||||
@ -635,7 +628,6 @@ describe('compiler compliance', () => {
|
||||
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵpureFunction1(1, $ff$, $r3$.ɵload(dirIndex).id)));
|
||||
},
|
||||
hostVars: 3,
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
template: function HostBindingComp_Template(rf, ctx) {}
|
||||
@ -679,7 +671,6 @@ describe('compiler compliance', () => {
|
||||
$r3$.ɵdirectiveInject(ElementRef), $r3$.ɵdirectiveInject(ViewContainerRef),
|
||||
$r3$.ɵdirectiveInject(ChangeDetectorRef));
|
||||
},
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf, ctx) {}
|
||||
@ -720,8 +711,7 @@ describe('compiler compliance', () => {
|
||||
IfDirective.ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||
type: IfDirective,
|
||||
selectors: [["", "if", ""]],
|
||||
factory: function IfDirective_Factory(t) { return new (t || IfDirective)($r3$.ɵdirectiveInject(TemplateRef)); },
|
||||
features: [$r3$.ɵPublicFeature]
|
||||
factory: function IfDirective_Factory(t) { return new (t || IfDirective)($r3$.ɵdirectiveInject(TemplateRef)); }
|
||||
});`;
|
||||
const MyComponentDefinition = `
|
||||
const $c1$ = ["foo", ""];
|
||||
@ -743,7 +733,6 @@ describe('compiler compliance', () => {
|
||||
type: MyComponent,
|
||||
selectors: [["my-component"]],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 3,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
@ -806,7 +795,6 @@ describe('compiler compliance', () => {
|
||||
type: MyApp,
|
||||
selectors: [["my-app"]],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 1,
|
||||
vars: 3,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
@ -888,7 +876,6 @@ describe('compiler compliance', () => {
|
||||
type: MyApp,
|
||||
selectors: [["my-app"]],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 1,
|
||||
vars: 11,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
@ -952,7 +939,6 @@ describe('compiler compliance', () => {
|
||||
type: MyApp,
|
||||
selectors: [["my-app"]],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 1,
|
||||
vars: 3,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
@ -1020,7 +1006,6 @@ describe('compiler compliance', () => {
|
||||
type: MyApp,
|
||||
selectors: [["my-app"]],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 1,
|
||||
vars: 8,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
@ -1079,7 +1064,6 @@ describe('compiler compliance', () => {
|
||||
type: SimpleComponent,
|
||||
selectors: [["simple"]],
|
||||
factory: function SimpleComponent_Factory(t) { return new (t || SimpleComponent)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 2,
|
||||
vars: 0,
|
||||
template: function SimpleComponent_Template(rf, ctx) {
|
||||
@ -1102,7 +1086,6 @@ describe('compiler compliance', () => {
|
||||
type: ComplexComponent,
|
||||
selectors: [["complex"]],
|
||||
factory: function ComplexComponent_Factory(t) { return new (t || ComplexComponent)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 4,
|
||||
vars: 0,
|
||||
template: function ComplexComponent_Template(rf, ctx) {
|
||||
@ -1171,7 +1154,6 @@ describe('compiler compliance', () => {
|
||||
type: ViewQueryComponent,
|
||||
selectors: [["view-query-component"]],
|
||||
factory: function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵquery(0, SomeDirective, true);
|
||||
@ -1348,9 +1330,9 @@ describe('compiler compliance', () => {
|
||||
factory: function ContentQueryComponent_Factory(t) {
|
||||
return new (t || ContentQueryComponent)();
|
||||
},
|
||||
contentQueries: function ContentQueryComponent_ContentQueries() {
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, true));
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false));
|
||||
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, true), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false), dirIndex);
|
||||
},
|
||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
||||
const instance = $r3$.ɵload(dirIndex);
|
||||
@ -1358,7 +1340,6 @@ describe('compiler compliance', () => {
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && ($instance$.someDir = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && ($instance$.someDirList = $tmp$));
|
||||
},
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 2,
|
||||
vars: 0,
|
||||
template: function ContentQueryComponent_Template(rf, ctx) {
|
||||
@ -1406,9 +1387,9 @@ describe('compiler compliance', () => {
|
||||
…
|
||||
ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
…
|
||||
contentQueries: function ContentQueryComponent_ContentQueries() {
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e0_attrs$, true));
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e1_attrs$, false));
|
||||
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e0_attrs$, true), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e1_attrs$, false), dirIndex);
|
||||
},
|
||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
||||
const instance = $r3$.ɵload(dirIndex);
|
||||
@ -1459,11 +1440,11 @@ describe('compiler compliance', () => {
|
||||
…
|
||||
ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
…
|
||||
contentQueries: function ContentQueryComponent_ContentQueries() {
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e0_attrs$ , true, TemplateRef));
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, true, ElementRef));
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e1_attrs$, false, ElementRef));
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false, TemplateRef));
|
||||
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e0_attrs$ , true, TemplateRef), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, true, ElementRef), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e1_attrs$, false, ElementRef), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false, TemplateRef), dirIndex);
|
||||
},
|
||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
||||
const instance = $r3$.ɵload(dirIndex);
|
||||
@ -1492,7 +1473,7 @@ describe('compiler compliance', () => {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule, Pipe, PipeTransform, OnDestroy} from '@angular/core';
|
||||
|
||||
|
||||
@Pipe({
|
||||
name: 'myPipe',
|
||||
pure: false
|
||||
@ -1502,7 +1483,7 @@ describe('compiler compliance', () => {
|
||||
transform(value: any, ...args: any[]) { return value; }
|
||||
ngOnDestroy(): void { }
|
||||
}
|
||||
|
||||
|
||||
@Pipe({
|
||||
name: 'myPurePipe',
|
||||
pure: true,
|
||||
@ -1510,7 +1491,7 @@ describe('compiler compliance', () => {
|
||||
export class MyPurePipe implements PipeTransform {
|
||||
transform(value: any, ...args: any[]) { return value; }
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: '{{name | myPipe:size | myPurePipe:size }}<p>{{ name | myPipe:1:2:3:4:5 }}</p>'
|
||||
@ -1519,7 +1500,7 @@ describe('compiler compliance', () => {
|
||||
name = 'World';
|
||||
size = 0;
|
||||
}
|
||||
|
||||
|
||||
@NgModule({declarations:[MyPipe, MyPurePipe, MyApp]})
|
||||
export class MyModule {}
|
||||
`
|
||||
@ -1552,7 +1533,6 @@ describe('compiler compliance', () => {
|
||||
type: MyApp,
|
||||
selectors: [["my-app"]],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 6,
|
||||
vars: 17,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
@ -1586,7 +1566,7 @@ describe('compiler compliance', () => {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule, Pipe, PipeTransform, OnDestroy} from '@angular/core';
|
||||
|
||||
|
||||
@Pipe({
|
||||
name: 'myPipe',
|
||||
pure: false
|
||||
@ -1596,14 +1576,14 @@ describe('compiler compliance', () => {
|
||||
transform(value: any, ...args: any[]) { return value; }
|
||||
ngOnDestroy(): void { }
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: '0:{{name | myPipe}}1:{{name | myPipe:1}}2:{{name | myPipe:1:2}}3:{{name | myPipe:1:2:3}}4:{{name | myPipe:1:2:3:4}}'
|
||||
})
|
||||
export class MyApp {
|
||||
}
|
||||
|
||||
|
||||
@NgModule({declarations:[MyPipe, MyApp]})
|
||||
export class MyModule {}
|
||||
`
|
||||
@ -1616,7 +1596,6 @@ describe('compiler compliance', () => {
|
||||
type: MyApp,
|
||||
selectors: [["my-app"]],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 6,
|
||||
vars: 27,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
@ -1630,8 +1609,8 @@ describe('compiler compliance', () => {
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵtextBinding(0, $r3$.ɵinterpolation5(
|
||||
"0:", i0.ɵpipeBind1(1, 5, ctx.name),
|
||||
"1:", i0.ɵpipeBind2(2, 7, ctx.name, 1),
|
||||
"0:", i0.ɵpipeBind1(1, 5, ctx.name),
|
||||
"1:", i0.ɵpipeBind2(2, 7, ctx.name, 1),
|
||||
"2:", i0.ɵpipeBind3(3, 10, ctx.name, 1, 2),
|
||||
"3:", i0.ɵpipeBind4(4, 14, ctx.name, 1, 2, 3),
|
||||
"4:", i0.ɵpipeBindV(5, 19, $r3$.ɵpureFunction1(25, $c0$, ctx.name)),
|
||||
@ -1671,7 +1650,6 @@ describe('compiler compliance', () => {
|
||||
type: MyComponent,
|
||||
selectors: [["my-component"]],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 3,
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
@ -1765,7 +1743,6 @@ describe('compiler compliance', () => {
|
||||
type: MyComponent,
|
||||
selectors: [["my-component"]],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 6,
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
@ -1910,8 +1887,8 @@ describe('compiler compliance', () => {
|
||||
type: LifecycleComp,
|
||||
selectors: [["lifecycle-comp"]],
|
||||
factory: function LifecycleComp_Factory(t) { return new (t || LifecycleComp)(); },
|
||||
inputs: {nameMin: "name"},
|
||||
features: [$r3$.ɵPublicFeature, $r3$.ɵNgOnChangesFeature],
|
||||
inputs: {nameMin: ["name", "nameMin"]},
|
||||
features: [$r3$.ɵNgOnChangesFeature],
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
template: function LifecycleComp_Template(rf, ctx) {}
|
||||
@ -1922,7 +1899,6 @@ describe('compiler compliance', () => {
|
||||
type: SimpleLayout,
|
||||
selectors: [["simple-layout"]],
|
||||
factory: function SimpleLayout_Factory(t) { return new (t || SimpleLayout)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 2,
|
||||
vars: 2,
|
||||
template: function SimpleLayout_Template(rf, ctx) {
|
||||
@ -2032,7 +2008,7 @@ describe('compiler compliance', () => {
|
||||
factory: function ForOfDirective_Factory(t) {
|
||||
return new (t || ForOfDirective)($r3$.ɵdirectiveInject(ViewContainerRef), $r3$.ɵdirectiveInject(TemplateRef));
|
||||
},
|
||||
features: [$r3$.ɵPublicFeature, $r3$.ɵNgOnChangesFeature],
|
||||
features: [$r3$.ɵNgOnChangesFeature],
|
||||
inputs: {forOf: "forOf"}
|
||||
});
|
||||
`;
|
||||
@ -2052,7 +2028,6 @@ describe('compiler compliance', () => {
|
||||
type: MyComponent,
|
||||
selectors: [["my-component"]],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 2,
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf, ctx){
|
||||
@ -2108,7 +2083,7 @@ describe('compiler compliance', () => {
|
||||
factory: function ForOfDirective_Factory(t) {
|
||||
return new (t || ForOfDirective)($r3$.ɵdirectiveInject(ViewContainerRef), $r3$.ɵdirectiveInject(TemplateRef));
|
||||
},
|
||||
features: [$r3$.ɵPublicFeature, $r3$.ɵNgOnChangesFeature],
|
||||
features: [$r3$.ɵNgOnChangesFeature],
|
||||
inputs: {forOf: "forOf"}
|
||||
});
|
||||
`;
|
||||
@ -2131,7 +2106,6 @@ describe('compiler compliance', () => {
|
||||
type: MyComponent,
|
||||
selectors: [["my-component"]],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 2,
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
@ -2231,7 +2205,6 @@ describe('compiler compliance', () => {
|
||||
type: MyComponent,
|
||||
selectors: [["my-component"]],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 2,
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
@ -2252,9 +2225,83 @@ describe('compiler compliance', () => {
|
||||
expectEmit(source, MyComponentDefinition, 'Invalid component definition');
|
||||
});
|
||||
});
|
||||
|
||||
it('should instantiate directives in a closure when they are forward referenced', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule, Directive} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'host-binding-comp',
|
||||
template: \`
|
||||
<my-forward-directive></my-forward-directive>
|
||||
\`
|
||||
})
|
||||
export class HostBindingComp {
|
||||
}
|
||||
|
||||
@Directive({
|
||||
selector: 'my-forward-directive'
|
||||
})
|
||||
class MyForwardDirective {}
|
||||
|
||||
@NgModule({declarations: [HostBindingComp, MyForwardDirective]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const MyAppDefinition = `
|
||||
…
|
||||
directives: function () { return [MyForwardDirective]; }
|
||||
…
|
||||
`;
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
const source = result.source;
|
||||
expectEmit(source, MyAppDefinition, 'Invalid component definition');
|
||||
});
|
||||
|
||||
it('should instantiate pipes in a closure when they are forward referenced', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule, Pipe} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'host-binding-comp',
|
||||
template: \`
|
||||
<div [attr.style]="{} | my_forward_pipe">...</div>
|
||||
\`
|
||||
})
|
||||
export class HostBindingComp {
|
||||
}
|
||||
|
||||
@Pipe({
|
||||
name: 'my_forward_pipe'
|
||||
})
|
||||
class MyForwardPipe {}
|
||||
|
||||
@NgModule({declarations: [HostBindingComp, MyForwardPipe]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const MyAppDefinition = `
|
||||
…
|
||||
pipes: function () { return [MyForwardPipe]; }
|
||||
…
|
||||
`;
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
const source = result.source;
|
||||
expectEmit(source, MyAppDefinition, 'Invalid component definition');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inherited bare classes', () => {
|
||||
describe('inherited base classes', () => {
|
||||
it('should add ngBaseDef if one or more @Input is present', () => {
|
||||
const files = {
|
||||
app: {
|
||||
|
@ -42,7 +42,6 @@ describe('compiler compliance: directives', () => {
|
||||
type: MyComponent,
|
||||
selectors: [["my-component"]],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
@ -88,7 +87,6 @@ describe('compiler compliance: directives', () => {
|
||||
type: MyComponent,
|
||||
selectors: [["my-component"]],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
|
@ -56,7 +56,7 @@ describe('compiler compliance: listen()', () => {
|
||||
…
|
||||
inputs:{
|
||||
componentInput: "componentInput",
|
||||
originalComponentInput: "renamedComponentInput"
|
||||
originalComponentInput: ["renamedComponentInput", "originalComponentInput"]
|
||||
},
|
||||
outputs: {
|
||||
componentOutput: "componentOutput",
|
||||
@ -70,7 +70,7 @@ describe('compiler compliance: listen()', () => {
|
||||
…
|
||||
inputs:{
|
||||
directiveInput: "directiveInput",
|
||||
originalDirectiveInput: "renamedDirectiveInput"
|
||||
originalDirectiveInput: ["renamedDirectiveInput", "originalDirectiveInput"]
|
||||
},
|
||||
outputs: {
|
||||
directiveOutput: "directiveOutput",
|
||||
@ -86,4 +86,4 @@ describe('compiler compliance: listen()', () => {
|
||||
expectEmit(result.source, directiveDef, 'Incorrect directive definition');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -202,7 +202,6 @@ describe('compiler compliance: listen()', () => {
|
||||
type: MyComponent,
|
||||
selectors: [["my-component"]],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 4,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
|
@ -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 {setup} from '@angular/compiler/test/aot/test_util';
|
||||
import {compile, expectEmit} from './mock_compile';
|
||||
|
||||
describe('compiler compliance: providers', () => {
|
||||
const angularFiles = setup({
|
||||
compileAngular: false,
|
||||
compileFakeCore: true,
|
||||
compileAnimations: false,
|
||||
});
|
||||
|
||||
it('should emit the ProvidersFeature feature when providers and viewProviders', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
abstract class Greeter { abstract greet(): string; }
|
||||
|
||||
class GreeterEN implements Greeter {
|
||||
greet() { return 'Hi'; }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: '<div></div>',
|
||||
providers: [GreeterEN, {provide: Greeter, useClass: GreeterEN}],
|
||||
viewProviders: [GreeterEN]
|
||||
})
|
||||
export class MyComponent {
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
expectEmit(
|
||||
result.source,
|
||||
'features: [i0.ɵProvidersFeature([GreeterEN, {provide: Greeter, useClass: GreeterEN}], [GreeterEN])],',
|
||||
'Incorrect features');
|
||||
});
|
||||
|
||||
it('should emit the ProvidersFeature feature when providers only', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
abstract class Greeter { abstract greet(): string; }
|
||||
|
||||
class GreeterEN implements Greeter {
|
||||
greet() { return 'Hi'; }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: '<div></div>',
|
||||
providers: [GreeterEN, {provide: Greeter, useClass: GreeterEN}]
|
||||
})
|
||||
export class MyComponent {
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
expectEmit(
|
||||
result.source,
|
||||
'features: [i0.ɵProvidersFeature([GreeterEN, {provide: Greeter, useClass: GreeterEN}])],',
|
||||
'Incorrect features');
|
||||
});
|
||||
|
||||
it('should emit the ProvidersFeature feature when viewProviders only', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
abstract class Greeter { abstract greet(): string; }
|
||||
|
||||
class GreeterEN implements Greeter {
|
||||
greet() { return 'Hi'; }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: '<div></div>',
|
||||
viewProviders: [GreeterEN]
|
||||
})
|
||||
export class MyComponent {
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
expectEmit(
|
||||
result.source, 'features: [i0.ɵProvidersFeature([], [GreeterEN])],', 'Incorrect features');
|
||||
});
|
||||
|
||||
it('should not emit the ProvidersFeature feature when no providers', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
abstract class Greeter { abstract greet(): string; }
|
||||
|
||||
class GreeterEN implements Greeter {
|
||||
greet() { return 'Hi'; }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: '<div></div>'
|
||||
})
|
||||
export class MyComponent {
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
expectEmit(
|
||||
result.source, `
|
||||
export class MyComponent {
|
||||
}
|
||||
MyComponent.ngComponentDef = i0.ɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); }, consts: 1, vars: 0, template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
||||
i0.ɵelement(0, "div");
|
||||
} } });`,
|
||||
'Incorrect features');
|
||||
});
|
||||
});
|
@ -128,7 +128,6 @@ describe('compiler compliance: styling', () => {
|
||||
factory:function MyComponent_Factory(t){
|
||||
return new (t || MyComponent)();
|
||||
},
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf, $ctx$) {
|
||||
@ -170,7 +169,6 @@ describe('compiler compliance: styling', () => {
|
||||
factory:function MyComponent_Factory(t){
|
||||
return new (t || MyComponent)();
|
||||
},
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf, $ctx$) {
|
||||
@ -308,7 +306,6 @@ describe('compiler compliance: styling', () => {
|
||||
factory:function MyComponent_Factory(t){
|
||||
return new (t || MyComponent)();
|
||||
},
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 1,
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf, $ctx$) {
|
||||
@ -367,7 +364,6 @@ describe('compiler compliance: styling', () => {
|
||||
factory: function MyComponent_Factory(t) {
|
||||
return new (t || MyComponent)();
|
||||
},
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
@ -506,7 +502,6 @@ describe('compiler compliance: styling', () => {
|
||||
factory:function MyComponent_Factory(t){
|
||||
return new (t || MyComponent)();
|
||||
},
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 1,
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf, $ctx$) {
|
||||
@ -563,7 +558,6 @@ describe('compiler compliance: styling', () => {
|
||||
factory:function MyComponent_Factory(t){
|
||||
return new (t || MyComponent)();
|
||||
},
|
||||
features: [$r3$.ɵPublicFeature],
|
||||
consts: 1,
|
||||
vars: 2,
|
||||
template: function MyComponent_Template(rf, $ctx$) {
|
||||
@ -625,5 +619,103 @@ describe('compiler compliance: styling', () => {
|
||||
expectEmit(result.source, template, 'Incorrect template');
|
||||
});
|
||||
|
||||
it('should stamp out pipe definitions in the creation block if used by styling bindings',
|
||||
() => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: \`<div [style]="myStyleExp | stylePipe" [class]="myClassExp | classPipe"></div>\`
|
||||
})
|
||||
export class MyComponent {
|
||||
myStyleExp = [{color:'red'}, {color:'blue', duration:1000}]
|
||||
myClassExp = 'foo bar apple';
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const template = `
|
||||
template: function MyComponent_Template(rf, $ctx$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵelementStyling(null, null, $r3$.ɵdefaultStyleSanitizer);
|
||||
$r3$.ɵpipe(1, "classPipe");
|
||||
$r3$.ɵpipe(2, "stylePipe");
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementStylingMap(0, $r3$.ɵpipeBind1(1, 0, $ctx$.myClassExp), $r3$.ɵpipeBind1(2, 2, $ctx$.myStyleExp));
|
||||
$r3$.ɵelementStylingApply(0);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
expectEmit(result.source, template, 'Incorrect template');
|
||||
});
|
||||
|
||||
it('should properly offset multiple style pipe references for styling bindings', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: \`
|
||||
<div [class]="{}"
|
||||
[class.foo]="fooExp | pipe:2000"
|
||||
[style]="myStyleExp | pipe:1000"
|
||||
[style.bar]="barExp | pipe:3000"
|
||||
[style.baz]="bazExp | pipe:4000">
|
||||
{{ item }}</div>\`
|
||||
})
|
||||
export class MyComponent {
|
||||
myStyleExp = {};
|
||||
fooExp = 'foo';
|
||||
barExp = 'bar';
|
||||
bazExp = 'baz';
|
||||
items = [1,2,3];
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const template = `
|
||||
template: function MyComponent_Template(rf, $ctx$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, "div");
|
||||
$r3$.ɵelementStyling($e0_styling$, $e1_styling$, $r3$.ɵdefaultStyleSanitizer);
|
||||
$r3$.ɵpipe(1, "pipe");
|
||||
$r3$.ɵpipe(2, "pipe");
|
||||
$r3$.ɵpipe(3, "pipe");
|
||||
$r3$.ɵpipe(4, "pipe");
|
||||
$r3$.ɵtext(5);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementStylingMap(0, $e2_styling$, $r3$.ɵpipeBind2(1, 1, $ctx$.myStyleExp, 1000));
|
||||
$r3$.ɵelementStyleProp(0, 0, $r3$.ɵpipeBind2(2, 4, $ctx$.barExp, 3000));
|
||||
$r3$.ɵelementStyleProp(0, 1, $r3$.ɵpipeBind2(3, 7, $ctx$.bazExp, 4000));
|
||||
$r3$.ɵelementClassProp(0, 0, $r3$.ɵpipeBind2(4, 10, $ctx$.fooExp, 2000));
|
||||
$r3$.ɵelementStylingApply(0);
|
||||
$r3$.ɵtextBinding(5, $r3$.ɵinterpolation1(" ", $ctx$.item, ""));
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
expectEmit(result.source, template, 'Incorrect template');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -380,7 +380,7 @@ describe('ngtsc behavioral tests', () => {
|
||||
const jsContents = env.getContents('test.js');
|
||||
expect(jsContents)
|
||||
.toContain(
|
||||
`factory: function FooCmp_Factory(t) { return new (t || FooCmp)(i0.ɵinjectAttribute("test"), i0.ɵdirectiveInject(ChangeDetectorRef), i0.ɵdirectiveInject(ElementRef), i0.ɵdirectiveInject(i0.INJECTOR), i0.ɵdirectiveInject(Renderer2), i0.ɵdirectiveInject(TemplateRef), i0.ɵdirectiveInject(ViewContainerRef)); }`);
|
||||
`factory: function FooCmp_Factory(t) { return new (t || FooCmp)(i0.ɵinjectAttribute("test"), i0.ɵdirectiveInject(ChangeDetectorRef), i0.ɵdirectiveInject(ElementRef), i0.ɵdirectiveInject(Injector), i0.ɵdirectiveInject(Renderer2), i0.ɵdirectiveInject(TemplateRef), i0.ɵdirectiveInject(ViewContainerRef)); }`);
|
||||
});
|
||||
|
||||
it('should generate queries for components', () => {
|
||||
@ -469,7 +469,7 @@ describe('ngtsc behavioral tests', () => {
|
||||
@HostBinding('class.someclass')
|
||||
get someClass(): boolean { return false; }
|
||||
|
||||
@HostListener('onChange', ['arg'])
|
||||
@HostListener('change', ['arg1', 'arg2', 'arg3'])
|
||||
onChange(event: any, arg: any): void {}
|
||||
}
|
||||
`);
|
||||
@ -483,8 +483,51 @@ describe('ngtsc behavioral tests', () => {
|
||||
expect(jsContents)
|
||||
.toContain(
|
||||
'i0.ɵelementProperty(elIndex, "class.someclass", i0.ɵbind(i0.ɵload(dirIndex).someClass))');
|
||||
expect(jsContents).toContain('i0.ɵload(dirIndex).onClick($event)');
|
||||
expect(jsContents).toContain('i0.ɵload(dirIndex).onChange(i0.ɵload(dirIndex).arg)');
|
||||
|
||||
const factoryDef = `
|
||||
factory: function FooCmp_Factory(t) {
|
||||
var f = new (t || FooCmp)();
|
||||
i0.ɵlistener("click", function FooCmp_click_HostBindingHandler($event) {
|
||||
return f.onClick($event);
|
||||
});
|
||||
i0.ɵlistener("change", function FooCmp_change_HostBindingHandler($event) {
|
||||
return f.onChange(f.arg1, f.arg2, f.arg3);
|
||||
});
|
||||
return f;
|
||||
}
|
||||
`;
|
||||
expect(jsContents).toContain(factoryDef.replace(/\s+/g, ' ').trim());
|
||||
});
|
||||
|
||||
it('should generate host listeners for directives with base factories', () => {
|
||||
env.tsconfig();
|
||||
env.write(`test.ts`, `
|
||||
import {Directive, HostListener} from '@angular/core';
|
||||
|
||||
class Base {}
|
||||
|
||||
@Directive({
|
||||
selector: '[test]',
|
||||
})
|
||||
class Dir extends Base {
|
||||
@HostListener('change', ['arg'])
|
||||
onChange(event: any, arg: any): void {}
|
||||
}
|
||||
`);
|
||||
|
||||
env.driveMain();
|
||||
const jsContents = env.getContents('test.js');
|
||||
const factoryDef = `
|
||||
factory: function Dir_Factory(t) {
|
||||
var f = ɵDir_BaseFactory((t || Dir));
|
||||
i0.ɵlistener("change", function Dir_change_HostBindingHandler($event) {
|
||||
return f.onChange(f.arg);
|
||||
});
|
||||
return f;
|
||||
}
|
||||
`;
|
||||
expect(jsContents).toContain(factoryDef.replace(/\s+/g, ' ').trim());
|
||||
expect(jsContents).toContain('var ɵDir_BaseFactory = i0.ɵgetInheritedFactory(Dir)');
|
||||
});
|
||||
|
||||
it('should correctly recognize local symbols', () => {
|
||||
@ -572,6 +615,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', `
|
||||
|
@ -56,7 +56,6 @@ export type Provider = any;
|
||||
export enum R3ResolvedDependencyType {
|
||||
Token = 0,
|
||||
Attribute = 1,
|
||||
Injector = 2,
|
||||
}
|
||||
|
||||
export interface R3DependencyMetadataFacade {
|
||||
|
@ -42,6 +42,7 @@ export function compileInjectable(meta: R3InjectableMetadata): InjectableDef {
|
||||
type: meta.type,
|
||||
deps: meta.ctorDeps,
|
||||
injectFn: Identifiers.inject,
|
||||
extraStatementFn: null,
|
||||
};
|
||||
|
||||
if (meta.useClass !== undefined) {
|
||||
|
@ -120,7 +120,7 @@ export class CompilerFacadeImpl implements CompilerFacade {
|
||||
...convertDirectiveFacadeToMetadata(facade),
|
||||
template,
|
||||
viewQueries: facade.viewQueries.map(convertToR3QueryMetadata),
|
||||
wrapDirectivesInClosure: false,
|
||||
wrapDirectivesAndPipesInClosure: false,
|
||||
styles: facade.styles || [],
|
||||
encapsulation: facade.encapsulation as any,
|
||||
animations: facade.animations != null ? new WrappedNodeExpr(facade.animations) : null,
|
||||
|
@ -49,6 +49,11 @@ export interface R3ConstructorFactoryMetadata {
|
||||
* function could be different, and other options control how it will be invoked.
|
||||
*/
|
||||
injectFn: o.ExternalReference;
|
||||
|
||||
/**
|
||||
* Function that allows extra statements to be inserted into factory function.
|
||||
*/
|
||||
extraStatementFn: ((instance: o.Expression) => o.Statement[])|null;
|
||||
}
|
||||
|
||||
export enum R3FactoryDelegateType {
|
||||
@ -95,11 +100,6 @@ export enum R3ResolvedDependencyType {
|
||||
* The token expression is a string representing the attribute name.
|
||||
*/
|
||||
Attribute = 1,
|
||||
|
||||
/**
|
||||
* The dependency is for the `Injector` type itself.
|
||||
*/
|
||||
Injector = 2,
|
||||
}
|
||||
|
||||
/**
|
||||
@ -194,8 +194,7 @@ export function compileFactoryFunction(meta: R3FactoryMetadata):
|
||||
]);
|
||||
|
||||
statements.push(delegateFactoryStmt);
|
||||
const r = makeConditionalFactory(delegateFactory.callFn([]));
|
||||
retExpr = r;
|
||||
retExpr = makeConditionalFactory(delegateFactory.callFn([]));
|
||||
} else if (isDelegatedMetadata(meta)) {
|
||||
// This type is created with a delegated factory. If a type parameter is not specified, call
|
||||
// the factory instead.
|
||||
@ -209,10 +208,22 @@ export function compileFactoryFunction(meta: R3FactoryMetadata):
|
||||
} else if (isExpressionFactoryMetadata(meta)) {
|
||||
// TODO(alxhub): decide whether to lower the value here or in the caller
|
||||
retExpr = makeConditionalFactory(meta.expression);
|
||||
} else if (meta.extraStatementFn) {
|
||||
// if extraStatementsFn is specified and the 'makeConditionalFactory' function
|
||||
// was not invoked, we need to create a reference to the instance, so we can
|
||||
// pass it as an argument to the 'extraStatementFn' function while calling it
|
||||
const variable = o.variable('f');
|
||||
body.push(variable.set(ctorExpr).toDeclStmt());
|
||||
retExpr = variable;
|
||||
} else {
|
||||
retExpr = ctorExpr;
|
||||
}
|
||||
|
||||
if (meta.extraStatementFn) {
|
||||
const extraStmts = meta.extraStatementFn(retExpr);
|
||||
body.push(...extraStmts);
|
||||
}
|
||||
|
||||
return {
|
||||
factory: o.fn(
|
||||
[new o.FnParam('t', o.DYNAMIC_TYPE)], [...body, new o.ReturnStatement(retExpr)],
|
||||
@ -230,22 +241,14 @@ function compileInjectDependency(
|
||||
dep: R3DependencyMetadata, injectFn: o.ExternalReference): o.Expression {
|
||||
// Interpret the dependency according to its resolved type.
|
||||
switch (dep.resolved) {
|
||||
case R3ResolvedDependencyType.Token:
|
||||
case R3ResolvedDependencyType.Injector: {
|
||||
case R3ResolvedDependencyType.Token: {
|
||||
// Build up the injection flags according to the metadata.
|
||||
const flags = InjectFlags.Default | (dep.self ? InjectFlags.Self : 0) |
|
||||
(dep.skipSelf ? InjectFlags.SkipSelf : 0) | (dep.host ? InjectFlags.Host : 0) |
|
||||
(dep.optional ? InjectFlags.Optional : 0);
|
||||
// Determine the token used for injection. In almost all cases this is the given token, but
|
||||
// if the dependency is resolved to the `Injector` then the special `INJECTOR` token is used
|
||||
// instead.
|
||||
let token: o.Expression = dep.token;
|
||||
if (dep.resolved === R3ResolvedDependencyType.Injector) {
|
||||
token = o.importExpr(Identifiers.INJECTOR);
|
||||
}
|
||||
|
||||
// Build up the arguments to the injectFn call.
|
||||
const injectArgs = [token];
|
||||
const injectArgs = [dep.token];
|
||||
// If this dependency is optional or otherwise has non-default flags, then additional
|
||||
// parameters describing how to inject the dependency must be passed to the inject function
|
||||
// that's being used.
|
||||
@ -280,12 +283,9 @@ export function dependenciesFromGlobalMetadata(
|
||||
for (let dependency of type.diDeps) {
|
||||
if (dependency.token) {
|
||||
const tokenRef = tokenReference(dependency.token);
|
||||
let resolved: R3ResolvedDependencyType = R3ResolvedDependencyType.Token;
|
||||
if (tokenRef === injectorRef) {
|
||||
resolved = R3ResolvedDependencyType.Injector;
|
||||
} else if (dependency.isAttribute) {
|
||||
resolved = R3ResolvedDependencyType.Attribute;
|
||||
}
|
||||
let resolved: R3ResolvedDependencyType = dependency.isAttribute ?
|
||||
R3ResolvedDependencyType.Attribute :
|
||||
R3ResolvedDependencyType.Token;
|
||||
|
||||
// In the case of most dependencies, the token will be a reference to a type. Sometimes,
|
||||
// however, it can be a string, in the case of older Angular code or @Attribute injection.
|
||||
|
@ -175,7 +175,7 @@ export class Identifiers {
|
||||
static InheritDefinitionFeature:
|
||||
o.ExternalReference = {name: 'ɵInheritDefinitionFeature', moduleName: CORE};
|
||||
|
||||
static PublicFeature: o.ExternalReference = {name: 'ɵPublicFeature', moduleName: CORE};
|
||||
static ProvidersFeature: o.ExternalReference = {name: 'ɵProvidersFeature', moduleName: CORE};
|
||||
|
||||
static listener: o.ExternalReference = {name: 'ɵlistener', moduleName: CORE};
|
||||
|
||||
|
@ -101,6 +101,7 @@ export function compileInjector(meta: R3InjectorMetadata): R3InjectorDef {
|
||||
type: meta.type,
|
||||
deps: meta.deps,
|
||||
injectFn: R3.inject,
|
||||
extraStatementFn: null,
|
||||
});
|
||||
const expression = o.importExpr(R3.defineInjector).callFn([mapToMapExpression({
|
||||
factory: result.factory,
|
||||
@ -151,4 +152,4 @@ function accessExportScope(module: o.Expression): o.Expression {
|
||||
function tupleTypeOf(exp: R3Reference[]): o.Type {
|
||||
const types = exp.map(ref => o.typeofExpr(ref.type));
|
||||
return exp.length > 0 ? o.expressionType(o.literalArr(types)) : o.NONE_TYPE;
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ export function compilePipeFromMetadata(metadata: R3PipeMetadata) {
|
||||
type: metadata.type,
|
||||
deps: metadata.deps,
|
||||
injectFn: R3.directiveInject,
|
||||
extraStatementFn: null,
|
||||
});
|
||||
definitionMapValues.push({key: 'factory', value: templateFactory.factory, quoted: false});
|
||||
|
||||
|
@ -159,11 +159,11 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata {
|
||||
directives: Map<string, o.Expression>;
|
||||
|
||||
/**
|
||||
* Whether to wrap the 'directives' array, if one is generated, in a closure.
|
||||
* Whether to wrap the 'directives' and/or `pipes` array, if one is generated, in a closure.
|
||||
*
|
||||
* This is done when the directives contain forward references.
|
||||
* This is done when the directives or pipes contain forward references.
|
||||
*/
|
||||
wrapDirectivesInClosure: boolean;
|
||||
wrapDirectivesAndPipesInClosure: boolean;
|
||||
|
||||
/**
|
||||
* A collection of styling data that will be applied and scoped to the component.
|
||||
@ -217,8 +217,8 @@ export interface R3QueryMetadata {
|
||||
descendants: boolean;
|
||||
|
||||
/**
|
||||
* An expression representing a type to read from each matched node, or null if the node itself
|
||||
* is to be returned.
|
||||
* An expression representing a type to read from each matched node, or null if the default value
|
||||
* for a given node is to be returned.
|
||||
*/
|
||||
read: o.Expression|null;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import {CompileReflector} from '../../compile_reflector';
|
||||
import {BindingForm, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter';
|
||||
import {ConstantPool, DefinitionKind} from '../../constant_pool';
|
||||
import * as core from '../../core';
|
||||
import {ParsedEvent} from '../../expression_parser/ast';
|
||||
import {LifecycleHooks} from '../../lifecycle_reflector';
|
||||
import * as o from '../../output/output_ast';
|
||||
import {typeSourceSpan} from '../../parse_util';
|
||||
@ -49,6 +50,7 @@ function baseDirectiveFields(
|
||||
type: meta.type,
|
||||
deps: meta.deps,
|
||||
injectFn: R3.directiveInject,
|
||||
extraStatementFn: createFactoryExtraStatementsFn(meta, bindingParser)
|
||||
});
|
||||
definitionMap.set('factory', result.factory);
|
||||
|
||||
@ -82,11 +84,30 @@ function baseDirectiveFields(
|
||||
// e.g 'outputs: {a: 'a'}`
|
||||
definitionMap.set('outputs', conditionallyCreateMapObjectLiteral(meta.outputs));
|
||||
|
||||
if (meta.exportAs !== null) {
|
||||
definitionMap.set('exportAs', o.literal(meta.exportAs));
|
||||
}
|
||||
|
||||
return {definitionMap, statements: result.statements};
|
||||
}
|
||||
|
||||
/**
|
||||
* Add features to the definition map.
|
||||
*/
|
||||
function addFeatures(
|
||||
definitionMap: DefinitionMap, meta: R3DirectiveMetadata | R3ComponentMetadata) {
|
||||
// e.g. `features: [NgOnChangesFeature]`
|
||||
const features: o.Expression[] = [];
|
||||
|
||||
// TODO: add `PublicFeature` so that directives get registered to the DI - make this configurable
|
||||
features.push(o.importExpr(R3.PublicFeature));
|
||||
const providers = meta.providers;
|
||||
const viewProviders = (meta as R3ComponentMetadata).viewProviders;
|
||||
if (providers || viewProviders) {
|
||||
const args = [providers || new o.LiteralArrayExpr([])];
|
||||
if (viewProviders) {
|
||||
args.push(viewProviders);
|
||||
}
|
||||
features.push(o.importExpr(R3.ProvidersFeature).callFn(args));
|
||||
}
|
||||
|
||||
if (meta.usesInheritance) {
|
||||
features.push(o.importExpr(R3.InheritDefinitionFeature));
|
||||
@ -97,11 +118,6 @@ function baseDirectiveFields(
|
||||
if (features.length) {
|
||||
definitionMap.set('features', o.literalArr(features));
|
||||
}
|
||||
if (meta.exportAs !== null) {
|
||||
definitionMap.set('exportAs', o.literal(meta.exportAs));
|
||||
}
|
||||
|
||||
return {definitionMap, statements: result.statements};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,6 +127,7 @@ export function compileDirectiveFromMetadata(
|
||||
meta: R3DirectiveMetadata, constantPool: ConstantPool,
|
||||
bindingParser: BindingParser): R3DirectiveDef {
|
||||
const {definitionMap, statements} = baseDirectiveFields(meta, constantPool, bindingParser);
|
||||
addFeatures(definitionMap, meta);
|
||||
const expression = o.importExpr(R3.defineDirective).callFn([definitionMap.toLiteralMap()]);
|
||||
|
||||
// On the type side, remove newlines from the selector as it will need to fit into a TypeScript
|
||||
@ -164,6 +181,7 @@ export function compileComponentFromMetadata(
|
||||
meta: R3ComponentMetadata, constantPool: ConstantPool,
|
||||
bindingParser: BindingParser): R3ComponentDef {
|
||||
const {definitionMap, statements} = baseDirectiveFields(meta, constantPool, bindingParser);
|
||||
addFeatures(definitionMap, meta);
|
||||
|
||||
const selector = meta.selector && CssSelector.parse(meta.selector);
|
||||
const firstSelector = selector && selector[0];
|
||||
@ -223,7 +241,7 @@ export function compileComponentFromMetadata(
|
||||
// e.g. `directives: [MyDirective]`
|
||||
if (directivesUsed.size) {
|
||||
let directivesExpr: o.Expression = o.literalArr(Array.from(directivesUsed));
|
||||
if (meta.wrapDirectivesInClosure) {
|
||||
if (meta.wrapDirectivesAndPipesInClosure) {
|
||||
directivesExpr = o.fn([], [new o.ReturnStatement(directivesExpr)]);
|
||||
}
|
||||
definitionMap.set('directives', directivesExpr);
|
||||
@ -231,7 +249,11 @@ export function compileComponentFromMetadata(
|
||||
|
||||
// e.g. `pipes: [MyPipe]`
|
||||
if (pipesUsed.size) {
|
||||
definitionMap.set('pipes', o.literalArr(Array.from(pipesUsed)));
|
||||
let pipesExpr: o.Expression = o.literalArr(Array.from(pipesUsed));
|
||||
if (meta.wrapDirectivesAndPipesInClosure) {
|
||||
pipesExpr = o.fn([], [new o.ReturnStatement(pipesExpr)]);
|
||||
}
|
||||
definitionMap.set('pipes', pipesExpr);
|
||||
}
|
||||
|
||||
// e.g. `styles: [str1, str2]`
|
||||
@ -315,12 +337,13 @@ export function compileComponentFromRender2(
|
||||
directives: typeMapToExpressionMap(directiveTypeBySel, outputCtx),
|
||||
pipes: typeMapToExpressionMap(pipeTypeByName, outputCtx),
|
||||
viewQueries: queriesFromGlobalMetadata(component.viewQueries, outputCtx),
|
||||
wrapDirectivesInClosure: false,
|
||||
wrapDirectivesAndPipesInClosure: false,
|
||||
styles: (summary.template && summary.template.styles) || EMPTY_ARRAY,
|
||||
encapsulation:
|
||||
(summary.template && summary.template.encapsulation) || core.ViewEncapsulation.Emulated,
|
||||
animations: null,
|
||||
viewProviders: null,
|
||||
viewProviders:
|
||||
component.viewProviders.length > 0 ? new o.WrappedNodeExpr(component.viewProviders) : null
|
||||
};
|
||||
const res = compileComponentFromMetadata(meta, outputCtx.constantPool, bindingParser);
|
||||
|
||||
@ -363,7 +386,7 @@ function directiveMetadataFromGlobalMetadata(
|
||||
outputs: directive.outputs,
|
||||
usesInheritance: false,
|
||||
exportAs: null,
|
||||
providers: null,
|
||||
providers: directive.providers.length > 0 ? new o.WrappedNodeExpr(directive.providers) : null
|
||||
};
|
||||
}
|
||||
|
||||
@ -453,11 +476,15 @@ function createContentQueriesFunction(
|
||||
if (meta.queries.length) {
|
||||
const statements: o.Statement[] = meta.queries.map((query: R3QueryMetadata) => {
|
||||
const queryDefinition = createQueryDefinition(query, constantPool, null);
|
||||
return o.importExpr(R3.registerContentQuery).callFn([queryDefinition]).toStmt();
|
||||
return o.importExpr(R3.registerContentQuery)
|
||||
.callFn([queryDefinition, o.variable('dirIndex')])
|
||||
.toStmt();
|
||||
});
|
||||
const typeName = meta.name;
|
||||
const parameters = [new o.FnParam('dirIndex', o.NUMBER_TYPE)];
|
||||
return o.fn(
|
||||
[], statements, o.INFERRED_TYPE, null, typeName ? `${typeName}_ContentQueries` : null);
|
||||
parameters, statements, o.INFERRED_TYPE, null,
|
||||
typeName ? `${typeName}_ContentQueries` : null);
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -507,12 +534,15 @@ function stringAsType(str: string): o.Type {
|
||||
return o.expressionType(o.literal(str));
|
||||
}
|
||||
|
||||
function stringMapAsType(map: {[key: string]: string | [string, string]}): o.Type {
|
||||
const mapValues = Object.keys(map).map(key => ({
|
||||
key,
|
||||
value: o.literal(map[key]),
|
||||
quoted: true,
|
||||
}));
|
||||
function stringMapAsType(map: {[key: string]: string | string[]}): o.Type {
|
||||
const mapValues = Object.keys(map).map(key => {
|
||||
const value = Array.isArray(map[key]) ? map[key][0] : map[key];
|
||||
return {
|
||||
key,
|
||||
value: o.literal(value),
|
||||
quoted: true,
|
||||
};
|
||||
});
|
||||
return o.expressionType(o.literalMap(mapValues));
|
||||
}
|
||||
|
||||
@ -606,26 +636,6 @@ function createHostBindingsFunction(
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate host event bindings
|
||||
const eventBindings =
|
||||
bindingParser.createDirectiveHostEventAsts(directiveSummary, hostBindingSourceSpan);
|
||||
if (eventBindings) {
|
||||
for (const binding of eventBindings) {
|
||||
const bindingExpr = convertActionBinding(
|
||||
null, bindingContext, binding.handler, 'b', () => error('Unexpected interpolation'));
|
||||
const bindingName = binding.name && sanitizeIdentifier(binding.name);
|
||||
const typeName = meta.name;
|
||||
const functionName =
|
||||
typeName && bindingName ? `${typeName}_${bindingName}_HostBindingHandler` : null;
|
||||
const handler = o.fn(
|
||||
[new o.FnParam('$event', o.DYNAMIC_TYPE)],
|
||||
[...bindingExpr.stmts, new o.ReturnStatement(bindingExpr.allowDefault)], o.INFERRED_TYPE,
|
||||
null, functionName);
|
||||
statements.push(
|
||||
o.importExpr(R3.listener).callFn([o.literal(binding.name), handler]).toStmt());
|
||||
}
|
||||
}
|
||||
|
||||
if (statements.length > 0) {
|
||||
const typeName = meta.name;
|
||||
return o.fn(
|
||||
@ -639,6 +649,32 @@ function createHostBindingsFunction(
|
||||
return null;
|
||||
}
|
||||
|
||||
function createFactoryExtraStatementsFn(meta: R3DirectiveMetadata, bindingParser: BindingParser):
|
||||
((instance: o.Expression) => o.Statement[])|null {
|
||||
const eventBindings =
|
||||
bindingParser.createDirectiveHostEventAsts(metadataAsSummary(meta), meta.typeSourceSpan);
|
||||
return eventBindings && eventBindings.length ?
|
||||
(instance: o.Expression) => createHostListeners(instance, eventBindings, meta) :
|
||||
null;
|
||||
}
|
||||
|
||||
function createHostListeners(
|
||||
bindingContext: o.Expression, eventBindings: ParsedEvent[],
|
||||
meta: R3DirectiveMetadata): o.Statement[] {
|
||||
return eventBindings.map(binding => {
|
||||
const bindingExpr = convertActionBinding(
|
||||
null, bindingContext, binding.handler, 'b', () => error('Unexpected interpolation'));
|
||||
const bindingName = binding.name && sanitizeIdentifier(binding.name);
|
||||
const typeName = meta.name;
|
||||
const functionName =
|
||||
typeName && bindingName ? `${typeName}_${bindingName}_HostBindingHandler` : null;
|
||||
const handler = o.fn(
|
||||
[new o.FnParam('$event', o.DYNAMIC_TYPE)], [...bindingExpr.render3Stmts], o.INFERRED_TYPE,
|
||||
null, functionName);
|
||||
return o.importExpr(R3.listener).callFn([o.literal(binding.name), handler]).toStmt();
|
||||
});
|
||||
}
|
||||
|
||||
function metadataAsSummary(meta: R3DirectiveMetadata): CompileDirectiveSummary {
|
||||
// clang-format off
|
||||
return {
|
||||
|
@ -643,18 +643,23 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
|
||||
const stylingInput = mapBasedStyleInput || mapBasedClassInput;
|
||||
if (stylingInput) {
|
||||
// these values must be outside of the update block so that they can
|
||||
// be evaluted (the AST visit call) during creation time so that any
|
||||
// pipes can be picked up in time before the template is built
|
||||
const mapBasedClassValue =
|
||||
mapBasedClassInput ? mapBasedClassInput.value.visit(this._valueConverter) : null;
|
||||
const mapBasedStyleValue =
|
||||
mapBasedStyleInput ? mapBasedStyleInput.value.visit(this._valueConverter) : null;
|
||||
this.updateInstruction(stylingInput.sourceSpan, R3.elementStylingMap, () => {
|
||||
const params: o.Expression[] = [indexLiteral];
|
||||
|
||||
if (mapBasedClassInput) {
|
||||
const mapBasedClassValue = mapBasedClassInput.value.visit(this._valueConverter);
|
||||
if (mapBasedClassValue) {
|
||||
params.push(this.convertPropertyBinding(implicit, mapBasedClassValue, true));
|
||||
} else if (mapBasedStyleInput) {
|
||||
params.push(o.NULL_EXPR);
|
||||
}
|
||||
|
||||
if (mapBasedStyleInput) {
|
||||
const mapBasedStyleValue = mapBasedStyleInput.value.visit(this._valueConverter);
|
||||
if (mapBasedStyleValue) {
|
||||
params.push(this.convertPropertyBinding(implicit, mapBasedStyleValue, true));
|
||||
}
|
||||
|
||||
@ -670,15 +675,18 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
const key = input.name;
|
||||
const styleIndex: number = stylesIndexMap[key] !;
|
||||
const value = input.value.visit(this._valueConverter);
|
||||
const params: o.Expression[] = [
|
||||
indexLiteral, o.literal(styleIndex), this.convertPropertyBinding(implicit, value, true)
|
||||
];
|
||||
this.updateInstruction(input.sourceSpan, R3.elementStyleProp, () => {
|
||||
const params: o.Expression[] = [
|
||||
indexLiteral, o.literal(styleIndex),
|
||||
this.convertPropertyBinding(implicit, value, true)
|
||||
];
|
||||
|
||||
if (input.unit != null) {
|
||||
params.push(o.literal(input.unit));
|
||||
}
|
||||
if (input.unit != null) {
|
||||
params.push(o.literal(input.unit));
|
||||
}
|
||||
|
||||
this.updateInstruction(input.sourceSpan, R3.elementStyleProp, params);
|
||||
return params;
|
||||
});
|
||||
}
|
||||
|
||||
lastInputCommand = styleInputs[styleInputs.length - 1];
|
||||
@ -696,10 +704,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
const classIndex: number = classesIndexMap[key] !;
|
||||
const value = input.value.visit(this._valueConverter);
|
||||
this.updateInstruction(input.sourceSpan, R3.elementClassProp, () => {
|
||||
return [
|
||||
indexLiteral, o.literal(classIndex),
|
||||
this.convertPropertyBinding(implicit, value, true), ...params
|
||||
];
|
||||
const valueLiteral = this.convertPropertyBinding(implicit, value, true);
|
||||
return [indexLiteral, o.literal(classIndex), valueLiteral];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -67,8 +67,8 @@ export function asLiteral(value: any): o.Expression {
|
||||
return o.literal(value, o.INFERRED_TYPE);
|
||||
}
|
||||
|
||||
export function conditionallyCreateMapObjectLiteral(
|
||||
keys: {[key: string]: string | [string, string]}): o.Expression|null {
|
||||
export function conditionallyCreateMapObjectLiteral(keys: {[key: string]: string | string[]}):
|
||||
o.Expression|null {
|
||||
if (Object.getOwnPropertyNames(keys).length > 0) {
|
||||
return mapToExpression(keys);
|
||||
}
|
||||
|
@ -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",
|
||||
@ -41,30 +34,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__;
|
||||
|
@ -14,9 +14,9 @@ export {isListLikeIterable as ɵisListLikeIterable} from './change_detection/cha
|
||||
export {ChangeDetectorStatus as ɵChangeDetectorStatus, isDefaultChangeDetectionStrategy as ɵisDefaultChangeDetectionStrategy} from './change_detection/constants';
|
||||
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 {inject as ɵinject, setCurrentInjector as ɵsetCurrentInjector} from './di/injector_compatibility';
|
||||
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';
|
||||
|
@ -25,7 +25,7 @@ export {
|
||||
getFactoryOf as ɵgetFactoryOf,
|
||||
getInheritedFactory as ɵgetInheritedFactory,
|
||||
templateRefExtractor as ɵtemplateRefExtractor,
|
||||
PublicFeature as ɵPublicFeature,
|
||||
ProvidersFeature as ɵProvidersResolver,
|
||||
InheritDefinitionFeature as ɵInheritDefinitionFeature,
|
||||
NgOnChangesFeature as ɵNgOnChangesFeature,
|
||||
NgModuleType as ɵNgModuleType,
|
||||
@ -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,43 @@ 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 {
|
||||
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';
|
||||
|
||||
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';
|
||||
|
||||
publishGlobalUtil as ɵpublishGlobalUtil
|
||||
} from './render3/publish_global_util';
|
||||
export {
|
||||
SWITCH_INJECTOR_FACTORY__POST_R3__ as ɵSWITCH_INJECTOR_FACTORY__POST_R3__,
|
||||
} from './di/injector';
|
||||
|
||||
// clang-format on
|
||||
|
@ -16,7 +16,8 @@ export * from './di/metadata';
|
||||
export {InjectableType, InjectorType, defineInjectable, defineInjector} from './di/defs';
|
||||
export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';
|
||||
export {Injectable, InjectableDecorator, InjectableProvider} from './di/injectable';
|
||||
export {inject, InjectFlags, INJECTOR, Injector} from './di/injector';
|
||||
export {INJECTOR, Injector} from './di/injector';
|
||||
export {inject, InjectFlags} from './di/injector_compatibility';
|
||||
export {ReflectiveInjector} from './di/reflective_injector';
|
||||
export {StaticProvider, ValueProvider, ConstructorSansProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ClassProvider} from './di/provider';
|
||||
export {createInjector} from './di/r3_injector';
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import {Type} from '../type';
|
||||
import {stringify} from '../util';
|
||||
import {getClosureSafeProperty} from '../util/property';
|
||||
|
||||
|
||||
|
||||
@ -22,6 +23,8 @@ import {stringify} from '../util';
|
||||
*/
|
||||
export interface ForwardRefFn { (): any; }
|
||||
|
||||
const __forward_ref__ = getClosureSafeProperty({__forward_ref__: getClosureSafeProperty});
|
||||
|
||||
/**
|
||||
* Allows to refer to references which are not yet defined.
|
||||
*
|
||||
@ -53,10 +56,11 @@ export function forwardRef(forwardRefFn: ForwardRefFn): Type<any> {
|
||||
* @see `forwardRef`
|
||||
* @publicApi
|
||||
*/
|
||||
export function resolveForwardRef(type: any): any {
|
||||
if (typeof type === 'function' && type.hasOwnProperty('__forward_ref__') &&
|
||||
type.__forward_ref__ === forwardRef) {
|
||||
return (<ForwardRefFn>type)();
|
||||
export function resolveForwardRef<T>(type: T): T {
|
||||
const fn: any = type;
|
||||
if (typeof fn === 'function' && fn.hasOwnProperty(__forward_ref__) &&
|
||||
fn.__forward_ref__ === forwardRef) {
|
||||
return fn();
|
||||
} else {
|
||||
return type;
|
||||
}
|
||||
|
@ -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__;
|
||||
|
@ -6,13 +6,16 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {injectInjector} from '../render3/di';
|
||||
import {Type} from '../type';
|
||||
import {stringify} from '../util';
|
||||
import {noop} from '../util/noop';
|
||||
import {getClosureSafeProperty} from '../util/property';
|
||||
|
||||
import {InjectableDef, defineInjectable, getInjectableDef} from './defs';
|
||||
import {defineInjectable} from './defs';
|
||||
import {resolveForwardRef} from './forward_ref';
|
||||
import {InjectionToken} from './injection_token';
|
||||
import {InjectFlags, inject} from './injector_compatibility';
|
||||
import {Inject, Optional, Self, SkipSelf} from './metadata';
|
||||
import {ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, StaticProvider, ValueProvider} from './provider';
|
||||
|
||||
@ -104,8 +107,16 @@ export abstract class Injector {
|
||||
providedIn: 'any' as any,
|
||||
factory: () => inject(INJECTOR),
|
||||
});
|
||||
|
||||
/** @internal */
|
||||
static __NG_ELEMENT_ID__: () => Injector = () => SWITCH_INJECTOR_FACTORY();
|
||||
}
|
||||
|
||||
export const SWITCH_INJECTOR_FACTORY__POST_R3__ = function() {
|
||||
return injectInjector();
|
||||
};
|
||||
const SWITCH_INJECTOR_FACTORY__PRE_R3__ = noop;
|
||||
const SWITCH_INJECTOR_FACTORY: typeof injectInjector = SWITCH_INJECTOR_FACTORY__PRE_R3__;
|
||||
|
||||
|
||||
const IDENT = function<T>(value: T): T {
|
||||
@ -336,7 +347,6 @@ function resolveToken(
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
function computeDeps(provider: StaticProvider): DependencyRecord[] {
|
||||
let deps: DependencyRecord[] = EMPTY;
|
||||
const providerDeps: any[] =
|
||||
@ -396,107 +406,3 @@ function formatError(text: string, obj: any, source: string | null = null): stri
|
||||
function staticError(text: string, obj: any): Error {
|
||||
return new Error(formatError(text, obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection flags for DI.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export const enum InjectFlags {
|
||||
Default = 0b0000,
|
||||
|
||||
/**
|
||||
* Specifies that an injector should retrieve a dependency from any injector until reaching the
|
||||
* host element of the current component. (Only used with Element Injector)
|
||||
*/
|
||||
Host = 0b0001,
|
||||
/** Don't descend into ancestors of the node requesting injection. */
|
||||
Self = 0b0010,
|
||||
/** Skip the node that is requesting injection. */
|
||||
SkipSelf = 0b0100,
|
||||
/** Inject `defaultValue` instead if token not found. */
|
||||
Optional = 0b1000,
|
||||
}
|
||||
|
||||
/**
|
||||
* Current injector value used by `inject`.
|
||||
* - `undefined`: it is an error to call `inject`
|
||||
* - `null`: `inject` can be called but there is no injector (limp-mode).
|
||||
* - Injector instance: Use the injector for resolution.
|
||||
*/
|
||||
let _currentInjector: Injector|undefined|null = undefined;
|
||||
|
||||
export function setCurrentInjector(injector: Injector | null | undefined): Injector|undefined|null {
|
||||
const former = _currentInjector;
|
||||
_currentInjector = injector;
|
||||
return former;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects a token from the currently active injector.
|
||||
*
|
||||
* This function must be used in the context of a factory function such as one defined for an
|
||||
* `InjectionToken`, and will throw an error if not called from such a context.
|
||||
*
|
||||
* @usageNotes
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/injector_spec.ts region='ShakeableInjectionToken'}
|
||||
*
|
||||
* Within such a factory function `inject` is utilized to request injection of a dependency, instead
|
||||
* of providing an additional array of dependencies as was common to do with `useFactory` providers.
|
||||
* `inject` is faster and more type-safe.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function inject<T>(token: Type<T>| InjectionToken<T>): T;
|
||||
export function inject<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags): T|null;
|
||||
export function inject<T>(token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null {
|
||||
if (_currentInjector === undefined) {
|
||||
throw new Error(`inject() must be called from an injection context`);
|
||||
} else if (_currentInjector === null) {
|
||||
const injectableDef: InjectableDef<T>|null = getInjectableDef(token);
|
||||
if (injectableDef && injectableDef.providedIn == 'root') {
|
||||
return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
|
||||
injectableDef.value;
|
||||
}
|
||||
if (flags & InjectFlags.Optional) return null;
|
||||
throw new Error(`Injector: NOT_FOUND [${stringify(token)}]`);
|
||||
} else {
|
||||
return _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
|
||||
}
|
||||
}
|
||||
|
||||
export function injectArgs(types: (Type<any>| InjectionToken<any>| any[])[]): any[] {
|
||||
const args: any[] = [];
|
||||
for (let i = 0; i < types.length; i++) {
|
||||
const arg = types[i];
|
||||
if (Array.isArray(arg)) {
|
||||
if (arg.length === 0) {
|
||||
throw new Error('Arguments array must have arguments.');
|
||||
}
|
||||
let type: Type<any>|undefined = undefined;
|
||||
let flags: InjectFlags = InjectFlags.Default;
|
||||
|
||||
for (let j = 0; j < arg.length; j++) {
|
||||
const meta = arg[j];
|
||||
if (meta instanceof Optional || meta.ngMetadataName === 'Optional') {
|
||||
flags |= InjectFlags.Optional;
|
||||
} else if (meta instanceof SkipSelf || meta.ngMetadataName === 'SkipSelf') {
|
||||
flags |= InjectFlags.SkipSelf;
|
||||
} else if (meta instanceof Self || meta.ngMetadataName === 'Self') {
|
||||
flags |= InjectFlags.Self;
|
||||
} else if (meta instanceof Inject) {
|
||||
type = meta.token;
|
||||
} else {
|
||||
type = meta;
|
||||
}
|
||||
}
|
||||
|
||||
args.push(inject(type !, flags));
|
||||
} else {
|
||||
args.push(inject(arg));
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
165
packages/core/src/di/injector_compatibility.ts
Normal file
165
packages/core/src/di/injector_compatibility.ts
Normal file
@ -0,0 +1,165 @@
|
||||
/**
|
||||
* @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 {Type} from '../type';
|
||||
import {stringify} from '../util';
|
||||
|
||||
import {InjectableDef, getInjectableDef} from './defs';
|
||||
import {InjectionToken} from './injection_token';
|
||||
import {Injector} from './injector';
|
||||
import {Inject, Optional, Self, SkipSelf} from './metadata';
|
||||
|
||||
/**
|
||||
* Injection flags for DI.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export const enum InjectFlags {
|
||||
Default = 0b0000,
|
||||
|
||||
/**
|
||||
* Specifies that an injector should retrieve a dependency from any injector until reaching the
|
||||
* host element of the current component. (Only used with Element Injector)
|
||||
*/
|
||||
Host = 0b0001,
|
||||
/** Don't descend into ancestors of the node requesting injection. */
|
||||
Self = 0b0010,
|
||||
/** Skip the node that is requesting injection. */
|
||||
SkipSelf = 0b0100,
|
||||
/** Inject `defaultValue` instead if token not found. */
|
||||
Optional = 0b1000,
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Current injector value used by `inject`.
|
||||
* - `undefined`: it is an error to call `inject`
|
||||
* - `null`: `inject` can be called but there is no injector (limp-mode).
|
||||
* - Injector instance: Use the injector for resolution.
|
||||
*/
|
||||
let _currentInjector: Injector|undefined|null = undefined;
|
||||
|
||||
export function setCurrentInjector(injector: Injector | null | undefined): Injector|undefined|null {
|
||||
const former = _currentInjector;
|
||||
_currentInjector = injector;
|
||||
return former;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current implementation of inject.
|
||||
*
|
||||
* By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed
|
||||
* to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this
|
||||
* way for two reasons:
|
||||
* 1. `Injector` should not depend on ivy logic.
|
||||
* 2. To maintain tree shake-ability we don't want to bring in unnecessary code.
|
||||
*/
|
||||
let _injectImplementation: (<T>(token: Type<T>| InjectionToken<T>, flags: InjectFlags) => T | null)|
|
||||
undefined;
|
||||
|
||||
/**
|
||||
* Sets the current inject implementation.
|
||||
*/
|
||||
export function setInjectImplementation(
|
||||
impl: (<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags) => T | null) | undefined):
|
||||
(<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags) => T | null)|undefined {
|
||||
const previous = _injectImplementation;
|
||||
_injectImplementation = impl;
|
||||
return previous;
|
||||
}
|
||||
|
||||
export function injectInjectorOnly<T>(token: Type<T>| InjectionToken<T>): T;
|
||||
export function injectInjectorOnly<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags): T|
|
||||
null;
|
||||
export function injectInjectorOnly<T>(
|
||||
token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null {
|
||||
if (_currentInjector === undefined) {
|
||||
throw new Error(`inject() must be called from an injection context`);
|
||||
} else if (_currentInjector === null) {
|
||||
return injectRootLimpMode(token, undefined, flags);
|
||||
} else {
|
||||
return _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects a token from the currently active injector.
|
||||
*
|
||||
* This function must be used in the context of a factory function such as one defined for an
|
||||
* `InjectionToken`, and will throw an error if not called from such a context.
|
||||
*
|
||||
* @usageNotes
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/injector_spec.ts region='ShakeableInjectionToken'}
|
||||
*
|
||||
* Within such a factory function `inject` is utilized to request injection of a dependency, instead
|
||||
* of providing an additional array of dependencies as was common to do with `useFactory` providers.
|
||||
* `inject` is faster and more type-safe.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function inject<T>(token: Type<T>| InjectionToken<T>): T;
|
||||
export function inject<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags): T|null;
|
||||
export function inject<T>(token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null {
|
||||
return (_injectImplementation || injectInjectorOnly)(token, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects `root` tokens in limp mode.
|
||||
*
|
||||
* If no injector exists, we can still inject tree-shakable providers which have `providedIn` set to
|
||||
* `"root"`. This is known as the limp mode injection. In such case the value is stored in the
|
||||
* `InjectableDef`.
|
||||
*/
|
||||
export function injectRootLimpMode<T>(
|
||||
token: Type<T>| InjectionToken<T>, notFoundValue: T | undefined, flags: InjectFlags): T|null {
|
||||
const injectableDef: InjectableDef<T>|null = getInjectableDef(token);
|
||||
if (injectableDef && injectableDef.providedIn == 'root') {
|
||||
return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
|
||||
injectableDef.value;
|
||||
}
|
||||
if (flags & InjectFlags.Optional) return null;
|
||||
if (notFoundValue !== undefined) return notFoundValue;
|
||||
throw new Error(`Injector: NOT_FOUND [${stringify(token)}]`);
|
||||
}
|
||||
|
||||
export function injectArgs(types: (Type<any>| InjectionToken<any>| any[])[]): any[] {
|
||||
const args: any[] = [];
|
||||
for (let i = 0; i < types.length; i++) {
|
||||
const arg = types[i];
|
||||
if (Array.isArray(arg)) {
|
||||
if (arg.length === 0) {
|
||||
throw new Error('Arguments array must have arguments.');
|
||||
}
|
||||
let type: Type<any>|undefined = undefined;
|
||||
let flags: InjectFlags = InjectFlags.Default;
|
||||
|
||||
for (let j = 0; j < arg.length; j++) {
|
||||
const meta = arg[j];
|
||||
if (meta instanceof Optional || meta.ngMetadataName === 'Optional') {
|
||||
flags |= InjectFlags.Optional;
|
||||
} else if (meta instanceof SkipSelf || meta.ngMetadataName === 'SkipSelf') {
|
||||
flags |= InjectFlags.SkipSelf;
|
||||
} else if (meta instanceof Self || meta.ngMetadataName === 'Self') {
|
||||
flags |= InjectFlags.Self;
|
||||
} else if (meta instanceof Inject) {
|
||||
type = meta.token;
|
||||
} else {
|
||||
type = meta;
|
||||
}
|
||||
}
|
||||
|
||||
args.push(inject(type !, flags));
|
||||
} else {
|
||||
args.push(inject(arg));
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
@ -13,7 +13,8 @@ import {stringify} from '../util';
|
||||
import {InjectableDef, InjectableType, InjectorType, InjectorTypeWithProviders, getInjectableDef, getInjectorDef} from './defs';
|
||||
import {resolveForwardRef} from './forward_ref';
|
||||
import {InjectionToken} from './injection_token';
|
||||
import {INJECTOR, InjectFlags, Injector, NullInjector, THROW_IF_NOT_FOUND, USE_VALUE, inject, injectArgs, setCurrentInjector} from './injector';
|
||||
import {INJECTOR, Injector, NullInjector, THROW_IF_NOT_FOUND, USE_VALUE} from './injector';
|
||||
import {InjectFlags, inject, injectArgs, setCurrentInjector} from './injector_compatibility';
|
||||
import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, Provider, StaticClassProvider, StaticProvider, TypeProvider, ValueProvider} from './provider';
|
||||
import {APP_ROOT} from './scope';
|
||||
|
||||
@ -165,7 +166,7 @@ export class R3Injector {
|
||||
if (def && this.injectableDefInScope(def)) {
|
||||
// Found an ngInjectableDef and it's scoped to this injector. Pretend as if it was here
|
||||
// all along.
|
||||
record = injectableDefRecord(token);
|
||||
record = makeRecord(injectableDefFactory(token), NOT_YET);
|
||||
this.records.set(token, record);
|
||||
}
|
||||
}
|
||||
@ -177,8 +178,8 @@ export class R3Injector {
|
||||
|
||||
// Select the next injector based on the Self flag - if self is set, the next injector is
|
||||
// the NullInjector, otherwise it's the parent.
|
||||
let next = !(flags & InjectFlags.Self) ? this.parent : getNullInjector();
|
||||
return this.parent.get(token, notFoundValue);
|
||||
const nextInjector = !(flags & InjectFlags.Self) ? this.parent : getNullInjector();
|
||||
return nextInjector.get(token, notFoundValue);
|
||||
} finally {
|
||||
// Lastly, clean up the state by restoring the previous injector.
|
||||
setCurrentInjector(previousInjector);
|
||||
@ -328,7 +329,7 @@ export class R3Injector {
|
||||
}
|
||||
}
|
||||
|
||||
function injectableDefRecord(token: Type<any>| InjectionToken<any>): Record<any> {
|
||||
function injectableDefFactory(token: Type<any>| InjectionToken<any>): () => any {
|
||||
const injectableDef = getInjectableDef(token as InjectableType<any>);
|
||||
if (injectableDef === null) {
|
||||
if (token instanceof InjectionToken) {
|
||||
@ -336,35 +337,47 @@ function injectableDefRecord(token: Type<any>| InjectionToken<any>): Record<any>
|
||||
}
|
||||
// TODO(alxhub): there should probably be a strict mode which throws here instead of assuming a
|
||||
// no-args constructor.
|
||||
return makeRecord(() => new (token as Type<any>)());
|
||||
return () => new (token as Type<any>)();
|
||||
}
|
||||
return makeRecord(injectableDef.factory);
|
||||
return injectableDef.factory;
|
||||
}
|
||||
|
||||
function providerToRecord(provider: SingleProvider): Record<any> {
|
||||
let token = resolveForwardRef(provider);
|
||||
let value: any = NOT_YET;
|
||||
let factory: (() => any)|undefined = providerToFactory(provider);
|
||||
if (isValueProvider(provider)) {
|
||||
return makeRecord(undefined, provider.useValue);
|
||||
} else {
|
||||
return makeRecord(factory, NOT_YET);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a `SingleProvider` into a factory function.
|
||||
*
|
||||
* @param provider provider to convert to factory
|
||||
*/
|
||||
export function providerToFactory(provider: SingleProvider): () => any {
|
||||
let factory: (() => any)|undefined = undefined;
|
||||
if (isTypeProvider(provider)) {
|
||||
return injectableDefRecord(provider);
|
||||
return injectableDefFactory(resolveForwardRef(provider));
|
||||
} else {
|
||||
token = resolveForwardRef(provider.provide);
|
||||
if (isValueProvider(provider)) {
|
||||
value = provider.useValue;
|
||||
factory = () => resolveForwardRef(provider.useValue);
|
||||
} else if (isExistingProvider(provider)) {
|
||||
factory = () => inject(provider.useExisting);
|
||||
factory = () => inject(resolveForwardRef(provider.useExisting));
|
||||
} else if (isFactoryProvider(provider)) {
|
||||
factory = () => provider.useFactory(...injectArgs(provider.deps || []));
|
||||
} else {
|
||||
const classRef = (provider as StaticClassProvider | ClassProvider).useClass || token;
|
||||
const classRef = resolveForwardRef(
|
||||
(provider as StaticClassProvider | ClassProvider).useClass || provider.provide);
|
||||
if (hasDeps(provider)) {
|
||||
factory = () => new (classRef)(...injectArgs(provider.deps));
|
||||
} else {
|
||||
return injectableDefRecord(classRef);
|
||||
return injectableDefFactory(classRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
return makeRecord(factory, value);
|
||||
return factory;
|
||||
}
|
||||
|
||||
function makeRecord<T>(
|
||||
@ -392,7 +405,7 @@ function isFactoryProvider(value: SingleProvider): value is FactoryProvider {
|
||||
return !!(value as FactoryProvider).useFactory;
|
||||
}
|
||||
|
||||
function isTypeProvider(value: SingleProvider): value is TypeProvider {
|
||||
export function isTypeProvider(value: SingleProvider): value is TypeProvider {
|
||||
return typeof value === 'function';
|
||||
}
|
||||
|
||||
|
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_compatibility';
|
||||
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>| null = () => 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__;
|
||||
|
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
export {InjectableDef as ɵInjectableDef, InjectorDef as ɵInjectorDef, defineInjectable, defineInjector} from './di/defs';
|
||||
export {inject} from './di/injector';
|
||||
export {inject} from './di/injector_compatibility';
|
||||
export {NgModuleDef as ɵNgModuleDef, NgModuleDefWithMeta as ɵNgModuleDefWithMeta} from './metadata/ng_module';
|
||||
export {defineNgModule as ɵdefineNgModule} from './render3/definition';
|
||||
export {NgModuleFactory as ɵNgModuleFactory} from './render3/ng_module_ref';
|
||||
|
@ -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__;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user