Compare commits
220 Commits
4.0.0-beta
...
2.4.6
Author | SHA1 | Date | |
---|---|---|---|
343ee8a3a2 | |||
223b5eb367 | |||
7e639aac15 | |||
83dafd3054 | |||
e641636624 | |||
c409860a9f | |||
0101aa31d6 | |||
a5b4af0fdd | |||
d9420311ca | |||
774e1db87c | |||
109f0d16ef | |||
71567d1eee | |||
bb71acc172 | |||
e98d6f0912 | |||
1dbebb184f | |||
8882b86b54 | |||
0965636735 | |||
4d2901d480 | |||
a047124e1a | |||
09e2d20e22 | |||
e3bdf82c0d | |||
0614289608 | |||
7c344a4e49 | |||
250dbc4bc8 | |||
70bbdf55da | |||
41b8d95fa7 | |||
1eece5046d | |||
1ef3eeecbd | |||
94500e0fad | |||
dd53606f69 | |||
6c8b5dda87 | |||
458ccc1aff | |||
07cfd8c432 | |||
23bd0fbfc1 | |||
3d1e536143 | |||
c827097610 | |||
8d4aa82c04 | |||
14e97516cb | |||
bc47a8cc74 | |||
32cc6759ef | |||
d5f1419afe | |||
117fa79c7c | |||
777ba46837 | |||
f3d55068a8 | |||
7ed39ebaaf | |||
091f0a5aaa | |||
315606e02c | |||
5ea373d184 | |||
6e36bb7b20 | |||
3b2fb23805 | |||
bd2eecb4de | |||
3d351a4f5f | |||
5492fada21 | |||
fd4f9acbcf | |||
48528a86e1 | |||
80364def27 | |||
1803beb4d5 | |||
3bcba8a570 | |||
84542d8ae7 | |||
17cb3ec565 | |||
015878afe6 | |||
2af58622c1 | |||
7ffd10541d | |||
481b099d82 | |||
49c4b0fa92 | |||
b8b6b1d27a | |||
892b5ba950 | |||
bd15110c7d | |||
2250082fd7 | |||
87316c52db | |||
606b76d9bb | |||
3d0b1b8184 | |||
261fd16780 | |||
104cc42f6d | |||
a7d28044c5 | |||
055bea2969 | |||
dad0d21b89 | |||
313683f6f3 | |||
338be6d6a5 | |||
4b56f79328 | |||
d7f2a3c71b | |||
1c929ae244 | |||
83d0ff6d13 | |||
d43e5dd44d | |||
61ba223c1a | |||
6164eb25f3 | |||
5e9d3dba3a | |||
16922655ca | |||
7dc12b93fe | |||
1c82b58185 | |||
d6c414c08f | |||
d25d1730c7 | |||
03b35d2e8f | |||
722543739e | |||
56b4296a09 | |||
f1cde4339b | |||
b245b920a6 | |||
f47a71689c | |||
6be55cc214 | |||
504199cf5a | |||
17c5fa9293 | |||
5f49c3ed23 | |||
ebba63057f | |||
5058461af7 | |||
21f5f05893 | |||
f2ee81fa7a | |||
ae1029da35 | |||
230e33f3f1 | |||
ec0ca01224 | |||
1cd73c7a79 | |||
9f6a647908 | |||
29ffdfdffe | |||
5754ecc3e1 | |||
dab15c79dd | |||
21942a88f0 | |||
018865ee6b | |||
f7234378b6 | |||
5f47583c94 | |||
0e7f9f0bff | |||
28a92b2bcd | |||
48be539824 | |||
d788c679b6 | |||
a38f14b39c | |||
6a5e46cedd | |||
6316e5df71 | |||
90fca7c879 | |||
d871ae2dc6 | |||
44e84d87f9 | |||
b9e979e0a5 | |||
cb2aa41782 | |||
189a7e3750 | |||
6efdf84d3e | |||
e61bfc8b24 | |||
070f9d0644 | |||
8d5da1e57a | |||
b6406191c7 | |||
124face441 | |||
de4ace77fe | |||
debb0c9798 | |||
9b87bb6d7f | |||
71e88a8c3c | |||
c26c24c544 | |||
3f178410c3 | |||
b36f4bc00d | |||
355c537883 | |||
f277303ca3 | |||
50afbe094f | |||
15ea758d01 | |||
1f0f429f2a | |||
dbb364e23a | |||
540b1197a6 | |||
d30cc8461b | |||
f27954e62c | |||
69b52eb2b3 | |||
b9b557cdb0 | |||
a72a002a8d | |||
a0437f8c9d | |||
1c279b3264 | |||
cd03c77364 | |||
f6ef7d6e5a | |||
6aeaca3fb4 | |||
af62050729 | |||
cb69656b56 | |||
2fc0560988 | |||
86c50983d7 | |||
21976446e0 | |||
998ce9ad7e | |||
111523677c | |||
2d74a224d0 | |||
4d6ac9d414 | |||
6557bc34f6 | |||
e2622add07 | |||
ecfad467a1 | |||
5918133784 | |||
700bce9ec1 | |||
a64a35a8c1 | |||
b3dcff0cc1 | |||
124267c87a | |||
547bfa92ef | |||
d40bbf4d5c | |||
94b7031fe9 | |||
df0bf1dd74 | |||
c8a9b70890 | |||
efa2d80df8 | |||
a58e5efd09 | |||
86cf0ef892 | |||
5c568fab86 | |||
566104504c | |||
307d305b2d | |||
0a7364feea | |||
4544b1d7a6 | |||
9e0e6b59d1 | |||
14dd2b367a | |||
91eb8914dd | |||
77823d721f | |||
2afe2d107f | |||
17f40fb75f | |||
98936fdf16 | |||
7383e4a801 | |||
65c9b5b6aa | |||
5fab8710cb | |||
f106a18b96 | |||
8db184d349 | |||
c18eb298eb | |||
3f4aa59cfa | |||
79728b4c41 | |||
413167ab1b | |||
203cc7e1f1 | |||
b0cd514709 | |||
392c9ac214 | |||
a26e054857 | |||
c0b001a6af | |||
c8c1f22f9c | |||
e4d5a5f003 | |||
03d9de33a1 | |||
a8a80cf523 | |||
6c1d7908d5 | |||
9aab6d24eb | |||
5ee8155e4e | |||
21de0f239d |
233
.pullapprove.yml
Normal file
233
.pullapprove.yml
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
# Configuration for pullapprove.com
|
||||||
|
#
|
||||||
|
# Approval access and primary role is determined by info in the project ownership spreadsheet:
|
||||||
|
# https://docs.google.com/spreadsheets/d/1-HIlzfbPYGsPr9KuYMe6bLfc4LXzPjpoALqtYRYTZB0/edit?pli=1#gid=0&vpid=A5
|
||||||
|
#
|
||||||
|
# === GitHub username to Full name map ===
|
||||||
|
#
|
||||||
|
# alexeagle - Alex Eagle
|
||||||
|
# alxhub - Alex Rickabaugh
|
||||||
|
# chuckjaz - Chuck Jazdzewski
|
||||||
|
# gkalpak - George Kalpakas
|
||||||
|
# IgorMinar - Igor Minar
|
||||||
|
# kara - Kara Erickson
|
||||||
|
# matsko - Matias Niemelä
|
||||||
|
# mhevery - Misko Hevery
|
||||||
|
# petebacondarwin - Pete Bacon Darwin
|
||||||
|
# pkozlowski-opensource - Pawel Kozlowski
|
||||||
|
# robwormald - Rob Wormald
|
||||||
|
# tbosch - Tobias Bosch
|
||||||
|
# vicb - Victor Berchet
|
||||||
|
# vikerman - Vikram Subramanian
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
group_defaults:
|
||||||
|
required: 1
|
||||||
|
reset_on_reopened:
|
||||||
|
enabled: true
|
||||||
|
approve_by_comment:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
groups:
|
||||||
|
root:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
include:
|
||||||
|
- "*"
|
||||||
|
exclude:
|
||||||
|
- "angular.io/*"
|
||||||
|
- "integration/*"
|
||||||
|
- "modules/*"
|
||||||
|
- "tools/*"
|
||||||
|
users:
|
||||||
|
- IgorMinar
|
||||||
|
- mhevery
|
||||||
|
|
||||||
|
build-and-ci:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
include:
|
||||||
|
- "*.yml"
|
||||||
|
- "*.json"
|
||||||
|
- "*.lock"
|
||||||
|
- "tools/*"
|
||||||
|
exclude:
|
||||||
|
- "tools/@angular/tsc-wrapped/*"
|
||||||
|
users:
|
||||||
|
- IgorMinar #primary
|
||||||
|
- mhevery
|
||||||
|
|
||||||
|
integration:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "integration/*"
|
||||||
|
users:
|
||||||
|
- alexeagle
|
||||||
|
- mhevery
|
||||||
|
- tbosch
|
||||||
|
- vicb
|
||||||
|
- IgorMinar #fallback
|
||||||
|
|
||||||
|
|
||||||
|
core:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/core/*"
|
||||||
|
users:
|
||||||
|
- tbosch #primary
|
||||||
|
- mhevery
|
||||||
|
- vicb
|
||||||
|
- IgorMinar #fallback
|
||||||
|
|
||||||
|
compiler/animations:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/compiler/src/animation/*"
|
||||||
|
users:
|
||||||
|
- matsko #primary
|
||||||
|
- tbosch
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
compiler/i18n:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/compiler/src/i18n/*"
|
||||||
|
users:
|
||||||
|
- vicb #primary
|
||||||
|
- tbosch
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
compiler:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/compiler/*"
|
||||||
|
users:
|
||||||
|
- tbosch #primary
|
||||||
|
- mhevery
|
||||||
|
- IgorMinar #fallback
|
||||||
|
|
||||||
|
compiler-cli:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "tools/@angular/tsc-wrapped/*"
|
||||||
|
- "modules/@angular/compiler-cli/*"
|
||||||
|
users:
|
||||||
|
- alexeagle
|
||||||
|
- chuckjaz
|
||||||
|
- tbosch
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
common:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/common/*"
|
||||||
|
users:
|
||||||
|
- pkozlowski-opensource #primary
|
||||||
|
- vicb
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
forms:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/forms/*"
|
||||||
|
users:
|
||||||
|
- kara #primary
|
||||||
|
# needs secondary
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
http:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/http/*"
|
||||||
|
users:
|
||||||
|
- vikerman #primary
|
||||||
|
- alxhub
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
language-service:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/language-service/*"
|
||||||
|
users:
|
||||||
|
- chuckjaz #primary
|
||||||
|
# needs secondary
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
router:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/router/*"
|
||||||
|
users:
|
||||||
|
- vicb #primary
|
||||||
|
# needs secondary
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
upgrade:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/upgrade/*"
|
||||||
|
users:
|
||||||
|
- petebacondarwin #primary
|
||||||
|
- gkalpak
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
platform-browser:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/platform-browser/*"
|
||||||
|
users:
|
||||||
|
- tbosch #primary
|
||||||
|
- vicb
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
platform-server:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/platform-server/*"
|
||||||
|
users:
|
||||||
|
- vikerman #primary
|
||||||
|
- alxhub
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
platform-webworker:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/platform-webworker/*"
|
||||||
|
users:
|
||||||
|
- vicb #primary
|
||||||
|
# needs secondary
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
benchpress:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "modules/@angular/benchpress/*"
|
||||||
|
users:
|
||||||
|
- tbosch #primary
|
||||||
|
# needs secondary
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
angular.io:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "angular.io/*"
|
||||||
|
users:
|
||||||
|
- IgorMinar
|
||||||
|
- robwormald
|
||||||
|
- mhevery #fallback
|
@ -2,6 +2,9 @@ language: node_js
|
|||||||
sudo: false
|
sudo: false
|
||||||
node_js:
|
node_js:
|
||||||
- '6.6.0'
|
- '6.6.0'
|
||||||
|
git:
|
||||||
|
# Increased from default (50) to ensure last release tag is in this range
|
||||||
|
depth: 150
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
# firefox: "38.0"
|
# firefox: "38.0"
|
||||||
|
153
CHANGELOG.md
153
CHANGELOG.md
@ -1,54 +1,101 @@
|
|||||||
<a name="4.0.0-beta.2"></a>
|
<a name="2.4.6"></a>
|
||||||
# [4.0.0-beta.2](https://github.com/angular/angular/compare/4.0.0-beta.1...4.0.0-beta.2) (2017-01-06)
|
## [2.4.6](https://github.com/angular/angular/compare/2.4.5...2.4.6) (2017-02-03)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **compiler:** generate less code for bindings to DOM elements ([db49d42](https://github.com/angular/angular/commit/db49d42))
|
|
||||||
* **compiler:** generate proper reexports in `.ngfactory.ts` files to not need transitive deps for compiling `.ngfactory.ts` files. ([#13524](https://github.com/angular/angular/issues/13524)) ([9c69703](https://github.com/angular/angular/commit/9c69703)), closes [#12787](https://github.com/angular/angular/issues/12787)
|
|
||||||
* **router:** add an extra argument to CanDeactivate interface ([#13560](https://github.com/angular/angular/issues/13560)) ([69fa3bb](https://github.com/angular/angular/commit/69fa3bb)), closes [#9853](https://github.com/angular/angular/issues/9853)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **common:** improve error message by adding a link to trackBy docs ([#13634](https://github.com/angular/angular/issues/13634)) ([6976903](https://github.com/angular/angular/commit/6976903))
|
* **common:** add PopStateEvent interface ([#13400](https://github.com/angular/angular/issues/13400)) ([71567d1](https://github.com/angular/angular/commit/71567d1)), closes [#13378](https://github.com/angular/angular/issues/13378)
|
||||||
* **common:** do not override locale provided on bootstrap ([#13654](https://github.com/angular/angular/issues/13654)) ([2dd6280](https://github.com/angular/angular/commit/2dd6280)), closes [#13607](https://github.com/angular/angular/issues/13607)
|
* **common:** DatePipe does't throw for NaN ([#14117](https://github.com/angular/angular/issues/14117)) ([32cc675](https://github.com/angular/angular/commit/32cc675)), closes [#14103](https://github.com/angular/angular/issues/14103)
|
||||||
* **common:** allow null/undefined values for `NgForTrackBy` ([f88cd2f](https://github.com/angular/angular/commit/f88cd2f)), closes [#13641](https://github.com/angular/angular/issues/13641)
|
* **common:** DatePipe parses input string if it's not a valid date in browser ([#13895](https://github.com/angular/angular/issues/13895)) ([e641636](https://github.com/angular/angular/commit/e641636)), closes [#12334](https://github.com/angular/angular/issues/12334) [#13874](https://github.com/angular/angular/issues/13874)
|
||||||
* **compiler:** don’t throw when using `ANALYZE_FOR_ENTRY_COMPONENTS` with user classes ([#13679](https://github.com/angular/angular/issues/13679)) ([7690d02](https://github.com/angular/angular/commit/7690d02)), closes [#13565](https://github.com/angular/angular/issues/13565)
|
* **common:** introduce isObservable method ([#14067](https://github.com/angular/angular/issues/14067)) ([109f0d1](https://github.com/angular/angular/commit/109f0d1)), closes [#8848](https://github.com/angular/angular/issues/8848)
|
||||||
* **compiler:** improve error message for undefined providers ([#13546](https://github.com/angular/angular/issues/13546)) ([6b02b80](https://github.com/angular/angular/commit/6b02b80)), closes [#10835](https://github.com/angular/angular/issues/10835)
|
* **compiler:** allow empty translations for attributes ([#14085](https://github.com/angular/angular/issues/14085)) ([f3d5506](https://github.com/angular/angular/commit/f3d5506)), closes [#13897](https://github.com/angular/angular/issues/13897)
|
||||||
* **compiler:** improve the error when template is not a string ([2c0c86e](https://github.com/angular/angular/commit/2c0c86e)), closes [#8708](https://github.com/angular/angular/issues/8708) [#13377](https://github.com/angular/angular/issues/13377)
|
* **core:** add bootstrapped modules into platform modules list ([#13740](https://github.com/angular/angular/issues/13740)) ([250dbc4](https://github.com/angular/angular/commit/250dbc4)), closes [#12015](https://github.com/angular/angular/issues/12015)
|
||||||
* **compiler:** query `<template>` elements before their children. ([#13677](https://github.com/angular/angular/issues/13677)) ([7c21064](https://github.com/angular/angular/commit/7c21064)), closes [#13118](https://github.com/angular/angular/issues/13118) [#13167](https://github.com/angular/angular/issues/13167)
|
* **core:** ViewContainerRef.indexOf should not throw error when empty ([#13220](https://github.com/angular/angular/issues/13220)) ([41b8d95](https://github.com/angular/angular/commit/41b8d95))
|
||||||
* **compiler:** throw an error for invalid provider ([#13544](https://github.com/angular/angular/issues/13544)) ([445ed43](https://github.com/angular/angular/commit/445ed43)), closes [#8870](https://github.com/angular/angular/issues/8870)
|
* **forms:** show a blank line when nothing is selected in IE or Edge ([#13903](https://github.com/angular/angular/issues/13903)) ([09e2d20](https://github.com/angular/angular/commit/09e2d20)), closes [#10010](https://github.com/angular/angular/issues/10010)
|
||||||
* **compiler:** allow "." in attribute selectors ([#13653](https://github.com/angular/angular/issues/13653)) ([881eb89](https://github.com/angular/angular/commit/881eb89)), closes [#13645](https://github.com/angular/angular/issues/13645)
|
* **forms:** verify functions passed into async validators returns Observable or Promise ([#14053](https://github.com/angular/angular/issues/14053)) ([774e1db](https://github.com/angular/angular/commit/774e1db))
|
||||||
* **core:** animations no longer silently exits if the element is not apart of the DOM ([#13763](https://github.com/angular/angular/issues/13763)) ([21030e9](https://github.com/angular/angular/commit/21030e9))
|
* ngModel should use rxjs/symbol/observable to detect observable ([#14236](https://github.com/angular/angular/issues/14236)) ([7e639aa](https://github.com/angular/angular/commit/7e639aa))
|
||||||
* **core:** animations should blend in all previously transitioned styles into next animation if interrupted ([#13148](https://github.com/angular/angular/issues/13148)) ([889b48d](https://github.com/angular/angular/commit/889b48d))
|
* **http:** remove dots from jsonp callback name ([#13219](https://github.com/angular/angular/issues/13219)) ([1eece50](https://github.com/angular/angular/commit/1eece50))
|
||||||
* **core:** remove reference to "Angular 2" in dev mode warning ([#13751](https://github.com/angular/angular/issues/13751)) ([c5c53f3](https://github.com/angular/angular/commit/c5c53f3))
|
* **i18n:** parse ICU messages while normalizing templates ([#14153](https://github.com/angular/angular/issues/14153)) ([8d4aa82](https://github.com/angular/angular/commit/8d4aa82))
|
||||||
* **forms:** Validators.required properly validate arrays ([#13362](https://github.com/angular/angular/issues/13362)) ([9898d8f](https://github.com/angular/angular/commit/9898d8f)), closes [#12274](https://github.com/angular/angular/issues/12274)
|
* **language-service:** do not crash when Angular cannot be located ([#14123](https://github.com/angular/angular/issues/14123)) ([a5b4af0](https://github.com/angular/angular/commit/a5b4af0)), closes [#14122](https://github.com/angular/angular/issues/14122)
|
||||||
* **i18n:** parse ICU messages while normalizing templates ([e74d8aa](https://github.com/angular/angular/commit/e74d8aa))
|
* **platform-browser:** remove style nodes on destroy ([#13744](https://github.com/angular/angular/issues/13744)) ([0614289](https://github.com/angular/angular/commit/0614289)), closes [#11746](https://github.com/angular/angular/issues/11746)
|
||||||
* **language-service:** support TypeScript 2.1 ([#13655](https://github.com/angular/angular/issues/13655)) ([8063b0d](https://github.com/angular/angular/commit/8063b0d))
|
* **router:** fix CanActivate redirect to the root on initial load ([#13929](https://github.com/angular/angular/issues/13929)) ([a047124](https://github.com/angular/angular/commit/a047124)), closes [#13530](https://github.com/angular/angular/issues/13530)
|
||||||
* **router:** fix lazy loaded module with wildcard route ([#13649](https://github.com/angular/angular/issues/13649)) ([0eca960](https://github.com/angular/angular/commit/0eca960)), closes [#12955](https://github.com/angular/angular/issues/12955)
|
* **router:** should find guard provided in a lazy loaded module ([#13989](https://github.com/angular/angular/issues/13989)) ([0965636](https://github.com/angular/angular/commit/0965636)), closes [#12275](https://github.com/angular/angular/issues/12275)
|
||||||
* **router:** routerLink support of null ([#13380](https://github.com/angular/angular/issues/13380)) ([174334d](https://github.com/angular/angular/commit/174334d)), closes [#6971](https://github.com/angular/angular/issues/6971)
|
* **router:** should allow navigation from root component in ngOnInit hook ([#13932](https://github.com/angular/angular/issues/13932)) ([4d2901d](https://github.com/angular/angular/commit/4d2901d)), closes [#13795](https://github.com/angular/angular/issues/13795)
|
||||||
* **router:** update route snapshot before emit new values ([#13558](https://github.com/angular/angular/issues/13558)) ([07e0fce](https://github.com/angular/angular/commit/07e0fce)), closes [#12912](https://github.com/angular/angular/issues/12912)
|
* **testing:** async/fakeAsync/inject/withModule helpers should pass through context to callback functions ([#13718](https://github.com/angular/angular/issues/13718)) ([70bbdf5](https://github.com/angular/angular/commit/70bbdf5))
|
||||||
* **testing:** improve misleading error message when don't call compileComponents ([#13543](https://github.com/angular/angular/issues/13543)) ([67380d4](https://github.com/angular/angular/commit/67380d4)), closes [#11301](https://github.com/angular/angular/issues/11301)
|
* **upgrade:** detect async downgrade component changes ([#14039](https://github.com/angular/angular/issues/14039)) ([117fa79](https://github.com/angular/angular/commit/117fa79)), closes [#6385](https://github.com/angular/angular/issues/6385) [#6385](https://github.com/angular/angular/issues/6385)
|
||||||
* **upgrade:** fix/improve support for lifecycle hooks ([#13020](https://github.com/angular/angular/issues/13020)) ([e5c4e58](https://github.com/angular/angular/commit/e5c4e58))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* **core**: `SimpleChange` now takes an additional argument that defines whether this is the first
|
|
||||||
change or not. This is a low profile API and we don't expect anyone to be affected by this change.
|
|
||||||
If you are impacted by this change please file an issue. ([465516b](https://github.com/angular/angular/commit/465516b))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="4.0.0-beta.1"></a>
|
<a name="2.4.5"></a>
|
||||||
# [4.0.0-beta.1](https://github.com/angular/angular/compare/2.4.0-marker...4.0.0-beta.1) (2016-12-22)
|
## [2.4.5](https://github.com/angular/angular/compare/2.4.4...2.4.5) (2017-01-25)
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **upgrade:** support the `$doCheck()` lifecycle hook in `UpgradeComponent` ([#13015](https://github.com/angular/angular/issues/13015)) ([9da4c25](https://github.com/angular/angular/commit/9da4c25))
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** [i18n] XMB/XTB placeholder names can contain only A-Z, 0-9, _n ([5492fad](https://github.com/angular/angular/commit/5492fad))
|
||||||
|
* **compiler:** fix regexp to support firefox 31 ([#14082](https://github.com/angular/angular/issues/14082)) ([bd2eecb](https://github.com/angular/angular/commit/bd2eecb)), closes [#14029](https://github.com/angular/angular/issues/14029) [#13900](https://github.com/angular/angular/issues/13900)
|
||||||
|
* **core:** export animation classes required for Renderer impl ([#14002](https://github.com/angular/angular/issues/14002)) ([fd4f9ac](https://github.com/angular/angular/commit/fd4f9ac)), closes [#14001](https://github.com/angular/angular/issues/14001)
|
||||||
|
* **upgrade:** ensure upgraded injector is initialized early enough ([#14065](https://github.com/angular/angular/issues/14065)) ([3b2fb23](https://github.com/angular/angular/commit/3b2fb23)), closes [#13811](https://github.com/angular/angular/issues/13811)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.4.4"></a>
|
||||||
|
## [2.4.4](https://github.com/angular/angular/compare/2.4.3...2.4.4) (2017-01-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **animations:** fix internal jscompiler issue and AOT quoting ([#13798](https://github.com/angular/angular/issues/13798)) ([261fd16](https://github.com/angular/angular/commit/261fd16))
|
||||||
|
* **common:** support numeric value as discrete cases for NgPlural ([#13876](https://github.com/angular/angular/issues/13876)) ([3d0b1b8](https://github.com/angular/angular/commit/3d0b1b8))
|
||||||
|
* **http:** don't create a blob out of ArrayBuffer when type is application/octet-stream ([#13992](https://github.com/angular/angular/issues/13992)) ([015878a](https://github.com/angular/angular/commit/015878a)), closes [#13973](https://github.com/angular/angular/issues/13973)
|
||||||
|
* **router:** enable loadChildren with function in aot ([#13909](https://github.com/angular/angular/issues/13909)) ([2af5862](https://github.com/angular/angular/commit/2af5862)), closes [#11075](https://github.com/angular/angular/issues/11075)
|
||||||
|
* **router:** routerLinkActive should not throw when not initialized ([#13273](https://github.com/angular/angular/issues/13273)) ([49c4b0f](https://github.com/angular/angular/commit/49c4b0f)), closes [#13270](https://github.com/angular/angular/issues/13270)
|
||||||
|
* **security:** allow calc and gradient functions. ([#13943](https://github.com/angular/angular/issues/13943)) ([bd15110](https://github.com/angular/angular/commit/bd15110))
|
||||||
|
* **upgrade:** detect async downgrade component changes ([#13812](https://github.com/angular/angular/issues/13812)) ([2250082](https://github.com/angular/angular/commit/2250082)), closes [#6385](https://github.com/angular/angular/issues/6385) [#6385](https://github.com/angular/angular/issues/6385) [#10660](https://github.com/angular/angular/issues/10660) [#12318](https://github.com/angular/angular/issues/12318) [#12034](https://github.com/angular/angular/issues/12034)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.4.3"></a>
|
||||||
|
## [2.4.3](https://github.com/angular/angular/compare/2.4.2...2.4.3) (2017-01-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** avoid evaluating arguments to unknown decorators ([5e9d3db](https://github.com/angular/angular/commit/5e9d3db)), closes [#13605](https://github.com/angular/angular/issues/13605)
|
||||||
|
* **compiler:** fix template binding parsing (`*directive="-..."`) ([7dc12b9](https://github.com/angular/angular/commit/7dc12b9)), closes [#13800](https://github.com/angular/angular/issues/13800)
|
||||||
|
* **compiler-cli:** add support for more than 2 levels of nested lazy routes ([6164eb2](https://github.com/angular/angular/commit/6164eb2)), closes [angular/angular-cli#3663](https://github.com/angular/angular-cli/issues/3663)
|
||||||
|
* **compiler-cli:** avoid handling functions in loadChildren as lazy load routes paths ([313683f](https://github.com/angular/angular/commit/313683f)), closes [angular/angular-cli#3204](https://github.com/angular/angular-cli/issues/3204)
|
||||||
|
* **i18n:** translate attributes inside elements marked for translation ([d7f2a3c](https://github.com/angular/angular/commit/d7f2a3c))
|
||||||
|
* **router:** RouterLink mirrors input `target` as attribute ([1c82b58](https://github.com/angular/angular/commit/1c82b58)), closes [#13837](https://github.com/angular/angular/issues/13837)
|
||||||
|
* **router:** throw an error when navigate to null/undefined path ([61ba223](https://github.com/angular/angular/commit/61ba223)), closes [#10560](https://github.com/angular/angular/issues/10560) [#13384](https://github.com/angular/angular/issues/13384)
|
||||||
|
* **router:** fix checking for object intersection ([1692265](https://github.com/angular/angular/commit/1692265))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.4.2"></a>
|
||||||
|
## [2.4.2](https://github.com/angular/angular/compare/2.4.1...2.4.2) (2017-01-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** add link to trackBy docs from the error message ([#13634](https://github.com/angular/angular/issues/13634)) ([f723437](https://github.com/angular/angular/commit/f723437))
|
||||||
|
* **common:** do not override locale provided on bootstrap ([#13654](https://github.com/angular/angular/issues/13654)) ([5f49c3e](https://github.com/angular/angular/commit/5f49c3e)), closes [#13607](https://github.com/angular/angular/issues/13607)
|
||||||
|
* **common:** allow null/undefined values for `NgForTrackBy` ([6be55cc](https://github.com/angular/angular/commit/6be55cc)), closes [#13641](https://github.com/angular/angular/issues/13641)
|
||||||
|
* **compiler:** don’t throw when using `ANALYZE_FOR_ENTRY_COMPONENTS` with user classes ([#13679](https://github.com/angular/angular/issues/13679)) ([230e33f](https://github.com/angular/angular/commit/230e33f)), closes [#13565](https://github.com/angular/angular/issues/13565)
|
||||||
|
* **compiler:** query `<template>` elements before their children. ([#13677](https://github.com/angular/angular/issues/13677)) ([1cd73c7](https://github.com/angular/angular/commit/1cd73c7)), closes [#13118](https://github.com/angular/angular/issues/13118) [#13167](https://github.com/angular/angular/issues/13167)
|
||||||
|
* **compiler:** allow "." in attribute selectors ([#13653](https://github.com/angular/angular/issues/13653)) ([29ffdfd](https://github.com/angular/angular/commit/29ffdfd)), closes [#13645](https://github.com/angular/angular/issues/13645) [#13982](https://github.com/angular/angular/issues/13982)
|
||||||
|
* **core:** animations no longer silently exits if the element is not apart of the DOM ([#13763](https://github.com/angular/angular/issues/13763)) ([f1cde43](https://github.com/angular/angular/commit/f1cde43))
|
||||||
|
* **core:** animations should blend in all previously transitioned styles into next animation if interrupted ([#13148](https://github.com/angular/angular/issues/13148)) ([b245b92](https://github.com/angular/angular/commit/b245b92))
|
||||||
|
* **core:** remove reference to "Angular 2" in dev mode warning ([#13751](https://github.com/angular/angular/issues/13751)) ([21f5f05](https://github.com/angular/angular/commit/21f5f05))
|
||||||
|
* **core/testing:** improve misleading error message when don't call compileComponents ([#13543](https://github.com/angular/angular/issues/13543)) ([0e7f9f0](https://github.com/angular/angular/commit/0e7f9f0)), closes [#11301](https://github.com/angular/angular/issues/11301)
|
||||||
|
* **forms:** Validators.required properly validate arrays ([#13362](https://github.com/angular/angular/issues/13362)) ([17c5fa9](https://github.com/angular/angular/commit/17c5fa9)), closes [#12274](https://github.com/angular/angular/issues/12274)
|
||||||
|
* **language-service:** support TypeScript 2.1 ([#13655](https://github.com/angular/angular/issues/13655)) ([56b4296](https://github.com/angular/angular/commit/56b4296))
|
||||||
|
* **router:** fix lazy loaded module with wildcard route ([#13649](https://github.com/angular/angular/issues/13649)) ([5754ecc](https://github.com/angular/angular/commit/5754ecc)), closes [#12955](https://github.com/angular/angular/issues/12955)
|
||||||
|
* **router:** routerLink support of null ([#13380](https://github.com/angular/angular/issues/13380)) ([018865e](https://github.com/angular/angular/commit/018865e)), closes [#6971](https://github.com/angular/angular/issues/6971)
|
||||||
|
* **router:** update route snapshot before emit new values ([#13558](https://github.com/angular/angular/issues/13558)) ([9f6a647](https://github.com/angular/angular/commit/9f6a647)), closes [#12912](https://github.com/angular/angular/issues/12912)
|
||||||
|
* **upgrade:** fix/improve support for lifecycle hooks ([#13020](https://github.com/angular/angular/issues/13020)) ([21942a8](https://github.com/angular/angular/commit/21942a8))
|
||||||
|
|
||||||
|
|
||||||
Note: 4.0.0-beta.1 release also contains all the changes present in the 2.4.0 and the 2.4.1 releases.
|
|
||||||
|
|
||||||
<a name="2.4.1"></a>
|
<a name="2.4.1"></a>
|
||||||
## [2.4.1](https://github.com/angular/angular/compare/2.4.0...2.4.1) (2016-12-21)
|
## [2.4.1](https://github.com/angular/angular/compare/2.4.0...2.4.1) (2016-12-21)
|
||||||
@ -82,25 +129,6 @@ Note: 4.0.0-beta.1 release also contains all the changes present in the 2.4.0 an
|
|||||||
* update to `rxjs@5.0.1` and unpin the rxjs peerDeps via `^5.0.1` ([#13572](https://github.com/angular/angular/issues/13572)) ([8d5da1e](https://github.com/angular/angular/commit/8d5da1e)), closes [#13561](https://github.com/angular/angular/issues/13561) [#13478](https://github.com/angular/angular/issues/13478)
|
* update to `rxjs@5.0.1` and unpin the rxjs peerDeps via `^5.0.1` ([#13572](https://github.com/angular/angular/issues/13572)) ([8d5da1e](https://github.com/angular/angular/commit/8d5da1e)), closes [#13561](https://github.com/angular/angular/issues/13561) [#13478](https://github.com/angular/angular/issues/13478)
|
||||||
|
|
||||||
|
|
||||||
<a name="4.0.0-beta.0"></a>
|
|
||||||
# [4.0.0-beta.0](https://github.com/angular/angular/compare/2.3.0...4.0.0-beta.0) (2016-12-15)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **common:** add a `titlecase` pipe ([#13324](https://github.com/angular/angular/issues/13324)) ([61d7c1e](https://github.com/angular/angular/commit/61d7c1e)), closes [#11436](https://github.com/angular/angular/issues/11436)
|
|
||||||
* **common:** export NgLocaleLocalization ([#13367](https://github.com/angular/angular/issues/13367)) ([56dce0e](https://github.com/angular/angular/commit/56dce0e)), closes [#11921](https://github.com/angular/angular/issues/11921)
|
|
||||||
* **compiler:** add id property to i18nMessage ([6dd5201](https://github.com/angular/angular/commit/6dd5201))
|
|
||||||
* **compiler:** digest methods return i18nMessage id if sets ([562f7a2](https://github.com/angular/angular/commit/562f7a2))
|
|
||||||
* **forms:** add novalidate by default ([#13092](https://github.com/angular/angular/issues/13092)) ([4c35be3](https://github.com/angular/angular/commit/4c35be3))
|
|
||||||
* **http:** simplify URLSearchParams creation ([#13338](https://github.com/angular/angular/issues/13338)) ([90c2235](https://github.com/angular/angular/commit/90c2235)), closes [#8858](https://github.com/angular/angular/issues/8858)
|
|
||||||
* **language-service:** warn when a method isn't called in an event ([#13437](https://github.com/angular/angular/issues/13437)) ([9ec0a4e](https://github.com/angular/angular/commit/9ec0a4e))
|
|
||||||
* **platform browser:** introduce Meta service ([#12322](https://github.com/angular/angular/issues/12322)) ([72361fb](https://github.com/angular/angular/commit/72361fb))
|
|
||||||
* **router:** routerLink add tabindex attribute ([#13094](https://github.com/angular/angular/issues/13094)) ([a006c14](https://github.com/angular/angular/commit/a006c14)), closes [#10895](https://github.com/angular/angular/issues/10895)
|
|
||||||
* **testing:** add overrideTemplate method ([#13372](https://github.com/angular/angular/issues/13372)) ([169ed82](https://github.com/angular/angular/commit/169ed82)), closes [#10685](https://github.com/angular/angular/issues/10685)
|
|
||||||
* **common** ngIf now supports else; saves condition to local var ([b4db73d](https://github.com/angular/angular/commit/b4db73d)), closes [#13061](https://github.com/angular/angular/issues/13061) [#13297](https://github.com/angular/angular/issues/13297)
|
|
||||||
|
|
||||||
Note: 4.0.0-beta.0 release also contains all the changes present in the 2.3.1 release.
|
|
||||||
|
|
||||||
<a name="2.3.1"></a>
|
<a name="2.3.1"></a>
|
||||||
## [2.3.1](https://github.com/angular/angular/compare/2.3.0...2.3.1) (2016-12-15)
|
## [2.3.1](https://github.com/angular/angular/compare/2.3.0...2.3.1) (2016-12-15)
|
||||||
@ -147,14 +175,15 @@ Note: 4.0.0-beta.0 release also contains all the changes present in the 2.3.1 re
|
|||||||
|
|
||||||
### Note ###
|
### Note ###
|
||||||
|
|
||||||
Due to regression in the 2.3.0 release that was fixed by [#13464](https://github.com/angular/angular/pull/13464),
|
Due to regression in the 2.3.0 release that was fixed by [#13464](https://github.com/angular/angular/pull/13464),
|
||||||
components that have been compiled using 2.3.0 and published to npm will need to be recompiled and republished.
|
components that have been compiled using 2.3.0 and published to npm will need to be recompiled and republished.
|
||||||
|
|
||||||
The >=2.3.1 compiler will issue is the following error if it encounters components compiled with 2.3.0:
|
The >=2.3.1 compiler will issue is the following error if it encounters components compiled with 2.3.0:
|
||||||
`Unsupported metadata version 2 for module ${module}. This module should be compiled with a newer version of ngc`.
|
`Unsupported metadata version 2 for module ${module}. This module should be compiled with a newer version of ngc`.
|
||||||
|
|
||||||
We are adding more tests to our test suite to catch these kinds of problems before we cut a release.
|
We are adding more tests to our test suite to catch these kinds of problems before we cut a release.
|
||||||
|
|
||||||
|
|
||||||
<a name="2.3.0"></a>
|
<a name="2.3.0"></a>
|
||||||
# [2.3.0](https://github.com/angular/angular/compare/2.3.0-rc.0...2.3.0) (2016-12-07)
|
# [2.3.0](https://github.com/angular/angular/compare/2.3.0-rc.0...2.3.0) (2016-12-07)
|
||||||
|
|
||||||
|
27
COMMITTER.md
27
COMMITTER.md
@ -6,29 +6,16 @@ for details about how we maintain a linear commit history, and the rules for com
|
|||||||
As a contributor, just read the instructions in [CONTRIBUTING.md](CONTRIBUTING.md) and send a pull request.
|
As a contributor, just read the instructions in [CONTRIBUTING.md](CONTRIBUTING.md) and send a pull request.
|
||||||
Someone with committer access will do the rest.
|
Someone with committer access will do the rest.
|
||||||
|
|
||||||
## The `PR: merge` label and `presubmit-*` branches
|
# Change approvals
|
||||||
|
|
||||||
We have automated the process for merging pull requests into master. Our goal is to minimize the disruption for
|
Change approvals in our monorepo are managed via [pullapprove.com](https://about.pullapprove.com/) and are configured via the `.pullapprove.yaml` file.
|
||||||
Angular committers and also prevent breakages on master.
|
|
||||||
|
|
||||||
When a PR has `pr_state: LGTM` and is ready to merge, you should add the `pr_action: merge` label.
|
|
||||||
Currently (late 2015), we need to ensure that each PR will cleanly merge into the Google-internal version control,
|
|
||||||
so the caretaker reviews the changes manually.
|
|
||||||
|
|
||||||
After this review, the caretaker adds `zomg_admin: do_merge` which is restricted to admins only.
|
# Merging
|
||||||
A robot running as [mary-poppins](https://github.com/mary-poppins)
|
|
||||||
is notified that the label was added by an authorized person,
|
|
||||||
and will create a new branch in the angular project, using the convention `presubmit-{username}-pr-{number}`.
|
|
||||||
|
|
||||||
(Note: if the automation fails, committers can instead push the commits to a branch following this naming scheme.)
|
Once a change has all the approvals either the last approver or the PR author (if PR author has the project collaborator status) should mark the PR with "PR: merge" label.
|
||||||
|
This signals to the caretaker that the PR should be merged.
|
||||||
|
|
||||||
When a Travis build succeeds for a presubmit branch named following the convention,
|
# Who is the Caretaker?
|
||||||
Travis will re-base the commits, merge to master, and close the PR automatically.
|
|
||||||
|
|
||||||
Finally, after merge `mary-poppins` removes the presubmit branch.
|
See [this explanation](https://twitter.com/IgorMinar/status/799365744806854656).
|
||||||
|
|
||||||
## Administration
|
|
||||||
|
|
||||||
The list of users who can trigger a merge by adding the `zomg_admin: do_merge` label is stored in our appengine app datastore.
|
|
||||||
Edit the contents of the [CoreTeamMember Table](
|
|
||||||
https://console.developers.google.com/project/angular2-automation/datastore/query?queryType=KindQuery&namespace=&kind=CoreTeamMember)
|
|
||||||
|
@ -71,7 +71,13 @@ particular `gulp` and `protractor` commands. If you prefer, you can drop this pa
|
|||||||
Since global installs can become stale, and required versions can vary by project, we avoid their
|
Since global installs can become stale, and required versions can vary by project, we avoid their
|
||||||
use in these instructions.
|
use in these instructions.
|
||||||
|
|
||||||
*Option 2*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
|
*Option 2*: globally installing the package `npm-run` by running `npm install -g npm-run`
|
||||||
|
(you might need to prefix this command with `sudo`). You will then be able to run locally installed
|
||||||
|
package scripts by invoking: e.g., `npm-run gulp build`
|
||||||
|
(see [npm-run project page](https://github.com/timoxley/npm-run) for more details).
|
||||||
|
|
||||||
|
|
||||||
|
*Option 3*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
|
||||||
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
|
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
|
||||||
|
|
||||||
## Installing Bower Modules
|
## Installing Bower Modules
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
The MIT License
|
The MIT License
|
||||||
|
|
||||||
Copyright (c) 2014-2016 Google, Inc. http://angular.io
|
Copyright (c) 2014-2017 Google, Inc. http://angular.io
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
machine:
|
machine:
|
||||||
node:
|
node:
|
||||||
version: 5.4.1
|
version: 6.6.0
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
pre:
|
pre:
|
||||||
- npm install -g npm@3.6.0
|
- npm install -g npm@3.5.3
|
||||||
|
|
||||||
test:
|
test:
|
||||||
override:
|
override:
|
||||||
|
@ -19,7 +19,7 @@ One intentional omission from this list is `@angular/compiler`, which is current
|
|||||||
|
|
||||||
Additionally only the command line usage (not direct use of APIs) of `@angular/compiler-cli` is covered.
|
Additionally only the command line usage (not direct use of APIs) of `@angular/compiler-cli` is covered.
|
||||||
|
|
||||||
Other projects developed by the Angular team like angular-cli, Angular Material, benchpress, will be covered by these or similar guarantees in the future as they mature.
|
Other projects developed by the Angular team like angular-cli, Angular Material, will be covered by these or similar guarantees in the future as they mature.
|
||||||
|
|
||||||
Within the supported packages, we provide guarantees for:
|
Within the supported packages, we provide guarantees for:
|
||||||
|
|
||||||
@ -37,4 +37,4 @@ We explicitly don't consider the following to be our public API surface:
|
|||||||
- the contents and API surface of the code generated by Angular's compiler (with one notable exception: the existence and name of `NgModuleFactory` instances exported from generated code is guaranteed)
|
- the contents and API surface of the code generated by Angular's compiler (with one notable exception: the existence and name of `NgModuleFactory` instances exported from generated code is guaranteed)
|
||||||
|
|
||||||
|
|
||||||
Our peer dependencies (e.g. typescript, zone.js, or rxjs) are not considered part of our API surface, but they are included in our SemVer policies. We might update the required version of any of these dependencies in minor releases if the update doesn't cause breaking changes for Angular applications. Peer dependency updates that result in non-trivial breaking changes must be deferred to major Angular releases.
|
Our peer dependencies (e.g. TypeScript, Zone.js, or RxJS) are not considered part of our API surface, but they are included in our SemVer policies. We might update the required version of any of these dependencies in minor releases if the update doesn't cause breaking changes for Angular applications. Peer dependency updates that result in non-trivial breaking changes must be deferred to major Angular releases.
|
||||||
|
@ -12,9 +12,9 @@
|
|||||||
* Entry point for all public APIs of the common package.
|
* Entry point for all public APIs of the common package.
|
||||||
*/
|
*/
|
||||||
export * from './location/index';
|
export * from './location/index';
|
||||||
export {NgLocaleLocalization, NgLocalization} from './localization';
|
export {NgLocalization} from './localization';
|
||||||
export {CommonModule} from './common_module';
|
export {CommonModule} from './common_module';
|
||||||
export {NgClass, NgFor, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet} from './directives/index';
|
export {NgClass, NgFor, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet} from './directives/index';
|
||||||
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe, TitleCasePipe} from './pipes/index';
|
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe} from './pipes/index';
|
||||||
export {VERSION} from './version';
|
export {VERSION} from './version';
|
||||||
export {Version} from '@angular/core';
|
export {Version} from '@angular/core';
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||||
|
|
||||||
import {isListLikeIterable} from '../facade/collection';
|
import {isListLikeIterable} from '../facade/collection';
|
||||||
import {isPresent, stringify} from '../facade/lang';
|
import {stringify} from '../facade/lang';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngModule CommonModule
|
* @ngModule CommonModule
|
||||||
@ -134,7 +134,7 @@ export class NgClass implements DoCheck {
|
|||||||
(<any>rawClassVal).forEach((klass: string) => this._toggleClass(klass, !isCleanup));
|
(<any>rawClassVal).forEach((klass: string) => this._toggleClass(klass, !isCleanup));
|
||||||
} else {
|
} else {
|
||||||
Object.keys(rawClassVal).forEach(klass => {
|
Object.keys(rawClassVal).forEach(klass => {
|
||||||
if (isPresent(rawClassVal[klass])) this._toggleClass(klass, !isCleanup);
|
if (rawClassVal[klass] != null) this._toggleClass(klass, !isCleanup);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,152 +6,46 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conditionally includes a template based on the value of an `expression`.
|
* Removes or recreates a portion of the DOM tree based on an {expression}.
|
||||||
*
|
*
|
||||||
* `ngIf` evaluates the `expression` and then renders the `then` or `else` template in its place
|
* If the expression assigned to `ngIf` evaluates to a falsy value then the element
|
||||||
* when expression is truthy or falsy respectively. Typically the:
|
* is removed from the DOM, otherwise a clone of the element is reinserted into the DOM.
|
||||||
* - `then` template is the inline template of `ngIf` unless bound to a different value.
|
|
||||||
* - `else` template is blank unless it is bound.
|
|
||||||
*
|
*
|
||||||
* # Most common usage
|
* ### Example ([live demo](http://plnkr.co/edit/fe0kgemFBtmQOY31b4tw?p=preview)):
|
||||||
*
|
|
||||||
* The most common usage of the `ngIf` directive is to conditionally show the inline template as
|
|
||||||
* seen in this example:
|
|
||||||
* {@example common/ngIf/ts/module.ts region='NgIfSimple'}
|
|
||||||
*
|
|
||||||
* # Showing an alternative template using `else`
|
|
||||||
*
|
|
||||||
* If it is necessary to display a template when the `expression` is falsy use the `else` template
|
|
||||||
* binding as shown. Note that the `else` binding points to a `<template>` labeled `#elseBlock`.
|
|
||||||
* The template can be defined anywhere in the component view but is typically placed right after
|
|
||||||
* `ngIf` for readability.
|
|
||||||
*
|
|
||||||
* {@example common/ngIf/ts/module.ts region='NgIfElse'}
|
|
||||||
*
|
|
||||||
* # Using non-inlined `then` template
|
|
||||||
*
|
|
||||||
* Usually the `then` template is the inlined template of the `ngIf`, but it can be changed using
|
|
||||||
* a binding (just like `else`). Because `then` and `else` are bindings, the template references can
|
|
||||||
* change at runtime as shown in this example.
|
|
||||||
*
|
|
||||||
* {@example common/ngIf/ts/module.ts region='NgIfThenElse'}
|
|
||||||
*
|
|
||||||
* # Storing conditional result in a variable
|
|
||||||
*
|
|
||||||
* A common pattern is that we need to show a set of properties from the same object. If the
|
|
||||||
* object is undefined, then we have to use the safe-traversal-operator `?.` to guard against
|
|
||||||
* dereferencing a `null` value. This is especially the case when waiting on async data such as
|
|
||||||
* when using the `async` pipe as shown in folowing example:
|
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* Hello {{ (userStream|async)?.last }}, {{ (userStream|async)?.first }}!
|
* <div *ngIf="errorCount > 0" class="error">
|
||||||
|
* <!-- Error message displayed when the errorCount property in the current context is greater
|
||||||
|
* than 0. -->
|
||||||
|
* {{errorCount}} errors detected
|
||||||
|
* </div>
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* There are several inefficiencies in the above example:
|
|
||||||
* - We create multiple subscriptions on `userStream`. One for each `async` pipe, or two in the
|
|
||||||
* example above.
|
|
||||||
* - We cannot display an alternative screen while waiting for the data to arrive asynchronously.
|
|
||||||
* - We have to use the safe-traversal-operator `?.` to access properties, which is cumbersome.
|
|
||||||
* - We have to place the `async` pipe in parenthesis.
|
|
||||||
*
|
|
||||||
* A better way to do this is to use `ngIf` and store the result of the condition in a local
|
|
||||||
* variable as shown in the the example below:
|
|
||||||
*
|
|
||||||
* {@example common/ngIf/ts/module.ts region='NgIfLet'}
|
|
||||||
*
|
|
||||||
* Notice that:
|
|
||||||
* - We use only one `async` pipe and hence only one subscription gets created.
|
|
||||||
* - `ngIf` stores the result of the `userStream|async` in the local variable `user`.
|
|
||||||
* - The local `user` can then be bound repeatedly in a more efficient way.
|
|
||||||
* - No need to use the safe-traversal-operator `?.` to access properties as `ngIf` will only
|
|
||||||
* display the data if `userStream` returns a value.
|
|
||||||
* - We can display an alternative template while waiting for the data.
|
|
||||||
*
|
|
||||||
* ### Syntax
|
* ### Syntax
|
||||||
*
|
*
|
||||||
* Simple form:
|
|
||||||
* - `<div *ngIf="condition">...</div>`
|
* - `<div *ngIf="condition">...</div>`
|
||||||
* - `<div template="ngIf condition">...</div>`
|
* - `<div template="ngIf condition">...</div>`
|
||||||
* - `<template [ngIf]="condition"><div>...</div></template>`
|
* - `<template [ngIf]="condition"><div>...</div></template>`
|
||||||
*
|
*
|
||||||
* Form with an else block:
|
|
||||||
* ```
|
|
||||||
* <div *ngIf="condition; else elseBlock">...</div>
|
|
||||||
* <template #elseBlock>...</template>
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Form with a `then` and `else` block:
|
|
||||||
* ```
|
|
||||||
* <div *ngIf="condition; then thenBlock else elseBlock"></div>
|
|
||||||
* <template #thenBlock>...</template>
|
|
||||||
* <template #elseBlock>...</template>
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Form with storing the value locally:
|
|
||||||
* ```
|
|
||||||
* <div *ngIf="condition; else elseBlock; let value">{{value}}</div>
|
|
||||||
* <template #elseBlock>...</template>
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngIf]'})
|
@Directive({selector: '[ngIf]'})
|
||||||
export class NgIf {
|
export class NgIf {
|
||||||
private _context: NgIfContext = new NgIfContext();
|
private _hasView = false;
|
||||||
private _thenTemplateRef: TemplateRef<NgIfContext> = null;
|
|
||||||
private _elseTemplateRef: TemplateRef<NgIfContext> = null;
|
|
||||||
private _thenViewRef: EmbeddedViewRef<NgIfContext> = null;
|
|
||||||
private _elseViewRef: EmbeddedViewRef<NgIfContext> = null;
|
|
||||||
|
|
||||||
constructor(private _viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext>) {
|
constructor(private _viewContainer: ViewContainerRef, private _template: TemplateRef<Object>) {}
|
||||||
this._thenTemplateRef = templateRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
set ngIf(condition: any) {
|
set ngIf(condition: any) {
|
||||||
this._context.$implicit = condition;
|
if (condition && !this._hasView) {
|
||||||
this._updateView();
|
this._hasView = true;
|
||||||
}
|
this._viewContainer.createEmbeddedView(this._template);
|
||||||
|
} else if (!condition && this._hasView) {
|
||||||
@Input()
|
this._hasView = false;
|
||||||
set ngIfThen(templateRef: TemplateRef<NgIfContext>) {
|
this._viewContainer.clear();
|
||||||
this._thenTemplateRef = templateRef;
|
|
||||||
this._thenViewRef = null; // clear previous view if any.
|
|
||||||
this._updateView();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
set ngIfElse(templateRef: TemplateRef<NgIfContext>) {
|
|
||||||
this._elseTemplateRef = templateRef;
|
|
||||||
this._elseViewRef = null; // clear previous view if any.
|
|
||||||
this._updateView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _updateView() {
|
|
||||||
if (this._context.$implicit) {
|
|
||||||
if (!this._thenViewRef) {
|
|
||||||
this._viewContainer.clear();
|
|
||||||
this._elseViewRef = null;
|
|
||||||
if (this._thenTemplateRef) {
|
|
||||||
this._thenViewRef =
|
|
||||||
this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!this._elseViewRef) {
|
|
||||||
this._viewContainer.clear();
|
|
||||||
this._thenViewRef = null;
|
|
||||||
if (this._elseTemplateRef) {
|
|
||||||
this._elseViewRef =
|
|
||||||
this._viewContainer.createEmbeddedView(this._elseTemplateRef, this._context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NgIfContext { public $implicit: any = null; }
|
|
||||||
|
@ -21,10 +21,9 @@ import {SwitchView} from './ng_switch';
|
|||||||
* @howToUse
|
* @howToUse
|
||||||
* ```
|
* ```
|
||||||
* <some-element [ngPlural]="value">
|
* <some-element [ngPlural]="value">
|
||||||
* <ng-container *ngPluralCase="'=0'">there is nothing</ng-container>
|
* <template ngPluralCase="=0">there is nothing</template>
|
||||||
* <ng-container *ngPluralCase="'=1'">there is one</ng-container>
|
* <template ngPluralCase="=1">there is one</template>
|
||||||
* <ng-container *ngPluralCase="'few'">there are a few</ng-container>
|
* <template ngPluralCase="few">there are a few</template>
|
||||||
* <ng-container *ngPluralCase="'other'">there are exactly #</ng-container>
|
|
||||||
* </some-element>
|
* </some-element>
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
@ -90,8 +89,8 @@ export class NgPlural {
|
|||||||
* @howToUse
|
* @howToUse
|
||||||
* ```
|
* ```
|
||||||
* <some-element [ngPlural]="value">
|
* <some-element [ngPlural]="value">
|
||||||
* <ng-container *ngPluralCase="'=0'">...</ng-container>
|
* <template ngPluralCase="=0">...</template>
|
||||||
* <ng-container *ngPluralCase="'other'">...</ng-container>
|
* <template ngPluralCase="other">...</template>
|
||||||
* </some-element>
|
* </some-element>
|
||||||
*```
|
*```
|
||||||
*
|
*
|
||||||
@ -104,6 +103,7 @@ export class NgPluralCase {
|
|||||||
constructor(
|
constructor(
|
||||||
@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
|
@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
|
||||||
viewContainer: ViewContainerRef, @Host() ngPlural: NgPlural) {
|
viewContainer: ViewContainerRef, @Host() ngPlural: NgPlural) {
|
||||||
ngPlural.addCase(value, new SwitchView(viewContainer, template));
|
const isANumber: boolean = !isNaN(Number(value));
|
||||||
|
ngPlural.addCase(isANumber ? `=${value}` : value, new SwitchView(viewContainer, template));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,10 +49,10 @@ export function getPluralCategory(
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class NgLocaleLocalization extends NgLocalization {
|
export class NgLocaleLocalization extends NgLocalization {
|
||||||
constructor(@Inject(LOCALE_ID) protected locale: string) { super(); }
|
constructor(@Inject(LOCALE_ID) private _locale: string) { super(); }
|
||||||
|
|
||||||
getPluralCategory(value: any): string {
|
getPluralCategory(value: any): string {
|
||||||
const plural = getPluralCase(this.locale, value);
|
const plural = getPluralCase(this._locale, value);
|
||||||
|
|
||||||
switch (plural) {
|
switch (plural) {
|
||||||
case Plural.Zero:
|
case Plural.Zero:
|
||||||
@ -431,4 +431,4 @@ export function getPluralCase(locale: string, nLike: number | string): Plural {
|
|||||||
default:
|
default:
|
||||||
return Plural.Other;
|
return Plural.Other;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,6 +10,12 @@ import {EventEmitter, Injectable} from '@angular/core';
|
|||||||
|
|
||||||
import {LocationStrategy} from './location_strategy';
|
import {LocationStrategy} from './location_strategy';
|
||||||
|
|
||||||
|
/** @experimental */
|
||||||
|
export interface PopStateEvent {
|
||||||
|
pop?: boolean;
|
||||||
|
type?: string;
|
||||||
|
url?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @whatItDoes `Location` is a service that applications can use to interact with a browser's URL.
|
* @whatItDoes `Location` is a service that applications can use to interact with a browser's URL.
|
||||||
@ -122,7 +128,7 @@ export class Location {
|
|||||||
* Subscribe to the platform's `popState` events.
|
* Subscribe to the platform's `popState` events.
|
||||||
*/
|
*/
|
||||||
subscribe(
|
subscribe(
|
||||||
onNext: (value: any) => void, onThrow: (exception: any) => void = null,
|
onNext: (value: PopStateEvent) => void, onThrow: (exception: any) => void = null,
|
||||||
onReturn: () => void = null): Object {
|
onReturn: () => void = null): Object {
|
||||||
return this._subject.subscribe({next: onNext, error: onThrow, complete: onReturn});
|
return this._subject.subscribe({next: onNext, error: onThrow, complete: onReturn});
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ChangeDetectorRef, OnDestroy, Pipe, WrappedValue} from '@angular/core';
|
import {ChangeDetectorRef, OnDestroy, Pipe, PipeTransform, WrappedValue} from '@angular/core';
|
||||||
import {EventEmitter, Observable} from '../facade/async';
|
import {EventEmitter, Observable} from '../facade/async';
|
||||||
import {isPromise} from '../private_import_core';
|
import {isObservable, isPromise} from '../private_import_core';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
interface SubscriptionStrategy {
|
interface SubscriptionStrategy {
|
||||||
@ -66,7 +66,7 @@ const _observableStrategy = new ObservableStrategy();
|
|||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Pipe({name: 'async', pure: false})
|
@Pipe({name: 'async', pure: false})
|
||||||
export class AsyncPipe implements OnDestroy {
|
export class AsyncPipe implements OnDestroy, PipeTransform {
|
||||||
private _latestValue: Object = null;
|
private _latestValue: Object = null;
|
||||||
private _latestReturnedValue: Object = null;
|
private _latestReturnedValue: Object = null;
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ export class AsyncPipe implements OnDestroy {
|
|||||||
return _promiseStrategy;
|
return _promiseStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((<any>obj).subscribe) {
|
if (isObservable(obj)) {
|
||||||
return _observableStrategy;
|
return _observableStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ export class AsyncPipe implements OnDestroy {
|
|||||||
this._obj = null;
|
this._obj = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _updateLatestValue(async: any, value: Object) {
|
private _updateLatestValue(async: any, value: Object): void {
|
||||||
if (async === this._obj) {
|
if (async === this._obj) {
|
||||||
this._latestValue = value;
|
this._latestValue = value;
|
||||||
this._ref.markForCheck();
|
this._ref.markForCheck();
|
||||||
|
@ -1,72 +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 {Pipe, PipeTransform} from '@angular/core';
|
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms text to lowercase.
|
|
||||||
*
|
|
||||||
* {@example common/pipes/ts/lowerupper_pipe.ts region='LowerUpperPipe' }
|
|
||||||
*
|
|
||||||
* @stable
|
|
||||||
*/
|
|
||||||
@Pipe({name: 'lowercase'})
|
|
||||||
export class LowerCasePipe implements PipeTransform {
|
|
||||||
transform(value: string): string {
|
|
||||||
if (!value) return value;
|
|
||||||
if (typeof value !== 'string') {
|
|
||||||
throw new InvalidPipeArgumentError(LowerCasePipe, value);
|
|
||||||
}
|
|
||||||
return value.toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method to transform a single word to titlecase.
|
|
||||||
*
|
|
||||||
* @stable
|
|
||||||
*/
|
|
||||||
function titleCaseWord(word: string) {
|
|
||||||
if (!word) return word;
|
|
||||||
return word[0].toUpperCase() + word.substr(1).toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms text to titlecase.
|
|
||||||
*
|
|
||||||
* @stable
|
|
||||||
*/
|
|
||||||
@Pipe({name: 'titlecase'})
|
|
||||||
export class TitleCasePipe implements PipeTransform {
|
|
||||||
transform(value: string): string {
|
|
||||||
if (!value) return value;
|
|
||||||
if (typeof value !== 'string') {
|
|
||||||
throw new InvalidPipeArgumentError(TitleCasePipe, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value.split(/\b/g).map(word => titleCaseWord(word)).join('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms text to uppercase.
|
|
||||||
*
|
|
||||||
* @stable
|
|
||||||
*/
|
|
||||||
@Pipe({name: 'uppercase'})
|
|
||||||
export class UpperCasePipe implements PipeTransform {
|
|
||||||
transform(value: string): string {
|
|
||||||
if (!value) return value;
|
|
||||||
if (typeof value !== 'string') {
|
|
||||||
throw new InvalidPipeArgumentError(UpperCasePipe, value);
|
|
||||||
}
|
|
||||||
return value.toUpperCase();
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,13 +7,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
||||||
|
import {NumberWrapper} from '../facade/lang';
|
||||||
import {NumberWrapper, isDate} from '../facade/lang';
|
|
||||||
|
|
||||||
import {DateFormatter} from './intl';
|
import {DateFormatter} from './intl';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
|
const ISO8601_DATE_REGEX =
|
||||||
|
/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
|
||||||
|
// 1 2 3 4 5 6 7 8 9 10 11
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngModule CommonModule
|
* @ngModule CommonModule
|
||||||
@ -24,7 +24,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
* Where:
|
* Where:
|
||||||
* - `expression` is a date object or a number (milliseconds since UTC epoch) or an ISO string
|
* - `expression` is a date object or a number (milliseconds since UTC epoch) or an ISO string
|
||||||
* (https://www.w3.org/TR/NOTE-datetime).
|
* (https://www.w3.org/TR/NOTE-datetime).
|
||||||
* - `format` indicates which date/time components to include. The format can be predifined as
|
* - `format` indicates which date/time components to include. The format can be predefined as
|
||||||
* shown below or custom as shown in the table.
|
* shown below or custom as shown in the table.
|
||||||
* - `'medium'`: equivalent to `'yMMMdjms'` (e.g. `Sep 3, 2010, 12:05:08 PM` for `en-US`)
|
* - `'medium'`: equivalent to `'yMMMdjms'` (e.g. `Sep 3, 2010, 12:05:08 PM` for `en-US`)
|
||||||
* - `'short'`: equivalent to `'yMdjm'` (e.g. `9/3/2010, 12:05 PM` for `en-US`)
|
* - `'short'`: equivalent to `'yMdjm'` (e.g. `9/3/2010, 12:05 PM` for `en-US`)
|
||||||
@ -103,7 +103,7 @@ export class DatePipe implements PipeTransform {
|
|||||||
transform(value: any, pattern: string = 'mediumDate'): string {
|
transform(value: any, pattern: string = 'mediumDate'): string {
|
||||||
let date: Date;
|
let date: Date;
|
||||||
|
|
||||||
if (isBlank(value)) return null;
|
if (isBlank(value) || value !== value) return null;
|
||||||
|
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
value = value.trim();
|
value = value.trim();
|
||||||
@ -130,7 +130,12 @@ export class DatePipe implements PipeTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isDate(date)) {
|
if (!isDate(date)) {
|
||||||
throw new InvalidPipeArgumentError(DatePipe, value);
|
let match: RegExpMatchArray;
|
||||||
|
if ((typeof value === 'string') && (match = value.match(ISO8601_DATE_REGEX))) {
|
||||||
|
date = isoStringToDate(match);
|
||||||
|
} else {
|
||||||
|
throw new InvalidPipeArgumentError(DatePipe, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return DateFormatter.format(date, this._locale, DatePipe._ALIASES[pattern] || pattern);
|
return DateFormatter.format(date, this._locale, DatePipe._ALIASES[pattern] || pattern);
|
||||||
@ -140,3 +145,31 @@ export class DatePipe implements PipeTransform {
|
|||||||
function isBlank(obj: any): boolean {
|
function isBlank(obj: any): boolean {
|
||||||
return obj == null || obj === '';
|
return obj == null || obj === '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isDate(obj: any): obj is Date {
|
||||||
|
return obj instanceof Date && !isNaN(obj.valueOf());
|
||||||
|
}
|
||||||
|
|
||||||
|
function isoStringToDate(match: RegExpMatchArray): Date {
|
||||||
|
const date = new Date(0);
|
||||||
|
let tzHour = 0;
|
||||||
|
let tzMin = 0;
|
||||||
|
const dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear;
|
||||||
|
const timeSetter = match[8] ? date.setUTCHours : date.setHours;
|
||||||
|
|
||||||
|
if (match[9]) {
|
||||||
|
tzHour = toInt(match[9] + match[10]);
|
||||||
|
tzMin = toInt(match[9] + match[11]);
|
||||||
|
}
|
||||||
|
dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
|
||||||
|
const h = toInt(match[4] || '0') - tzHour;
|
||||||
|
const m = toInt(match[5] || '0') - tzMin;
|
||||||
|
const s = toInt(match[6] || '0');
|
||||||
|
const ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
|
||||||
|
timeSetter.call(date, h, m, s, ms);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toInt(str: string): number {
|
||||||
|
return parseInt(str, 10);
|
||||||
|
}
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
import {isBlank} from '../facade/lang';
|
|
||||||
import {NgLocalization, getPluralCategory} from '../localization';
|
import {NgLocalization, getPluralCategory} from '../localization';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
@ -35,7 +34,7 @@ export class I18nPluralPipe implements PipeTransform {
|
|||||||
constructor(private _localization: NgLocalization) {}
|
constructor(private _localization: NgLocalization) {}
|
||||||
|
|
||||||
transform(value: number, pluralMap: {[count: string]: string}): string {
|
transform(value: number, pluralMap: {[count: string]: string}): string {
|
||||||
if (isBlank(value)) return '';
|
if (value == null) return '';
|
||||||
|
|
||||||
if (typeof pluralMap !== 'object' || pluralMap === null) {
|
if (typeof pluralMap !== 'object' || pluralMap === null) {
|
||||||
throw new InvalidPipeArgumentError(I18nPluralPipe, pluralMap);
|
throw new InvalidPipeArgumentError(I18nPluralPipe, pluralMap);
|
||||||
|
@ -12,13 +12,14 @@
|
|||||||
* This module provides a set of common Pipes.
|
* This module provides a set of common Pipes.
|
||||||
*/
|
*/
|
||||||
import {AsyncPipe} from './async_pipe';
|
import {AsyncPipe} from './async_pipe';
|
||||||
import {LowerCasePipe, TitleCasePipe, UpperCasePipe} from './case_conversion_pipes';
|
|
||||||
import {DatePipe} from './date_pipe';
|
import {DatePipe} from './date_pipe';
|
||||||
import {I18nPluralPipe} from './i18n_plural_pipe';
|
import {I18nPluralPipe} from './i18n_plural_pipe';
|
||||||
import {I18nSelectPipe} from './i18n_select_pipe';
|
import {I18nSelectPipe} from './i18n_select_pipe';
|
||||||
import {JsonPipe} from './json_pipe';
|
import {JsonPipe} from './json_pipe';
|
||||||
|
import {LowerCasePipe} from './lowercase_pipe';
|
||||||
import {CurrencyPipe, DecimalPipe, PercentPipe} from './number_pipe';
|
import {CurrencyPipe, DecimalPipe, PercentPipe} from './number_pipe';
|
||||||
import {SlicePipe} from './slice_pipe';
|
import {SlicePipe} from './slice_pipe';
|
||||||
|
import {UpperCasePipe} from './uppercase_pipe';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
AsyncPipe,
|
AsyncPipe,
|
||||||
@ -31,11 +32,9 @@ export {
|
|||||||
LowerCasePipe,
|
LowerCasePipe,
|
||||||
PercentPipe,
|
PercentPipe,
|
||||||
SlicePipe,
|
SlicePipe,
|
||||||
TitleCasePipe,
|
|
||||||
UpperCasePipe
|
UpperCasePipe
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A collection of Angular pipes that are likely to be used in each and every application.
|
* A collection of Angular pipes that are likely to be used in each and every application.
|
||||||
*/
|
*/
|
||||||
@ -47,7 +46,6 @@ export const COMMON_PIPES = [
|
|||||||
SlicePipe,
|
SlicePipe,
|
||||||
DecimalPipe,
|
DecimalPipe,
|
||||||
PercentPipe,
|
PercentPipe,
|
||||||
TitleCasePipe,
|
|
||||||
CurrencyPipe,
|
CurrencyPipe,
|
||||||
DatePipe,
|
DatePipe,
|
||||||
I18nPluralPipe,
|
I18nPluralPipe,
|
||||||
|
37
modules/@angular/common/src/pipes/lowercase_pipe.ts
Normal file
37
modules/@angular/common/src/pipes/lowercase_pipe.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* @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 {Pipe, PipeTransform} from '@angular/core';
|
||||||
|
import {isBlank} from '../facade/lang';
|
||||||
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Transforms string to lowercase.
|
||||||
|
* @howToUse `expression | lowercase`
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Converts value into a lowercase string using `String.prototype.toLowerCase()`.
|
||||||
|
*
|
||||||
|
* ### Example
|
||||||
|
*
|
||||||
|
* {@example common/pipes/ts/lowerupper_pipe.ts region='LowerUpperPipe'}
|
||||||
|
*
|
||||||
|
* @stable
|
||||||
|
*/
|
||||||
|
@Pipe({name: 'lowercase'})
|
||||||
|
export class LowerCasePipe implements PipeTransform {
|
||||||
|
transform(value: string): string {
|
||||||
|
if (isBlank(value)) return value;
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
throw new InvalidPipeArgumentError(LowerCasePipe, value);
|
||||||
|
}
|
||||||
|
return value.toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
|
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
|
||||||
|
|
||||||
import {NumberWrapper, isBlank, isPresent} from '../facade/lang';
|
import {NumberWrapper} from '../facade/lang';
|
||||||
|
|
||||||
import {NumberFormatStyle, NumberFormatter} from './intl';
|
import {NumberFormatStyle, NumberFormatter} from './intl';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
@ -18,7 +18,7 @@ const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
|
|||||||
function formatNumber(
|
function formatNumber(
|
||||||
pipe: Type<any>, locale: string, value: number | string, style: NumberFormatStyle,
|
pipe: Type<any>, locale: string, value: number | string, style: NumberFormatStyle,
|
||||||
digits: string, currency: string = null, currencyAsSymbol: boolean = false): string {
|
digits: string, currency: string = null, currencyAsSymbol: boolean = false): string {
|
||||||
if (isBlank(value)) return null;
|
if (value == null) return null;
|
||||||
|
|
||||||
// Convert strings to numbers
|
// Convert strings to numbers
|
||||||
value = typeof value === 'string' && NumberWrapper.isNumeric(value) ? +value : value;
|
value = typeof value === 'string' && NumberWrapper.isNumeric(value) ? +value : value;
|
||||||
@ -41,13 +41,13 @@ function formatNumber(
|
|||||||
if (parts === null) {
|
if (parts === null) {
|
||||||
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
||||||
}
|
}
|
||||||
if (isPresent(parts[1])) { // min integer digits
|
if (parts[1] != null) { // min integer digits
|
||||||
minInt = NumberWrapper.parseIntAutoRadix(parts[1]);
|
minInt = NumberWrapper.parseIntAutoRadix(parts[1]);
|
||||||
}
|
}
|
||||||
if (isPresent(parts[3])) { // min fraction digits
|
if (parts[3] != null) { // min fraction digits
|
||||||
minFraction = NumberWrapper.parseIntAutoRadix(parts[3]);
|
minFraction = NumberWrapper.parseIntAutoRadix(parts[3]);
|
||||||
}
|
}
|
||||||
if (isPresent(parts[5])) { // max fraction digits
|
if (parts[5] != null) { // max fraction digits
|
||||||
maxFraction = NumberWrapper.parseIntAutoRadix(parts[5]);
|
maxFraction = NumberWrapper.parseIntAutoRadix(parts[5]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
import {isBlank} from '../facade/lang';
|
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,7 +57,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
@Pipe({name: 'slice', pure: false})
|
@Pipe({name: 'slice', pure: false})
|
||||||
export class SlicePipe implements PipeTransform {
|
export class SlicePipe implements PipeTransform {
|
||||||
transform(value: any, start: number, end?: number): any {
|
transform(value: any, start: number, end?: number): any {
|
||||||
if (isBlank(value)) return value;
|
if (value == null) return value;
|
||||||
|
|
||||||
if (!this.supports(value)) {
|
if (!this.supports(value)) {
|
||||||
throw new InvalidPipeArgumentError(SlicePipe, value);
|
throw new InvalidPipeArgumentError(SlicePipe, value);
|
||||||
|
36
modules/@angular/common/src/pipes/uppercase_pipe.ts
Normal file
36
modules/@angular/common/src/pipes/uppercase_pipe.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* @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 {Pipe, PipeTransform} from '@angular/core';
|
||||||
|
import {isBlank} from '../facade/lang';
|
||||||
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Transforms string to uppercase.
|
||||||
|
* @howToUse `expression | uppercase`
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Converts value into an uppercase string using `String.prototype.toUpperCase()`.
|
||||||
|
*
|
||||||
|
* ### Example
|
||||||
|
*
|
||||||
|
* {@example common/pipes/ts/lowerupper_pipe.ts region='LowerUpperPipe'}
|
||||||
|
*
|
||||||
|
* @stable
|
||||||
|
*/
|
||||||
|
@Pipe({name: 'uppercase'})
|
||||||
|
export class UpperCasePipe implements PipeTransform {
|
||||||
|
transform(value: string): string {
|
||||||
|
if (isBlank(value)) return value;
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
throw new InvalidPipeArgumentError(UpperCasePipe, value);
|
||||||
|
}
|
||||||
|
return value.toUpperCase();
|
||||||
|
}
|
||||||
|
}
|
@ -9,3 +9,4 @@
|
|||||||
import {__core_private__ as r} from '@angular/core';
|
import {__core_private__ as r} from '@angular/core';
|
||||||
|
|
||||||
export const isPromise: typeof r.isPromise = r.isPromise;
|
export const isPromise: typeof r.isPromise = r.isPromise;
|
||||||
|
export const isObservable: typeof r.isObservable = r.isObservable;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {Component, ContentChild, TemplateRef} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
@ -29,10 +29,7 @@ export function main() {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [
|
declarations: [TestComponent],
|
||||||
TestComponent,
|
|
||||||
ComponentUsingTestComponent,
|
|
||||||
],
|
|
||||||
imports: [CommonModule],
|
imports: [CommonModule],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -77,7 +74,7 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should iterate over an array of objects', async(() => {
|
it('should iterate over an array of objects', async(() => {
|
||||||
const template = '<ul><li template="ngFor let item of items">{{item["name"]}};</li></ul>';
|
const template = '<ul><li *ngFor="let item of items">{{item["name"]}};</li></ul>';
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
// INIT
|
// INIT
|
||||||
@ -95,7 +92,7 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should gracefully handle nulls', async(() => {
|
it('should gracefully handle nulls', async(() => {
|
||||||
const template = '<ul><li template="ngFor let item of null">{{item}};</li></ul>';
|
const template = '<ul><li *ngFor="let item of null">{{item}};</li></ul>';
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
detectChangesAndExpectText('');
|
detectChangesAndExpectText('');
|
||||||
@ -140,12 +137,8 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should repeat over nested arrays', async(() => {
|
it('should repeat over nested arrays', async(() => {
|
||||||
const template = '<div>' +
|
const template = '<div *ngFor="let item of items">' +
|
||||||
'<div template="ngFor let item of items">' +
|
'<div *ngFor="let subitem of item">{{subitem}}-{{item.length}};</div>|' +
|
||||||
'<div template="ngFor let subitem of item">' +
|
|
||||||
'{{subitem}}-{{item.length}};' +
|
|
||||||
'</div>|' +
|
|
||||||
'</div>' +
|
|
||||||
'</div>';
|
'</div>';
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
@ -157,10 +150,9 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should repeat over nested arrays with no intermediate element', async(() => {
|
it('should repeat over nested arrays with no intermediate element', async(() => {
|
||||||
const template = '<div><template ngFor let-item [ngForOf]="items">' +
|
const template = '<div *ngFor="let item of items">' +
|
||||||
'<div template="ngFor let subitem of item">' +
|
'<div *ngFor="let subitem of item">{{subitem}}-{{item.length}};</div>' +
|
||||||
'{{subitem}}-{{item.length}};' +
|
'</div>';
|
||||||
'</div></template></div>';
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
getComponent().items = [['a', 'b'], ['c']];
|
getComponent().items = [['a', 'b'], ['c']];
|
||||||
@ -170,10 +162,11 @@ export function main() {
|
|||||||
detectChangesAndExpectText('e-1;f-2;g-2;');
|
detectChangesAndExpectText('e-1;f-2;g-2;');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should repeat over nested ngIf that are the last node in the ngFor temlate', async(() => {
|
it('should repeat over nested ngIf that are the last node in the ngFor template', async(() => {
|
||||||
const template =
|
const template = `<div *ngFor="let item of items; let i=index">` +
|
||||||
`<div><template ngFor let-item [ngForOf]="items" let-i="index"><div>{{i}}|</div>` +
|
`<div>{{i}}|</div>` +
|
||||||
`<div *ngIf="i % 2 == 0">even|</div></template></div>`;
|
`<div *ngIf="i % 2 == 0">even|</div>` +
|
||||||
|
`</div>`;
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
@ -189,8 +182,7 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should display indices correctly', async(() => {
|
it('should display indices correctly', async(() => {
|
||||||
const template =
|
const template = '<span *ngFor ="let item of items; let i=index">{{i.toString()}}</span>';
|
||||||
'<div><span template="ngFor: let item of items; let i=index">{{i.toString()}}</span></div>';
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
getComponent().items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
getComponent().items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||||
@ -202,7 +194,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should display first item correctly', async(() => {
|
it('should display first item correctly', async(() => {
|
||||||
const template =
|
const template =
|
||||||
'<div><span template="ngFor: let item of items; let isFirst=first">{{isFirst.toString()}}</span></div>';
|
'<span *ngFor="let item of items; let isFirst=first">{{isFirst.toString()}}</span>';
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
getComponent().items = [0, 1, 2];
|
getComponent().items = [0, 1, 2];
|
||||||
@ -214,7 +206,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should display last item correctly', async(() => {
|
it('should display last item correctly', async(() => {
|
||||||
const template =
|
const template =
|
||||||
'<div><span template="ngFor: let item of items; let isLast=last">{{isLast.toString()}}</span></div>';
|
'<span *ngFor="let item of items; let isLast=last">{{isLast.toString()}}</span>';
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
getComponent().items = [0, 1, 2];
|
getComponent().items = [0, 1, 2];
|
||||||
@ -226,7 +218,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should display even items correctly', async(() => {
|
it('should display even items correctly', async(() => {
|
||||||
const template =
|
const template =
|
||||||
'<div><span template="ngFor: let item of items; let isEven=even">{{isEven.toString()}}</span></div>';
|
'<span *ngFor="let item of items; let isEven=even">{{isEven.toString()}}</span>';
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
getComponent().items = [0, 1, 2];
|
getComponent().items = [0, 1, 2];
|
||||||
@ -238,7 +230,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should display odd items correctly', async(() => {
|
it('should display odd items correctly', async(() => {
|
||||||
const template =
|
const template =
|
||||||
'<div><span template="ngFor: let item of items; let isOdd=odd">{{isOdd.toString()}}</span></div>';
|
'<span *ngFor="let item of items; let isOdd=odd">{{isOdd.toString()}}</span>';
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
getComponent().items = [0, 1, 2, 3];
|
getComponent().items = [0, 1, 2, 3];
|
||||||
@ -249,55 +241,38 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should allow to use a custom template', async(() => {
|
it('should allow to use a custom template', async(() => {
|
||||||
const tcTemplate =
|
const template =
|
||||||
'<ul><template ngFor [ngForOf]="items" [ngForTemplate]="contentTpl"></template></ul>';
|
'<ng-container *ngFor="let item of items; template: tpl"></ng-container>' +
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: tcTemplate}});
|
'<template let-item let-i="index" #tpl><p>{{i}}: {{item}};</p></template>';
|
||||||
const cutTemplate =
|
fixture = createTestComponent(template);
|
||||||
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
|
getComponent().items = ['a', 'b', 'c'];
|
||||||
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
|
|
||||||
fixture = TestBed.createComponent(ComponentUsingTestComponent);
|
|
||||||
|
|
||||||
const testComponent = fixture.debugElement.children[0];
|
|
||||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
|
detectChangesAndExpectText('0: a;1: b;2: c;');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should use a default template if a custom one is null', async(() => {
|
it('should use a default template if a custom one is null', async(() => {
|
||||||
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
|
const template =
|
||||||
[ngForTemplate]="contentTpl" let-i="index">{{i}}: {{item}};</template></ul>`;
|
`<ul><ng-container *ngFor="let item of items; template: null; let i=index">{{i}}: {{item}};</ng-container></ul>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: testTemplate}});
|
fixture = createTestComponent(template);
|
||||||
const cutTemplate =
|
getComponent().items = ['a', 'b', 'c'];
|
||||||
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
|
|
||||||
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
|
|
||||||
fixture = TestBed.createComponent(ComponentUsingTestComponent);
|
|
||||||
|
|
||||||
const testComponent = fixture.debugElement.children[0];
|
|
||||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
|
detectChangesAndExpectText('0: a;1: b;2: c;');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should use a custom template when both default and a custom one are present', async(() => {
|
it('should use a custom template when both default and a custom one are present', async(() => {
|
||||||
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
|
const template =
|
||||||
[ngForTemplate]="contentTpl" let-i="index">{{i}}=> {{item}};</template></ul>`;
|
'<ng-container *ngFor="let item of items; template: tpl">{{i}};</ng-container>' +
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: testTemplate}});
|
'<template let-item let-i="index" #tpl>{{i}}: {{item}};</template>';
|
||||||
const cutTemplate =
|
fixture = createTestComponent(template);
|
||||||
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
|
getComponent().items = ['a', 'b', 'c'];
|
||||||
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
|
|
||||||
fixture = TestBed.createComponent(ComponentUsingTestComponent);
|
|
||||||
|
|
||||||
const testComponent = fixture.debugElement.children[0];
|
|
||||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
|
detectChangesAndExpectText('0: a;1: b;2: c;');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('track by', () => {
|
describe('track by', () => {
|
||||||
it('should console.warn if trackBy is not a function', async(() => {
|
it('should console.warn if trackBy is not a function', async(() => {
|
||||||
// TODO(vicb): expect a warning message when we have a proper log service
|
// TODO(vicb): expect a warning message when we have a proper log service
|
||||||
const template =
|
const template = `<p *ngFor="let item of items; trackBy: value"></p>`;
|
||||||
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="value"></template>`;
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
fixture.componentInstance.value = 0;
|
fixture.componentInstance.value = 0;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@ -305,8 +280,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should track by identity when trackBy is to `null` or `undefined`', async(() => {
|
it('should track by identity when trackBy is to `null` or `undefined`', async(() => {
|
||||||
// TODO(vicb): expect no warning message when we have a proper log service
|
// TODO(vicb): expect no warning message when we have a proper log service
|
||||||
const template =
|
const template = `<p *ngFor="let item of items; trackBy: value">{{ item }}</p>`;
|
||||||
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="value">{{ item }}</template>`;
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
fixture.componentInstance.items = ['a', 'b', 'c'];
|
fixture.componentInstance.items = ['a', 'b', 'c'];
|
||||||
fixture.componentInstance.value = null;
|
fixture.componentInstance.value = null;
|
||||||
@ -317,7 +291,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should set the context to the component instance', async(() => {
|
it('should set the context to the component instance', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByContext.bind(this)"></template>`;
|
`<p *ngFor="let item of items; trackBy: trackByContext.bind(this)"></p>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
thisArg = null;
|
thisArg = null;
|
||||||
@ -327,9 +301,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should not replace tracked items', async(() => {
|
it('should not replace tracked items', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById" let-i="index">
|
`<p *ngFor="let item of items; trackBy: trackById; let i=index">{{items[i]}}</p>`;
|
||||||
<p>{{items[i]}}</p>
|
|
||||||
</template>`;
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
const buildItemList = () => {
|
const buildItemList = () => {
|
||||||
@ -345,7 +317,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should update implicit local variable on view', async(() => {
|
it('should update implicit local variable on view', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
`<div *ngFor="let item of items; trackBy: trackById">{{item['color']}}</div>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
getComponent().items = [{'id': 'a', 'color': 'blue'}];
|
getComponent().items = [{'id': 'a', 'color': 'blue'}];
|
||||||
@ -357,7 +329,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should move items around and keep them updated ', async(() => {
|
it('should move items around and keep them updated ', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
`<div *ngFor="let item of items; trackBy: trackById">{{item['color']}}</div>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
getComponent().items = [{'id': 'a', 'color': 'blue'}, {'id': 'b', 'color': 'yellow'}];
|
getComponent().items = [{'id': 'a', 'color': 'blue'}, {'id': 'b', 'color': 'yellow'}];
|
||||||
@ -368,8 +340,7 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should handle added and removed items properly when tracking by index', async(() => {
|
it('should handle added and removed items properly when tracking by index', async(() => {
|
||||||
const template =
|
const template = `<div *ngFor="let item of items; trackBy: trackByIndex">{{item}}</div>`;
|
||||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByIndex">{{item}}</template></div>`;
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
getComponent().items = ['a', 'b', 'c', 'd'];
|
getComponent().items = ['a', 'b', 'c', 'd'];
|
||||||
@ -389,7 +360,6 @@ class Foo {
|
|||||||
|
|
||||||
@Component({selector: 'test-cmp', template: ''})
|
@Component({selector: 'test-cmp', template: ''})
|
||||||
class TestComponent {
|
class TestComponent {
|
||||||
@ContentChild(TemplateRef) contentTpl: TemplateRef<Object>;
|
|
||||||
value: any;
|
value: any;
|
||||||
items: any[] = [1, 2];
|
items: any[] = [1, 2];
|
||||||
trackById(index: number, item: any): string { return item['id']; }
|
trackById(index: number, item: any): string { return item['id']; }
|
||||||
@ -397,12 +367,7 @@ class TestComponent {
|
|||||||
trackByContext(): void { thisArg = this; }
|
trackByContext(): void { thisArg = this; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({selector: 'outer-cmp', template: ''})
|
const TEMPLATE = '<div><span *ngFor="let item of items">{{item.toString()}};</span></div>';
|
||||||
class ComponentUsingTestComponent {
|
|
||||||
items: any = [1, 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
const TEMPLATE = '<div><span template="ngFor let item of items">{{item.toString()}};</span></div>';
|
|
||||||
|
|
||||||
function createTestComponent(template: string = TEMPLATE): ComponentFixture<TestComponent> {
|
function createTestComponent(template: string = TEMPLATE): ComponentFixture<TestComponent> {
|
||||||
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
|
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
@ -28,203 +29,114 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should work in a template attribute', async(() => {
|
it('should work in a template attribute', async(() => {
|
||||||
const template = '<div><span template="ngIf booleanCondition">hello</span></div>';
|
const template = '<span *ngIf="booleanCondition">hello</span>';
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
|
||||||
expect(fixture.nativeElement).toHaveText('hello');
|
expect(fixture.nativeElement).toHaveText('hello');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should work in a template element', async(() => {
|
it('should work on a template element', async(() => {
|
||||||
const template =
|
const template = '<template [ngIf]="booleanCondition">hello2</template>';
|
||||||
'<div><template [ngIf]="booleanCondition"><span>hello2</span></template></div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
|
||||||
expect(fixture.nativeElement).toHaveText('hello2');
|
expect(fixture.nativeElement).toHaveText('hello2');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should toggle node when condition changes', async(() => {
|
it('should toggle node when condition changes', async(() => {
|
||||||
const template = '<div><span template="ngIf booleanCondition">hello</span></div>';
|
const template = '<span *ngIf="booleanCondition">hello</span>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
getComponent().booleanCondition = false;
|
getComponent().booleanCondition = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
|
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
|
||||||
expect(fixture.nativeElement).toHaveText('');
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
|
|
||||||
getComponent().booleanCondition = true;
|
getComponent().booleanCondition = true;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
|
||||||
expect(fixture.nativeElement).toHaveText('hello');
|
expect(fixture.nativeElement).toHaveText('hello');
|
||||||
|
|
||||||
getComponent().booleanCondition = false;
|
getComponent().booleanCondition = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
|
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
|
||||||
expect(fixture.nativeElement).toHaveText('');
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should handle nested if correctly', async(() => {
|
it('should handle nested if correctly', async(() => {
|
||||||
const template =
|
const template =
|
||||||
'<div><template [ngIf]="booleanCondition"><span *ngIf="nestedBooleanCondition">hello</span></template></div>';
|
'<div *ngIf="booleanCondition"><span *ngIf="nestedBooleanCondition">hello</span></div>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
getComponent().booleanCondition = false;
|
getComponent().booleanCondition = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
|
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
|
||||||
expect(fixture.nativeElement).toHaveText('');
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
|
|
||||||
getComponent().booleanCondition = true;
|
getComponent().booleanCondition = true;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
|
||||||
expect(fixture.nativeElement).toHaveText('hello');
|
expect(fixture.nativeElement).toHaveText('hello');
|
||||||
|
|
||||||
getComponent().nestedBooleanCondition = false;
|
getComponent().nestedBooleanCondition = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
|
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
|
||||||
expect(fixture.nativeElement).toHaveText('');
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
|
|
||||||
getComponent().nestedBooleanCondition = true;
|
getComponent().nestedBooleanCondition = true;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
|
||||||
expect(fixture.nativeElement).toHaveText('hello');
|
expect(fixture.nativeElement).toHaveText('hello');
|
||||||
|
|
||||||
getComponent().booleanCondition = false;
|
getComponent().booleanCondition = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
|
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
|
||||||
expect(fixture.nativeElement).toHaveText('');
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should update several nodes with if', async(() => {
|
it('should update several nodes with if', async(() => {
|
||||||
const template = '<div>' +
|
const template = '<span *ngIf="numberCondition + 1 >= 2">helloNumber</span>' +
|
||||||
'<span template="ngIf numberCondition + 1 >= 2">helloNumber</span>' +
|
'<span *ngIf="stringCondition == \'foo\'">helloString</span>' +
|
||||||
'<span template="ngIf stringCondition == \'foo\'">helloString</span>' +
|
'<span *ngIf="functionCondition(stringCondition, numberCondition)">helloFunction</span>';
|
||||||
'<span template="ngIf functionCondition(stringCondition, numberCondition)">helloFunction</span>' +
|
|
||||||
'</div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(3);
|
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(3);
|
||||||
expect(getDOM().getText(fixture.nativeElement))
|
expect(getDOM().getText(fixture.nativeElement))
|
||||||
.toEqual('helloNumberhelloStringhelloFunction');
|
.toEqual('helloNumberhelloStringhelloFunction');
|
||||||
|
|
||||||
getComponent().numberCondition = 0;
|
getComponent().numberCondition = 0;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
|
||||||
expect(fixture.nativeElement).toHaveText('helloString');
|
expect(fixture.nativeElement).toHaveText('helloString');
|
||||||
|
|
||||||
getComponent().numberCondition = 1;
|
getComponent().numberCondition = 1;
|
||||||
getComponent().stringCondition = 'bar';
|
getComponent().stringCondition = 'bar';
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
|
||||||
expect(fixture.nativeElement).toHaveText('helloNumber');
|
expect(fixture.nativeElement).toHaveText('helloNumber');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should not add the element twice if the condition goes from true to true (JS)',
|
it('should not add the element twice if the condition goes from truthy to truthy', async(() => {
|
||||||
async(() => {
|
const template = '<span *ngIf="numberCondition">hello</span>';
|
||||||
const template = '<div><span template="ngIf numberCondition">hello</span></div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
let els = fixture.debugElement.queryAll(By.css('span'));
|
||||||
|
expect(els.length).toEqual(1);
|
||||||
|
getDOM().addClass(els[0].nativeElement, 'marker');
|
||||||
expect(fixture.nativeElement).toHaveText('hello');
|
expect(fixture.nativeElement).toHaveText('hello');
|
||||||
|
|
||||||
getComponent().numberCondition = 2;
|
getComponent().numberCondition = 2;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
els = fixture.debugElement.queryAll(By.css('span'));
|
||||||
|
expect(els.length).toEqual(1);
|
||||||
|
expect(getDOM().hasClass(els[0].nativeElement, 'marker')).toBe(true);
|
||||||
|
|
||||||
expect(fixture.nativeElement).toHaveText('hello');
|
expect(fixture.nativeElement).toHaveText('hello');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should not recreate the element if the condition goes from true to true (JS)', async(() => {
|
|
||||||
const template = '<div><span template="ngIf numberCondition">hello</span></div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
getDOM().addClass(getDOM().querySelector(fixture.nativeElement, 'span'), 'foo');
|
|
||||||
|
|
||||||
getComponent().numberCondition = 2;
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getDOM().hasClass(getDOM().querySelector(fixture.nativeElement, 'span'), 'foo'))
|
|
||||||
.toBe(true);
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('else', () => {
|
|
||||||
it('should support else', async(() => {
|
|
||||||
const template = '<div>' +
|
|
||||||
'<span *ngIf="booleanCondition; else elseBlock">TRUE</span>' +
|
|
||||||
'<template #elseBlock>FALSE</template>' +
|
|
||||||
'</div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.nativeElement).toHaveText('TRUE');
|
|
||||||
|
|
||||||
getComponent().booleanCondition = false;
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.nativeElement).toHaveText('FALSE');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should support then and else', async(() => {
|
|
||||||
const template = '<div>' +
|
|
||||||
'<span *ngIf="booleanCondition; then thenBlock; else elseBlock">IGNORE</span>' +
|
|
||||||
'<template #thenBlock>THEN</template>' +
|
|
||||||
'<template #elseBlock>ELSE</template>' +
|
|
||||||
'</div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.nativeElement).toHaveText('THEN');
|
|
||||||
|
|
||||||
getComponent().booleanCondition = false;
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.nativeElement).toHaveText('ELSE');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should support dynamic else', async(() => {
|
|
||||||
const template = '<div>' +
|
|
||||||
'<span *ngIf="booleanCondition; else nestedBooleanCondition ? b1 : b2">TRUE</span>' +
|
|
||||||
'<template #b1>FALSE1</template>' +
|
|
||||||
'<template #b2>FALSE2</template>' +
|
|
||||||
'</div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.nativeElement).toHaveText('TRUE');
|
|
||||||
|
|
||||||
getComponent().booleanCondition = false;
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.nativeElement).toHaveText('FALSE1');
|
|
||||||
|
|
||||||
getComponent().nestedBooleanCondition = false;
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.nativeElement).toHaveText('FALSE2');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should support binding to variable', async(() => {
|
|
||||||
const template = '<div>' +
|
|
||||||
'<span *ngIf="booleanCondition; else elseBlock; let v">{{v}}</span>' +
|
|
||||||
'<template #elseBlock let-v>{{v}}</template>' +
|
|
||||||
'</div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.nativeElement).toHaveText('true');
|
|
||||||
|
|
||||||
getComponent().booleanCondition = false;
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.nativeElement).toHaveText('false');
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
|||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('switch', () => {
|
describe('ngPlural', () => {
|
||||||
let fixture: ComponentFixture<any>;
|
let fixture: ComponentFixture<any>;
|
||||||
|
|
||||||
function getComponent(): TestComponent { return fixture.componentInstance; }
|
function getComponent(): TestComponent { return fixture.componentInstance; }
|
||||||
@ -33,10 +33,25 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should display the template according to the exact value', async(() => {
|
it('should display the template according to the exact value', async(() => {
|
||||||
const template = '<div>' +
|
const template = '<ul [ngPlural]="switchValue">' +
|
||||||
'<ul [ngPlural]="switchValue">' +
|
|
||||||
'<template ngPluralCase="=0"><li>you have no messages.</li></template>' +
|
'<template ngPluralCase="=0"><li>you have no messages.</li></template>' +
|
||||||
'<template ngPluralCase="=1"><li>you have one message.</li></template>' +
|
'<template ngPluralCase="=1"><li>you have one message.</li></template>' +
|
||||||
|
'</ul>';
|
||||||
|
|
||||||
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
|
getComponent().switchValue = 0;
|
||||||
|
detectChangesAndExpectText('you have no messages.');
|
||||||
|
|
||||||
|
getComponent().switchValue = 1;
|
||||||
|
detectChangesAndExpectText('you have one message.');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should display the template according to the exact numeric value', async(() => {
|
||||||
|
const template = '<div>' +
|
||||||
|
'<ul [ngPlural]="switchValue">' +
|
||||||
|
'<template ngPluralCase="0"><li>you have no messages.</li></template>' +
|
||||||
|
'<template ngPluralCase="1"><li>you have one message.</li></template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
@ -51,10 +66,9 @@ export function main() {
|
|||||||
// https://github.com/angular/angular/issues/9868
|
// https://github.com/angular/angular/issues/9868
|
||||||
// https://github.com/angular/angular/issues/9882
|
// https://github.com/angular/angular/issues/9882
|
||||||
it('should not throw when ngPluralCase contains expressions', async(() => {
|
it('should not throw when ngPluralCase contains expressions', async(() => {
|
||||||
const template = '<div>' +
|
const template = '<ul [ngPlural]="switchValue">' +
|
||||||
'<ul [ngPlural]="switchValue">' +
|
|
||||||
'<template ngPluralCase="=0"><li>{{ switchValue }}</li></template>' +
|
'<template ngPluralCase="=0"><li>{{ switchValue }}</li></template>' +
|
||||||
'</ul></div>';
|
'</ul>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
@ -64,11 +78,10 @@ export function main() {
|
|||||||
|
|
||||||
|
|
||||||
it('should be applicable to <ng-container> elements', async(() => {
|
it('should be applicable to <ng-container> elements', async(() => {
|
||||||
const template = '<div>' +
|
const template = '<ng-container [ngPlural]="switchValue">' +
|
||||||
'<ng-container [ngPlural]="switchValue">' +
|
|
||||||
'<template ngPluralCase="=0">you have no messages.</template>' +
|
'<template ngPluralCase="=0">you have no messages.</template>' +
|
||||||
'<template ngPluralCase="=1">you have one message.</template>' +
|
'<template ngPluralCase="=1">you have one message.</template>' +
|
||||||
'</ng-container></div>';
|
'</ng-container>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
@ -80,11 +93,10 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should display the template according to the category', async(() => {
|
it('should display the template according to the category', async(() => {
|
||||||
const template = '<div>' +
|
const template = '<ul [ngPlural]="switchValue">' +
|
||||||
'<ul [ngPlural]="switchValue">' +
|
|
||||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||||
'<template ngPluralCase="many"><li>you have many messages.</li></template>' +
|
'<template ngPluralCase="many"><li>you have many messages.</li></template>' +
|
||||||
'</ul></div>';
|
'</ul>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
@ -96,11 +108,10 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should default to other when no matches are found', async(() => {
|
it('should default to other when no matches are found', async(() => {
|
||||||
const template = '<div>' +
|
const template = '<ul [ngPlural]="switchValue">' +
|
||||||
'<ul [ngPlural]="switchValue">' +
|
|
||||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||||
'<template ngPluralCase="other"><li>default message.</li></template>' +
|
'<template ngPluralCase="other"><li>default message.</li></template>' +
|
||||||
'</ul></div>';
|
'</ul>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
@ -109,11 +120,10 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should prioritize value matches over category matches', async(() => {
|
it('should prioritize value matches over category matches', async(() => {
|
||||||
const template = '<div>' +
|
const template = '<ul [ngPlural]="switchValue">' +
|
||||||
'<ul [ngPlural]="switchValue">' +
|
|
||||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||||
'<template ngPluralCase="=2">you have two messages.</template>' +
|
'<template ngPluralCase="=2">you have two messages.</template>' +
|
||||||
'</ul></div>';
|
'</ul>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
|
@ -29,22 +29,19 @@ export function main() {
|
|||||||
it('should add styles specified in an object literal', async(() => {
|
it('should add styles specified in an object literal', async(() => {
|
||||||
const template = `<div [ngStyle]="{'max-width': '40px'}"></div>`;
|
const template = `<div [ngStyle]="{'max-width': '40px'}"></div>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should add and change styles specified in an object expression', async(() => {
|
it('should add and change styles specified in an object expression', async(() => {
|
||||||
const template = `<div [ngStyle]="expr"></div>`;
|
const template = `<div [ngStyle]="expr"></div>`;
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
let expr: {[k: string]: string};
|
|
||||||
|
|
||||||
getComponent().expr = {'max-width': '40px'};
|
getComponent().expr = {'max-width': '40px'};
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
||||||
|
|
||||||
expr = getComponent().expr;
|
let expr = getComponent().expr;
|
||||||
expr['max-width'] = '30%';
|
expr['max-width'] = '30%';
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '30%'});
|
expectNativeEl(fixture).toHaveCssStyle({'max-width': '30%'});
|
||||||
|
@ -33,11 +33,10 @@ export function main() {
|
|||||||
|
|
||||||
describe('switch value changes', () => {
|
describe('switch value changes', () => {
|
||||||
it('should switch amongst when values', () => {
|
it('should switch amongst when values', () => {
|
||||||
const template = '<div>' +
|
const template = '<ul [ngSwitch]="switchValue">' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<li *ngSwitchCase="\'a\'">when a</li>' +
|
||||||
'<template ngSwitchCase="a"><li>when a</li></template>' +
|
'<li *ngSwitchCase="\'b\'">when b</li>' +
|
||||||
'<template ngSwitchCase="b"><li>when b</li></template>' +
|
'</ul>';
|
||||||
'</ul></div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
@ -51,11 +50,10 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should switch amongst when values with fallback to default', () => {
|
it('should switch amongst when values with fallback to default', () => {
|
||||||
const template = '<div>' +
|
const template = '<ul [ngSwitch]="switchValue">' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<li *ngSwitchCase="\'a\'">when a</li>' +
|
||||||
'<li template="ngSwitchCase \'a\'">when a</li>' +
|
'<li *ngSwitchDefault>when default</li>' +
|
||||||
'<li template="ngSwitchDefault">when default</li>' +
|
'</ul>';
|
||||||
'</ul></div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
detectChangesAndExpectText('when default');
|
detectChangesAndExpectText('when default');
|
||||||
@ -71,15 +69,14 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should support multiple whens with the same value', () => {
|
it('should support multiple whens with the same value', () => {
|
||||||
const template = '<div>' +
|
const template = '<ul [ngSwitch]="switchValue">' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<li *ngSwitchCase="\'a\'">when a1;</li>' +
|
||||||
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
|
'<li *ngSwitchCase="\'b\'">when b1;</li>' +
|
||||||
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
|
'<li *ngSwitchCase="\'a\'">when a2;</li>' +
|
||||||
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
|
'<li *ngSwitchCase="\'b\'">when b2;</li>' +
|
||||||
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
|
'<li *ngSwitchDefault>when default1;</li>' +
|
||||||
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
'<li *ngSwitchDefault>when default2;</li>' +
|
||||||
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
'</ul>';
|
||||||
'</ul></div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
detectChangesAndExpectText('when default1;when default2;');
|
detectChangesAndExpectText('when default1;when default2;');
|
||||||
@ -94,12 +91,11 @@ export function main() {
|
|||||||
|
|
||||||
describe('when values changes', () => {
|
describe('when values changes', () => {
|
||||||
it('should switch amongst when values', () => {
|
it('should switch amongst when values', () => {
|
||||||
const template = '<div>' +
|
const template = '<ul [ngSwitch]="switchValue">' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<li *ngSwitchCase="when1">when 1;</li>' +
|
||||||
'<template [ngSwitchCase]="when1"><li>when 1;</li></template>' +
|
'<li *ngSwitchCase="when2">when 2;</li>' +
|
||||||
'<template [ngSwitchCase]="when2"><li>when 2;</li></template>' +
|
'<li *ngSwitchDefault>when default;</li>' +
|
||||||
'<template ngSwitchDefault><li>when default;</li></template>' +
|
'</ul>';
|
||||||
'</ul></div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
getComponent().when1 = 'a';
|
getComponent().when1 = 'a';
|
||||||
@ -148,11 +144,10 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should create the default case if there is no other case', () => {
|
it('should create the default case if there is no other case', () => {
|
||||||
const template = '<div>' +
|
const template = '<ul [ngSwitch]="switchValue">' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<li *ngSwitchDefault>when default1;</li>' +
|
||||||
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
'<li *ngSwitchDefault>when default2;</li>' +
|
||||||
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
'</ul>';
|
||||||
'</ul></div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
detectChangesAndExpectText('when default1;when default2;');
|
detectChangesAndExpectText('when default1;when default2;');
|
||||||
@ -160,15 +155,14 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should allow defaults before cases', () => {
|
it('should allow defaults before cases', () => {
|
||||||
const template = '<div>' +
|
const template = '<ul [ngSwitch]="switchValue">' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<li *ngSwitchDefault>when default1;</li>' +
|
||||||
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
'<li *ngSwitchDefault>when default2;</li>' +
|
||||||
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
'<li *ngSwitchCase="\'a\'">when a1;</li>' +
|
||||||
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
|
'<li *ngSwitchCase="\'b\'">when b1;</li>' +
|
||||||
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
|
'<li *ngSwitchCase="\'a\'">when a2;</li>' +
|
||||||
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
|
'<li *ngSwitchCase="\'b\'">when b2;</li>' +
|
||||||
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
|
'</ul>';
|
||||||
'</ul></div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
detectChangesAndExpectText('when default1;when default2;');
|
detectChangesAndExpectText('when default1;when default2;');
|
||||||
|
@ -34,29 +34,22 @@ export function main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should do nothing if templateRef is null', async(() => {
|
it('should do nothing if templateRef is `null`', async(() => {
|
||||||
const template = `<template [ngTemplateOutlet]="null"></template>`;
|
const template = `<ng-container [ngTemplateOutlet]="null"></ng-container>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
detectChangesAndExpectText('');
|
detectChangesAndExpectText('');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should insert content specified by TemplateRef', async(() => {
|
it('should insert content specified by TemplateRef', async(() => {
|
||||||
const template =
|
const template = `<template #tpl>foo</template>` +
|
||||||
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
`<ng-container [ngTemplateOutlet]="tpl"></ng-container>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
detectChangesAndExpectText('');
|
|
||||||
|
|
||||||
const refs = fixture.debugElement.children[0].references['refs'];
|
|
||||||
|
|
||||||
setTplRef(refs.tplRefs.first);
|
|
||||||
detectChangesAndExpectText('foo');
|
detectChangesAndExpectText('foo');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should clear content if TemplateRef becomes null', async(() => {
|
it('should clear content if TemplateRef becomes `null`', async(() => {
|
||||||
const template =
|
const template = `<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs>` +
|
||||||
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const refs = fixture.debugElement.children[0].references['refs'];
|
const refs = fixture.debugElement.children[0].references['refs'];
|
||||||
@ -70,7 +63,8 @@ export function main() {
|
|||||||
|
|
||||||
it('should swap content if TemplateRef changes', async(() => {
|
it('should swap content if TemplateRef changes', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<tpl-refs #refs="tplRefs"><template>foo</template><template>bar</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
`<tpl-refs #refs="tplRefs"><template>foo</template><template>bar</template></tpl-refs>` +
|
||||||
|
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@ -83,70 +77,47 @@ export function main() {
|
|||||||
detectChangesAndExpectText('bar');
|
detectChangesAndExpectText('bar');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should display template if context is null', async(() => {
|
it('should display template if context is `null`', async(() => {
|
||||||
const template =
|
const template = `<template #tpl>foo</template>` +
|
||||||
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="null"></template>`;
|
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="null"></ng-container>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
detectChangesAndExpectText('');
|
|
||||||
|
|
||||||
const refs = fixture.debugElement.children[0].references['refs'];
|
|
||||||
|
|
||||||
setTplRef(refs.tplRefs.first);
|
|
||||||
detectChangesAndExpectText('foo');
|
detectChangesAndExpectText('foo');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should reflect initial context and changes', async(() => {
|
it('should reflect initial context and changes', async(() => {
|
||||||
const template =
|
const template = `<template let-foo="foo" #tpl>{{foo}}</template>` +
|
||||||
`<tpl-refs #refs="tplRefs"><template let-foo="foo"><span>{{foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
|
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="context"></ng-container>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const refs = fixture.debugElement.children[0].references['refs'];
|
|
||||||
setTplRef(refs.tplRefs.first);
|
|
||||||
|
|
||||||
detectChangesAndExpectText('bar');
|
detectChangesAndExpectText('bar');
|
||||||
|
|
||||||
fixture.componentInstance.context.foo = 'alter-bar';
|
fixture.componentInstance.context.foo = 'alter-bar';
|
||||||
|
|
||||||
detectChangesAndExpectText('alter-bar');
|
detectChangesAndExpectText('alter-bar');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should reflect user defined $implicit property in the context', async(() => {
|
it('should reflect user defined `$implicit` property in the context', async(() => {
|
||||||
const template =
|
const template = `<template let-ctx #tpl>{{ctx.foo}}</template>` +
|
||||||
`<tpl-refs #refs="tplRefs"><template let-ctx><span>{{ctx.foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
|
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="context"></ng-container>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
fixture.componentInstance.context = {$implicit: {foo: 'bra'}};
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('bra');
|
||||||
|
|
||||||
const refs = fixture.debugElement.children[0].references['refs'];
|
|
||||||
setTplRef(refs.tplRefs.first);
|
|
||||||
|
|
||||||
fixture.componentInstance.context = {$implicit: fixture.componentInstance.context};
|
|
||||||
detectChangesAndExpectText('bar');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should reflect context re-binding', async(() => {
|
it('should reflect context re-binding', async(() => {
|
||||||
const template =
|
const template = `<template let-shawshank="shawshank" #tpl>{{shawshank}}</template>` +
|
||||||
`<tpl-refs #refs="tplRefs"><template let-shawshank="shawshank"><span>{{shawshank}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
|
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="context"></ng-container>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
const refs = fixture.debugElement.children[0].references['refs'];
|
|
||||||
setTplRef(refs.tplRefs.first);
|
|
||||||
fixture.componentInstance.context = {shawshank: 'brooks'};
|
fixture.componentInstance.context = {shawshank: 'brooks'};
|
||||||
|
|
||||||
detectChangesAndExpectText('brooks');
|
detectChangesAndExpectText('brooks');
|
||||||
|
|
||||||
fixture.componentInstance.context = {shawshank: 'was here'};
|
fixture.componentInstance.context = {shawshank: 'was here'};
|
||||||
|
|
||||||
detectChangesAndExpectText('was here');
|
detectChangesAndExpectText('was here');
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Directive({selector: 'tpl-refs', exportAs: 'tplRefs'})
|
@Directive({selector: 'tpl-refs', exportAs: 'tplRefs'})
|
||||||
class CaptureTplRefs {
|
class CaptureTplRefs {
|
||||||
@ContentChildren(TemplateRef) tplRefs: QueryList<TemplateRef<any>>;
|
@ContentChildren(TemplateRef) tplRefs: QueryList<TemplateRef<any>>;
|
||||||
@ -162,4 +133,4 @@ function createTestComponent(template: string): ComponentFixture<TestComponent>
|
|||||||
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||||
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]})
|
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]})
|
||||||
.createComponent(TestComponent);
|
.createComponent(TestComponent);
|
||||||
}
|
}
|
||||||
|
@ -1,67 +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 {LowerCasePipe, TitleCasePipe, UpperCasePipe} from '@angular/common';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('LowerCasePipe', () => {
|
|
||||||
let pipe: LowerCasePipe;
|
|
||||||
|
|
||||||
beforeEach(() => { pipe = new LowerCasePipe(); });
|
|
||||||
|
|
||||||
it('should return lowercase', () => { expect(pipe.transform('FOO')).toEqual('foo'); });
|
|
||||||
|
|
||||||
it('should lowercase when there is a new value', () => {
|
|
||||||
expect(pipe.transform('FOO')).toEqual('foo');
|
|
||||||
expect(pipe.transform('BAr')).toEqual('bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not support other objects',
|
|
||||||
() => { expect(() => pipe.transform(<any>{})).toThrowError(); });
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('TitleCasePipe', () => {
|
|
||||||
let pipe: TitleCasePipe;
|
|
||||||
|
|
||||||
beforeEach(() => { pipe = new TitleCasePipe(); });
|
|
||||||
|
|
||||||
it('should return titlecase', () => { expect(pipe.transform('foo')).toEqual('Foo'); });
|
|
||||||
|
|
||||||
it('should return titlecase for subsequent words',
|
|
||||||
() => { expect(pipe.transform('one TWO Three fouR')).toEqual('One Two Three Four'); });
|
|
||||||
|
|
||||||
it('should support empty strings', () => { expect(pipe.transform('')).toEqual(''); });
|
|
||||||
|
|
||||||
it('should persist whitespace',
|
|
||||||
() => { expect(pipe.transform('one two')).toEqual('One Two'); });
|
|
||||||
|
|
||||||
it('should titlecase when there is a new value', () => {
|
|
||||||
expect(pipe.transform('bar')).toEqual('Bar');
|
|
||||||
expect(pipe.transform('foo')).toEqual('Foo');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not support other objects',
|
|
||||||
() => { expect(() => pipe.transform(<any>{})).toThrowError(); });
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('UpperCasePipe', () => {
|
|
||||||
let pipe: UpperCasePipe;
|
|
||||||
|
|
||||||
beforeEach(() => { pipe = new UpperCasePipe(); });
|
|
||||||
|
|
||||||
it('should return uppercase', () => { expect(pipe.transform('foo')).toEqual('FOO'); });
|
|
||||||
|
|
||||||
it('should uppercase when there is a new value', () => {
|
|
||||||
expect(pipe.transform('foo')).toEqual('FOO');
|
|
||||||
expect(pipe.transform('bar')).toEqual('BAR');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not support other objects',
|
|
||||||
() => { expect(() => pipe.transform(<any>{})).toThrowError(); });
|
|
||||||
});
|
|
||||||
}
|
|
@ -53,11 +53,13 @@ export function main() {
|
|||||||
|
|
||||||
it('should return null for empty string', () => expect(pipe.transform('')).toEqual(null));
|
it('should return null for empty string', () => expect(pipe.transform('')).toEqual(null));
|
||||||
|
|
||||||
|
it('should return null for NaN', () => expect(pipe.transform(Number.NaN)).toEqual(null));
|
||||||
|
|
||||||
it('should support ISO string without time',
|
it('should support ISO string without time',
|
||||||
() => { expect(() => pipe.transform(isoStringWithoutTime)).not.toThrow(); });
|
() => { expect(() => pipe.transform(isoStringWithoutTime)).not.toThrow(); });
|
||||||
|
|
||||||
it('should not support other objects',
|
it('should not support other objects',
|
||||||
() => { expect(() => pipe.transform({})).toThrowError(); });
|
() => expect(() => pipe.transform({})).toThrowError(/Invalid argument/));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('transform', () => {
|
describe('transform', () => {
|
||||||
@ -188,8 +190,14 @@ export function main() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should format invalid in IE ISO date',
|
||||||
|
() => expect(pipe.transform('2017-01-11T09:25:14.014-0500')).toEqual('Jan 11, 2017'));
|
||||||
|
|
||||||
|
it('should format invalid in Safari ISO date',
|
||||||
|
() => expect(pipe.transform('2017-01-20T19:00:00+0000')).toEqual('Jan 20, 2017'));
|
||||||
|
|
||||||
it('should remove bidi control characters',
|
it('should remove bidi control characters',
|
||||||
() => { expect(pipe.transform(date, 'MM/dd/yyyy').length).toEqual(10); });
|
() => expect(pipe.transform(date, 'MM/dd/yyyy').length).toEqual(10));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
42
modules/@angular/common/test/pipes/lowercase_pipe_spec.ts
Normal file
42
modules/@angular/common/test/pipes/lowercase_pipe_spec.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* @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 {LowerCasePipe} from '@angular/common';
|
||||||
|
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('LowerCasePipe', () => {
|
||||||
|
let upper: string;
|
||||||
|
let lower: string;
|
||||||
|
let pipe: LowerCasePipe;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
lower = 'something';
|
||||||
|
upper = 'SOMETHING';
|
||||||
|
pipe = new LowerCasePipe();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('transform', () => {
|
||||||
|
it('should return lowercase', () => {
|
||||||
|
const val = pipe.transform(upper);
|
||||||
|
expect(val).toEqual(lower);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should lowercase when there is a new value', () => {
|
||||||
|
const val = pipe.transform(upper);
|
||||||
|
expect(val).toEqual(lower);
|
||||||
|
const val2 = pipe.transform('WAT');
|
||||||
|
expect(val2).toEqual('wat');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not support other objects',
|
||||||
|
() => { expect(() => pipe.transform(<any>{})).toThrowError(); });
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
43
modules/@angular/common/test/pipes/uppercase_pipe_spec.ts
Normal file
43
modules/@angular/common/test/pipes/uppercase_pipe_spec.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* @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 {UpperCasePipe} from '@angular/common';
|
||||||
|
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('UpperCasePipe', () => {
|
||||||
|
let upper: string;
|
||||||
|
let lower: string;
|
||||||
|
let pipe: UpperCasePipe;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
lower = 'something';
|
||||||
|
upper = 'SOMETHING';
|
||||||
|
pipe = new UpperCasePipe();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('transform', () => {
|
||||||
|
|
||||||
|
it('should return uppercase', () => {
|
||||||
|
const val = pipe.transform(lower);
|
||||||
|
expect(val).toEqual(upper);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should uppercase when there is a new value', () => {
|
||||||
|
const val = pipe.transform(lower);
|
||||||
|
expect(val).toEqual(upper);
|
||||||
|
const val2 = pipe.transform('wat');
|
||||||
|
expect(val2).toEqual('WAT');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not support other objects',
|
||||||
|
() => { expect(() => pipe.transform(<any>{})).toThrowError(); });
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* @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 {Component, NgModule} from '@angular/core';
|
||||||
|
import {RouterModule} from '@angular/router';
|
||||||
|
|
||||||
|
@Component({selector: 'lazy-feature-comp', template: 'lazy feature, nested!'})
|
||||||
|
export class LazyFeatureNestedComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild([
|
||||||
|
{path: '', component: LazyFeatureNestedComponent, pathMatch: 'full'},
|
||||||
|
])],
|
||||||
|
declarations: [LazyFeatureNestedComponent]
|
||||||
|
})
|
||||||
|
export class LazyFeatureNestedModule {
|
||||||
|
}
|
@ -16,7 +16,10 @@ export class LazyFeatureComponent {
|
|||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forChild([
|
imports: [RouterModule.forChild([
|
||||||
{path: '', component: LazyFeatureComponent, pathMatch: 'full'},
|
{path: '', component: LazyFeatureComponent, pathMatch: 'full'},
|
||||||
{path: 'feature', loadChildren: './feature.module#FeatureModule'}
|
{path: 'feature', loadChildren: './feature.module#FeatureModule'}, {
|
||||||
|
path: 'nested-feature',
|
||||||
|
loadChildren: './lazy-feature-nested.module#LazyFeatureNestedModule'
|
||||||
|
}
|
||||||
])],
|
])],
|
||||||
declarations: [LazyFeatureComponent]
|
declarations: [LazyFeatureComponent]
|
||||||
})
|
})
|
||||||
|
@ -191,6 +191,8 @@ function lazyRoutesTest() {
|
|||||||
'./lazy.module#LazyModule': 'lazy.module.ts',
|
'./lazy.module#LazyModule': 'lazy.module.ts',
|
||||||
'./feature/feature.module#FeatureModule': 'feature/feature.module.ts',
|
'./feature/feature.module#FeatureModule': 'feature/feature.module.ts',
|
||||||
'./feature/lazy-feature.module#LazyFeatureModule': 'feature/lazy-feature.module.ts',
|
'./feature/lazy-feature.module#LazyFeatureModule': 'feature/lazy-feature.module.ts',
|
||||||
|
'./feature.module#FeatureModule': 'feature/feature.module.ts',
|
||||||
|
'./lazy-feature-nested.module#LazyFeatureNestedModule': 'feature/lazy-feature-nested.module.ts',
|
||||||
'feature2/feature2.module#Feature2Module': 'feature2/feature2.module.ts',
|
'feature2/feature2.module#Feature2Module': 'feature2/feature2.module.ts',
|
||||||
'./default.module': 'feature2/default.module.ts',
|
'./default.module': 'feature2/default.module.ts',
|
||||||
'feature/feature.module#FeatureModule': 'feature/feature.module.ts'
|
'feature/feature.module#FeatureModule': 'feature/feature.module.ts'
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"ng-xi18n": "./src/extract_i18n.js"
|
"ng-xi18n": "./src/extract_i18n.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/tsc-wrapped": "0.5.0",
|
"@angular/tsc-wrapped": "0.5.2",
|
||||||
"reflect-metadata": "^0.1.2",
|
"reflect-metadata": "^0.1.2",
|
||||||
"minimist": "^1.2.0"
|
"minimist": "^1.2.0"
|
||||||
},
|
},
|
||||||
|
@ -36,29 +36,6 @@ export class CodeGenerator {
|
|||||||
public host: ts.CompilerHost, private compiler: compiler.AotCompiler,
|
public host: ts.CompilerHost, private compiler: compiler.AotCompiler,
|
||||||
private ngCompilerHost: CompilerHost) {}
|
private ngCompilerHost: CompilerHost) {}
|
||||||
|
|
||||||
// Write codegen in a directory structure matching the sources.
|
|
||||||
private calculateEmitPath(filePath: string): string {
|
|
||||||
let root = this.options.basePath;
|
|
||||||
for (const eachRootDir of this.options.rootDirs || []) {
|
|
||||||
if (this.options.trace) {
|
|
||||||
console.error(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
|
||||||
}
|
|
||||||
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
|
|
||||||
root = eachRootDir;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// transplant the codegen path to be inside the `genDir`
|
|
||||||
let relativePath: string = path.relative(root, filePath);
|
|
||||||
while (relativePath.startsWith('..' + path.sep)) {
|
|
||||||
// Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
|
|
||||||
// into `genDir`.
|
|
||||||
relativePath = relativePath.substr(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
return path.join(this.options.genDir, relativePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
codegen(): Promise<any> {
|
codegen(): Promise<any> {
|
||||||
return this.compiler
|
return this.compiler
|
||||||
.compileAll(this.program.getSourceFiles().map(
|
.compileAll(this.program.getSourceFiles().map(
|
||||||
@ -66,7 +43,7 @@ export class CodeGenerator {
|
|||||||
.then(generatedModules => {
|
.then(generatedModules => {
|
||||||
generatedModules.forEach(generatedModule => {
|
generatedModules.forEach(generatedModule => {
|
||||||
const sourceFile = this.program.getSourceFile(generatedModule.srcFileUrl);
|
const sourceFile = this.program.getSourceFile(generatedModule.srcFileUrl);
|
||||||
const emitPath = this.calculateEmitPath(generatedModule.genFileUrl);
|
const emitPath = this.ngCompilerHost.calculateEmitPath(generatedModule.genFileUrl);
|
||||||
const source = GENERATED_META_FILES.test(emitPath) ? generatedModule.source :
|
const source = GENERATED_META_FILES.test(emitPath) ? generatedModule.source :
|
||||||
PREAMBLE + generatedModule.source;
|
PREAMBLE + generatedModule.source;
|
||||||
this.host.writeFile(emitPath, source, false, () => {}, [sourceFile]);
|
this.host.writeFile(emitPath, source, false, () => {}, [sourceFile]);
|
||||||
|
@ -261,6 +261,29 @@ export class CompilerHost implements AotCompilerHost {
|
|||||||
this.options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
|
this.options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
|
||||||
return !excludeRegex.test(filePath);
|
return !excludeRegex.test(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calculateEmitPath(filePath: string): string {
|
||||||
|
// Write codegen in a directory structure matching the sources.
|
||||||
|
let root = this.options.basePath;
|
||||||
|
for (const eachRootDir of this.options.rootDirs || []) {
|
||||||
|
if (this.options.trace) {
|
||||||
|
console.error(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
||||||
|
}
|
||||||
|
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
|
||||||
|
root = eachRootDir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transplant the codegen path to be inside the `genDir`
|
||||||
|
let relativePath: string = path.relative(root, filePath);
|
||||||
|
while (relativePath.startsWith('..' + path.sep)) {
|
||||||
|
// Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
|
||||||
|
// into `genDir`.
|
||||||
|
relativePath = relativePath.substr(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.join(this.options.genDir, relativePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompilerHostContextAdapter {
|
export class CompilerHostContextAdapter {
|
||||||
|
@ -15,8 +15,6 @@
|
|||||||
import {AotCompilerHost, StaticReflector, StaticSymbol} from '@angular/compiler';
|
import {AotCompilerHost, StaticReflector, StaticSymbol} from '@angular/compiler';
|
||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// We cannot depend directly to @angular/router.
|
// We cannot depend directly to @angular/router.
|
||||||
type Route = any;
|
type Route = any;
|
||||||
const ROUTER_MODULE_PATH = '@angular/router/src/router_config_loader';
|
const ROUTER_MODULE_PATH = '@angular/router/src/router_config_loader';
|
||||||
@ -63,29 +61,37 @@ export function listLazyRoutesOfModule(
|
|||||||
const className = entryRouteDef.className;
|
const className = entryRouteDef.className;
|
||||||
|
|
||||||
// List loadChildren of this single module.
|
// List loadChildren of this single module.
|
||||||
const staticSymbol = reflector.findDeclaration(modulePath, className, containingFile);
|
const appStaticSymbol = reflector.findDeclaration(modulePath, className, containingFile);
|
||||||
const ROUTES = reflector.findDeclaration(ROUTER_MODULE_PATH, ROUTER_ROUTES_SYMBOL_NAME);
|
const ROUTES = reflector.findDeclaration(ROUTER_MODULE_PATH, ROUTER_ROUTES_SYMBOL_NAME);
|
||||||
const lazyRoutes: LazyRoute[] =
|
const lazyRoutes: LazyRoute[] =
|
||||||
_extractLazyRoutesFromStaticModule(staticSymbol, reflector, host, ROUTES);
|
_extractLazyRoutesFromStaticModule(appStaticSymbol, reflector, host, ROUTES);
|
||||||
const routes: LazyRouteMap = {};
|
|
||||||
|
|
||||||
lazyRoutes.forEach((lazyRoute: LazyRoute) => {
|
const allLazyRoutes = lazyRoutes.reduce(
|
||||||
const route: string = lazyRoute.routeDef.toString();
|
function includeLazyRouteAndSubRoutes(allRoutes: LazyRouteMap, lazyRoute: LazyRoute):
|
||||||
_assertRoute(routes, lazyRoute);
|
LazyRouteMap {
|
||||||
routes[route] = lazyRoute;
|
const route: string = lazyRoute.routeDef.toString();
|
||||||
|
_assertRoute(allRoutes, lazyRoute);
|
||||||
|
allRoutes[route] = lazyRoute;
|
||||||
|
|
||||||
const lazyModuleSymbol = reflector.findDeclaration(
|
// StaticReflector does not support discovering annotations like `NgModule` on default
|
||||||
lazyRoute.absoluteFilePath, lazyRoute.routeDef.className || 'default');
|
// exports
|
||||||
const subRoutes = _extractLazyRoutesFromStaticModule(lazyModuleSymbol, reflector, host, ROUTES);
|
// Which means: if a default export NgModule was lazy-loaded, we can discover it, but,
|
||||||
|
// we cannot parse its routes to see if they have loadChildren or not.
|
||||||
|
if (!lazyRoute.routeDef.className) {
|
||||||
|
return allRoutes;
|
||||||
|
}
|
||||||
|
|
||||||
// Populate the map using the routes we just found.
|
const lazyModuleSymbol = reflector.findDeclaration(
|
||||||
subRoutes.forEach(subRoute => {
|
lazyRoute.absoluteFilePath, lazyRoute.routeDef.className || 'default');
|
||||||
_assertRoute(routes, subRoute);
|
|
||||||
routes[subRoute.routeDef.toString()] = subRoute;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return routes;
|
const subRoutes =
|
||||||
|
_extractLazyRoutesFromStaticModule(lazyModuleSymbol, reflector, host, ROUTES);
|
||||||
|
|
||||||
|
return subRoutes.reduce(includeLazyRouteAndSubRoutes, allRoutes);
|
||||||
|
},
|
||||||
|
{});
|
||||||
|
|
||||||
|
return allLazyRoutes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -192,7 +198,7 @@ function _collectRoutes(
|
|||||||
*/
|
*/
|
||||||
function _collectLoadChildren(routes: Route[]): string[] {
|
function _collectLoadChildren(routes: Route[]): string[] {
|
||||||
return routes.reduce((m, r) => {
|
return routes.reduce((m, r) => {
|
||||||
if (r.loadChildren) {
|
if (r.loadChildren && typeof r.loadChildren === 'string') {
|
||||||
return m.concat(r.loadChildren);
|
return m.concat(r.loadChildren);
|
||||||
} else if (Array.isArray(r)) {
|
} else if (Array.isArray(r)) {
|
||||||
return m.concat(_collectLoadChildren(r));
|
return m.concat(_collectLoadChildren(r));
|
||||||
|
@ -6,11 +6,9 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {SchemaMetadata} from '@angular/core';
|
|
||||||
|
|
||||||
import {AnimationCompiler} from '../animation/animation_compiler';
|
import {AnimationCompiler} from '../animation/animation_compiler';
|
||||||
import {AnimationParser} from '../animation/animation_parser';
|
import {AnimationParser} from '../animation/animation_parser';
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTypeSummary, componentFactoryName, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTypeSummary, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';
|
||||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||||
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||||
import {ListWrapper} from '../facade/collection';
|
import {ListWrapper} from '../facade/collection';
|
||||||
@ -22,14 +20,13 @@ import * as o from '../output/output_ast';
|
|||||||
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
|
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
|
||||||
import {SummaryResolver} from '../summary_resolver';
|
import {SummaryResolver} from '../summary_resolver';
|
||||||
import {TemplateParser} from '../template_parser/template_parser';
|
import {TemplateParser} from '../template_parser/template_parser';
|
||||||
import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
|
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
|
||||||
|
|
||||||
import {AotCompilerHost} from './compiler_host';
|
import {AotCompilerHost} from './compiler_host';
|
||||||
import {GeneratedFile} from './generated_file';
|
import {GeneratedFile} from './generated_file';
|
||||||
import {StaticSymbol} from './static_symbol';
|
import {StaticSymbol} from './static_symbol';
|
||||||
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
|
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
|
||||||
import {serializeSummaries} from './summary_serializer';
|
import {serializeSummaries, summaryFileName} from './summary_serializer';
|
||||||
import {ngfactoryFilePath, splitTypescriptSuffix, summaryFileName} from './util';
|
|
||||||
|
|
||||||
export class AotCompiler {
|
export class AotCompiler {
|
||||||
private _animationCompiler = new AnimationCompiler();
|
private _animationCompiler = new AnimationCompiler();
|
||||||
@ -66,13 +63,12 @@ export class AotCompiler {
|
|||||||
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||||
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[],
|
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[],
|
||||||
injectables: StaticSymbol[]): GeneratedFile[] {
|
injectables: StaticSymbol[]): GeneratedFile[] {
|
||||||
const fileSuffix = splitTypescriptSuffix(srcFileUrl)[1];
|
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1];
|
||||||
const statements: o.Statement[] = [];
|
const statements: o.Statement[] = [];
|
||||||
const exportedVars: string[] = [];
|
const exportedVars: string[] = [];
|
||||||
const generatedFiles: GeneratedFile[] = [];
|
const generatedFiles: GeneratedFile[] = [];
|
||||||
|
|
||||||
generatedFiles.push(this._createSummary(
|
generatedFiles.push(this._createSummary(srcFileUrl, directives, pipes, ngModules, injectables));
|
||||||
srcFileUrl, directives, pipes, ngModules, injectables, statements, exportedVars));
|
|
||||||
|
|
||||||
// compile all ng modules
|
// compile all ng modules
|
||||||
exportedVars.push(
|
exportedVars.push(
|
||||||
@ -111,7 +107,7 @@ export class AotCompiler {
|
|||||||
});
|
});
|
||||||
if (statements.length > 0) {
|
if (statements.length > 0) {
|
||||||
const srcModule = this._codegenSourceModule(
|
const srcModule = this._codegenSourceModule(
|
||||||
srcFileUrl, ngfactoryFilePath(srcFileUrl), statements, exportedVars);
|
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
|
||||||
generatedFiles.unshift(srcModule);
|
generatedFiles.unshift(srcModule);
|
||||||
}
|
}
|
||||||
return generatedFiles;
|
return generatedFiles;
|
||||||
@ -119,8 +115,7 @@ export class AotCompiler {
|
|||||||
|
|
||||||
private _createSummary(
|
private _createSummary(
|
||||||
srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[],
|
srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[],
|
||||||
ngModules: StaticSymbol[], injectables: StaticSymbol[], targetStatements: o.Statement[],
|
ngModules: StaticSymbol[], injectables: StaticSymbol[]): GeneratedFile {
|
||||||
targetExportedVars: string[]): GeneratedFile {
|
|
||||||
const symbolSummaries = this._symbolResolver.getSymbolsOf(srcFileUrl)
|
const symbolSummaries = this._symbolResolver.getSymbolsOf(srcFileUrl)
|
||||||
.map(symbol => this._symbolResolver.resolveSymbol(symbol));
|
.map(symbol => this._symbolResolver.resolveSymbol(symbol));
|
||||||
const typeSummaries = [
|
const typeSummaries = [
|
||||||
@ -129,13 +124,8 @@ export class AotCompiler {
|
|||||||
...pipes.map(ref => this._metadataResolver.getPipeSummary(ref)),
|
...pipes.map(ref => this._metadataResolver.getPipeSummary(ref)),
|
||||||
...injectables.map(ref => this._metadataResolver.getInjectableSummary(ref))
|
...injectables.map(ref => this._metadataResolver.getInjectableSummary(ref))
|
||||||
];
|
];
|
||||||
const {json, exportAs} = serializeSummaries(
|
const json = serializeSummaries(
|
||||||
this._summaryResolver, this._symbolResolver, symbolSummaries, typeSummaries);
|
this._host, this._summaryResolver, this._symbolResolver, symbolSummaries, typeSummaries);
|
||||||
exportAs.forEach((entry) => {
|
|
||||||
targetStatements.push(
|
|
||||||
o.variable(entry.exportAs).set(o.importExpr({reference: entry.symbol})).toDeclStmt());
|
|
||||||
targetExportedVars.push(entry.exportAs);
|
|
||||||
});
|
|
||||||
return new GeneratedFile(srcFileUrl, summaryFileName(srcFileUrl), json);
|
return new GeneratedFile(srcFileUrl, summaryFileName(srcFileUrl), json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,6 +148,12 @@ export class AotCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
|
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
|
||||||
|
|
||||||
|
appCompileResult.dependencies.forEach((dep) => {
|
||||||
|
dep.placeholder.reference = this._symbolResolver.getStaticSymbol(
|
||||||
|
_ngfactoryModuleUrl(identifierModuleUrl(dep.comp)), _componentFactoryName(dep.comp));
|
||||||
|
});
|
||||||
|
|
||||||
targetStatements.push(...appCompileResult.statements);
|
targetStatements.push(...appCompileResult.statements);
|
||||||
return appCompileResult.ngModuleFactoryVar;
|
return appCompileResult.ngModuleFactoryVar;
|
||||||
}
|
}
|
||||||
@ -174,12 +170,13 @@ export class AotCompiler {
|
|||||||
private _compileComponentFactory(
|
private _compileComponentFactory(
|
||||||
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string,
|
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string,
|
||||||
targetStatements: o.Statement[]): string {
|
targetStatements: o.Statement[]): string {
|
||||||
const hostType = this._metadataResolver.getHostComponentType(compMeta.type.reference);
|
|
||||||
const hostMeta = createHostComponentMeta(
|
const hostMeta = createHostComponentMeta(
|
||||||
hostType, compMeta, this._metadataResolver.getHostComponentViewClass(hostType));
|
this._symbolResolver.getStaticSymbol(
|
||||||
|
identifierModuleUrl(compMeta.type), `${identifierName(compMeta.type)}_Host`),
|
||||||
|
compMeta);
|
||||||
const hostViewFactoryVar = this._compileComponent(
|
const hostViewFactoryVar = this._compileComponent(
|
||||||
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements);
|
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements);
|
||||||
const compFactoryVar = componentFactoryName(compMeta.type.reference);
|
const compFactoryVar = _componentFactoryName(compMeta.type);
|
||||||
targetStatements.push(
|
targetStatements.push(
|
||||||
o.variable(compFactoryVar)
|
o.variable(compFactoryVar)
|
||||||
.set(o.importExpr(
|
.set(o.importExpr(
|
||||||
@ -220,7 +217,7 @@ export class AotCompiler {
|
|||||||
..._resolveStyleStatements(this._symbolResolver, componentStyles, fileSuffix));
|
..._resolveStyleStatements(this._symbolResolver, componentStyles, fileSuffix));
|
||||||
}
|
}
|
||||||
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
|
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
|
||||||
targetStatements.push(...viewResult.statements);
|
targetStatements.push(..._resolveViewStatements(this._symbolResolver, viewResult));
|
||||||
return viewResult.viewClassVar;
|
return viewResult.viewClassVar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,6 +239,27 @@ export class AotCompiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _resolveViewStatements(
|
||||||
|
reflector: StaticSymbolResolver, compileResult: ViewCompileResult): o.Statement[] {
|
||||||
|
compileResult.dependencies.forEach((dep) => {
|
||||||
|
if (dep instanceof ViewClassDependency) {
|
||||||
|
const vfd = <ViewClassDependency>dep;
|
||||||
|
vfd.placeholder.reference =
|
||||||
|
reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(vfd.comp)), dep.name);
|
||||||
|
} else if (dep instanceof ComponentFactoryDependency) {
|
||||||
|
const cfd = <ComponentFactoryDependency>dep;
|
||||||
|
cfd.placeholder.reference = reflector.getStaticSymbol(
|
||||||
|
_ngfactoryModuleUrl(identifierModuleUrl(cfd.comp)), _componentFactoryName(cfd.comp));
|
||||||
|
} else if (dep instanceof DirectiveWrapperDependency) {
|
||||||
|
const dwd = <DirectiveWrapperDependency>dep;
|
||||||
|
dwd.placeholder.reference =
|
||||||
|
reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(dwd.dir)), dwd.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return compileResult.statements;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function _resolveStyleStatements(
|
function _resolveStyleStatements(
|
||||||
reflector: StaticSymbolResolver, compileResult: CompiledStylesheet,
|
reflector: StaticSymbolResolver, compileResult: CompiledStylesheet,
|
||||||
fileSuffix: string): o.Statement[] {
|
fileSuffix: string): o.Statement[] {
|
||||||
@ -252,6 +270,15 @@ function _resolveStyleStatements(
|
|||||||
return compileResult.statements;
|
return compileResult.statements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _ngfactoryModuleUrl(dirUrl: string): string {
|
||||||
|
const urlWithSuffix = _splitTypescriptSuffix(dirUrl);
|
||||||
|
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _componentFactoryName(comp: CompileIdentifierMetadata): string {
|
||||||
|
return `${identifierName(comp)}NgFactory`;
|
||||||
|
}
|
||||||
|
|
||||||
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string {
|
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string {
|
||||||
return `${stylesheetUrl}${shim ? '.shim' : ''}.ngstyle${suffix}`;
|
return `${stylesheetUrl}${shim ? '.shim' : ''}.ngstyle${suffix}`;
|
||||||
}
|
}
|
||||||
@ -263,6 +290,20 @@ function _assertComponent(meta: CompileDirectiveMetadata) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _splitTypescriptSuffix(path: string): string[] {
|
||||||
|
if (path.endsWith('.d.ts')) {
|
||||||
|
return [path.slice(0, -5), '.ts'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastDot = path.lastIndexOf('.');
|
||||||
|
|
||||||
|
if (lastDot !== -1) {
|
||||||
|
return [path.substring(0, lastDot), path.substring(lastDot)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [path, ''];
|
||||||
|
}
|
||||||
|
|
||||||
export interface NgAnalyzedModules {
|
export interface NgAnalyzedModules {
|
||||||
ngModules: CompileNgModuleMetadata[];
|
ngModules: CompileNgModuleMetadata[];
|
||||||
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>;
|
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>;
|
||||||
|
@ -34,12 +34,11 @@ import {AotCompilerHost} from './compiler_host';
|
|||||||
import {AotCompilerOptions} from './compiler_options';
|
import {AotCompilerOptions} from './compiler_options';
|
||||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||||
import {StaticReflector} from './static_reflector';
|
import {StaticReflector} from './static_reflector';
|
||||||
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
import {StaticSymbolCache} from './static_symbol';
|
||||||
import {StaticSymbolResolver} from './static_symbol_resolver';
|
import {StaticSymbolResolver} from './static_symbol_resolver';
|
||||||
import {AotSummaryResolver} from './summary_resolver';
|
import {AotSummaryResolver} from './summary_resolver';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new AotCompiler based on options and a host.
|
* Creates a new AotCompiler based on options and a host.
|
||||||
*/
|
*/
|
||||||
@ -70,19 +69,13 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
|
|||||||
const resolver = new CompileMetadataResolver(
|
const resolver = new CompileMetadataResolver(
|
||||||
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
||||||
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
||||||
symbolCache, staticReflector);
|
staticReflector);
|
||||||
// TODO(vicb): do not pass options.i18nFormat here
|
// TODO(vicb): do not pass options.i18nFormat here
|
||||||
const importResolver = {
|
|
||||||
getImportAs: (symbol: StaticSymbol) => symbolResolver.getImportAs(symbol),
|
|
||||||
fileNameToModuleName: (fileName: string, containingFilePath: string) =>
|
|
||||||
compilerHost.fileNameToModuleName(fileName, containingFilePath)
|
|
||||||
};
|
|
||||||
const compiler = new AotCompiler(
|
const compiler = new AotCompiler(
|
||||||
compilerHost, resolver, tmplParser, new StyleCompiler(urlResolver),
|
compilerHost, resolver, tmplParser, new StyleCompiler(urlResolver),
|
||||||
new ViewCompiler(config, elementSchemaRegistry),
|
new ViewCompiler(config, elementSchemaRegistry),
|
||||||
new DirectiveWrapperCompiler(config, expressionParser, elementSchemaRegistry, console),
|
new DirectiveWrapperCompiler(config, expressionParser, elementSchemaRegistry, console),
|
||||||
new NgModuleCompiler(), new TypeScriptEmitter(importResolver), summaryResolver,
|
new NgModuleCompiler(), new TypeScriptEmitter(compilerHost), summaryResolver, options.locale,
|
||||||
options.locale, options.i18nFormat, new AnimationParser(elementSchemaRegistry),
|
options.i18nFormat, new AnimationParser(elementSchemaRegistry), symbolResolver);
|
||||||
symbolResolver);
|
|
||||||
return {compiler, reflector: staticReflector};
|
return {compiler, reflector: staticReflector};
|
||||||
}
|
}
|
||||||
|
@ -6,24 +6,19 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {ImportResolver} from '../output/path_util';
|
||||||
|
|
||||||
import {StaticSymbol} from './static_symbol';
|
import {StaticSymbol} from './static_symbol';
|
||||||
import {StaticSymbolResolverHost} from './static_symbol_resolver';
|
import {StaticSymbolResolverHost} from './static_symbol_resolver';
|
||||||
import {AotSummaryResolverHost} from './summary_resolver';
|
import {AotSummaryResolverHost} from './summary_resolver';
|
||||||
|
import {AotSummarySerializerHost} from './summary_serializer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The host of the AotCompiler disconnects the implementation from TypeScript / other language
|
* The host of the AotCompiler disconnects the implementation from TypeScript / other language
|
||||||
* services and from underlying file systems.
|
* services and from underlying file systems.
|
||||||
*/
|
*/
|
||||||
export interface AotCompilerHost extends StaticSymbolResolverHost, AotSummaryResolverHost {
|
export interface AotCompilerHost extends StaticSymbolResolverHost, ImportResolver,
|
||||||
/**
|
AotSummaryResolverHost, AotSummarySerializerHost {
|
||||||
* Converts a file path to a module name that can be used as an `import.
|
|
||||||
* I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`.
|
|
||||||
*
|
|
||||||
* See ImportResolver.
|
|
||||||
*/
|
|
||||||
fileNameToModuleName(importedFilePath: string, containingFilePath: string): string
|
|
||||||
/*|null*/;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a resource (e.g. html / css)
|
* Loads a resource (e.g. html / css)
|
||||||
*/
|
*/
|
||||||
|
@ -9,9 +9,10 @@
|
|||||||
import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
||||||
|
|
||||||
import {ReflectorReader} from '../private_import_core';
|
import {ReflectorReader} from '../private_import_core';
|
||||||
|
import {SyntaxError} from '../util';
|
||||||
|
|
||||||
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
import {StaticSymbol} from './static_symbol';
|
||||||
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
|
import {StaticSymbolResolver} from './static_symbol_resolver';
|
||||||
|
|
||||||
const ANGULAR_IMPORT_LOCATIONS = {
|
const ANGULAR_IMPORT_LOCATIONS = {
|
||||||
coreDecorators: '@angular/core/src/metadata',
|
coreDecorators: '@angular/core/src/metadata',
|
||||||
@ -83,11 +84,8 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
annotations = [];
|
annotations = [];
|
||||||
const classMetadata = this.getTypeMetadata(type);
|
const classMetadata = this.getTypeMetadata(type);
|
||||||
if (classMetadata['extends']) {
|
if (classMetadata['extends']) {
|
||||||
const parentType = this.simplify(type, classMetadata['extends']);
|
const parentAnnotations = this.annotations(this.simplify(type, classMetadata['extends']));
|
||||||
if (parentType instanceof StaticSymbol) {
|
annotations.push(...parentAnnotations);
|
||||||
const parentAnnotations = this.annotations(parentType);
|
|
||||||
annotations.push(...parentAnnotations);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (classMetadata['decorators']) {
|
if (classMetadata['decorators']) {
|
||||||
const ownAnnotations: any[] = this.simplify(type, classMetadata['decorators']);
|
const ownAnnotations: any[] = this.simplify(type, classMetadata['decorators']);
|
||||||
@ -104,13 +102,10 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
const classMetadata = this.getTypeMetadata(type);
|
const classMetadata = this.getTypeMetadata(type);
|
||||||
propMetadata = {};
|
propMetadata = {};
|
||||||
if (classMetadata['extends']) {
|
if (classMetadata['extends']) {
|
||||||
const parentType = this.simplify(type, classMetadata['extends']);
|
const parentPropMetadata = this.propMetadata(this.simplify(type, classMetadata['extends']));
|
||||||
if (parentType instanceof StaticSymbol) {
|
Object.keys(parentPropMetadata).forEach((parentProp) => {
|
||||||
const parentPropMetadata = this.propMetadata(parentType);
|
propMetadata[parentProp] = parentPropMetadata[parentProp];
|
||||||
Object.keys(parentPropMetadata).forEach((parentProp) => {
|
});
|
||||||
propMetadata[parentProp] = parentPropMetadata[parentProp];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const members = classMetadata['members'] || {};
|
const members = classMetadata['members'] || {};
|
||||||
@ -162,10 +157,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
parameters.push(nestedResult);
|
parameters.push(nestedResult);
|
||||||
});
|
});
|
||||||
} else if (classMetadata['extends']) {
|
} else if (classMetadata['extends']) {
|
||||||
const parentType = this.simplify(type, classMetadata['extends']);
|
parameters = this.parameters(this.simplify(type, classMetadata['extends']));
|
||||||
if (parentType instanceof StaticSymbol) {
|
|
||||||
parameters = this.parameters(parentType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!parameters) {
|
if (!parameters) {
|
||||||
parameters = [];
|
parameters = [];
|
||||||
@ -185,13 +177,10 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
const classMetadata = this.getTypeMetadata(type);
|
const classMetadata = this.getTypeMetadata(type);
|
||||||
methodNames = {};
|
methodNames = {};
|
||||||
if (classMetadata['extends']) {
|
if (classMetadata['extends']) {
|
||||||
const parentType = this.simplify(type, classMetadata['extends']);
|
const parentMethodNames = this._methodNames(this.simplify(type, classMetadata['extends']));
|
||||||
if (parentType instanceof StaticSymbol) {
|
Object.keys(parentMethodNames).forEach((parentProp) => {
|
||||||
const parentMethodNames = this._methodNames(parentType);
|
methodNames[parentProp] = parentMethodNames[parentProp];
|
||||||
Object.keys(parentMethodNames).forEach((parentProp) => {
|
});
|
||||||
methodNames[parentProp] = parentMethodNames[parentProp];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const members = classMetadata['members'] || {};
|
const members = classMetadata['members'] || {};
|
||||||
@ -322,6 +311,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
if (value && (depth != 0 || value.__symbolic != 'error')) {
|
if (value && (depth != 0 || value.__symbolic != 'error')) {
|
||||||
const parameters: string[] = targetFunction['parameters'];
|
const parameters: string[] = targetFunction['parameters'];
|
||||||
const defaults: any[] = targetFunction.defaults;
|
const defaults: any[] = targetFunction.defaults;
|
||||||
|
args = args.map(arg => simplifyInContext(context, arg, depth + 1));
|
||||||
if (defaults && defaults.length > args.length) {
|
if (defaults && defaults.length > args.length) {
|
||||||
args.push(...defaults.slice(args.length).map((value: any) => simplify(value)));
|
args.push(...defaults.slice(args.length).map((value: any) => simplify(value)));
|
||||||
}
|
}
|
||||||
@ -511,15 +501,15 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
const argExpressions: any[] = expression['arguments'] || [];
|
const argExpressions: any[] = expression['arguments'] || [];
|
||||||
const args =
|
|
||||||
argExpressions.map(arg => simplifyInContext(context, arg, depth + 1));
|
|
||||||
let converter = self.conversionMap.get(staticSymbol);
|
let converter = self.conversionMap.get(staticSymbol);
|
||||||
if (converter) {
|
if (converter) {
|
||||||
|
const args =
|
||||||
|
argExpressions.map(arg => simplifyInContext(context, arg, depth + 1));
|
||||||
return converter(context, args);
|
return converter(context, args);
|
||||||
} else {
|
} else {
|
||||||
// Determine if the function is one we can simplify.
|
// Determine if the function is one we can simplify.
|
||||||
const targetFunction = resolveReferenceValue(staticSymbol);
|
const targetFunction = resolveReferenceValue(staticSymbol);
|
||||||
return simplifyCall(staticSymbol, targetFunction, args);
|
return simplifyCall(staticSymbol, targetFunction, argExpressions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -549,7 +539,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
if (e.fileName) {
|
if (e.fileName) {
|
||||||
throw positionalError(message, e.fileName, e.line, e.column);
|
throw positionalError(message, e.fileName, e.line, e.column);
|
||||||
}
|
}
|
||||||
throw new Error(message);
|
throw new SyntaxError(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -663,10 +653,6 @@ class PopulatedScope extends BindingScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sameSymbol(a: StaticSymbol, b: StaticSymbol): boolean {
|
|
||||||
return a === b;
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldIgnore(value: any): boolean {
|
function shouldIgnore(value: any): boolean {
|
||||||
return value && value.__symbolic == 'ignore';
|
return value && value.__symbolic == 'ignore';
|
||||||
}
|
}
|
||||||
|
@ -12,14 +12,7 @@
|
|||||||
* This token is unique for a filePath and name and can be used as a hash table key.
|
* This token is unique for a filePath and name and can be used as a hash table key.
|
||||||
*/
|
*/
|
||||||
export class StaticSymbol {
|
export class StaticSymbol {
|
||||||
constructor(public filePath: string, public name: string, public members: string[]) {}
|
constructor(public filePath: string, public name: string, public members?: string[]) {}
|
||||||
|
|
||||||
assertNoMembers() {
|
|
||||||
if (this.members.length) {
|
|
||||||
throw new Error(
|
|
||||||
`Illegal state: symbol without members expected, but got ${JSON.stringify(this)}.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,18 +45,11 @@ const SUPPORTED_SCHEMA_VERSION = 3;
|
|||||||
/**
|
/**
|
||||||
* This class is responsible for loading metadata per symbol,
|
* This class is responsible for loading metadata per symbol,
|
||||||
* and normalizing references between symbols.
|
* and normalizing references between symbols.
|
||||||
*
|
|
||||||
* Internally, it only uses symbols without members,
|
|
||||||
* and deduces the values for symbols with members based
|
|
||||||
* on these symbols.
|
|
||||||
*/
|
*/
|
||||||
export class StaticSymbolResolver {
|
export class StaticSymbolResolver {
|
||||||
private metadataCache = new Map<string, {[key: string]: any}>();
|
private metadataCache = new Map<string, {[key: string]: any}>();
|
||||||
// Note: this will only contain StaticSymbols without members!
|
|
||||||
private resolvedSymbols = new Map<StaticSymbol, ResolvedStaticSymbol>();
|
private resolvedSymbols = new Map<StaticSymbol, ResolvedStaticSymbol>();
|
||||||
private resolvedFilePaths = new Set<string>();
|
private resolvedFilePaths = new Set<string>();
|
||||||
// Note: this will only contain StaticSymbols without members!
|
|
||||||
private importAs = new Map<StaticSymbol, StaticSymbol>();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private host: StaticSymbolResolverHost, private staticSymbolCache: StaticSymbolCache,
|
private host: StaticSymbolResolverHost, private staticSymbolCache: StaticSymbolCache,
|
||||||
@ -67,33 +60,13 @@ export class StaticSymbolResolver {
|
|||||||
if (staticSymbol.members.length > 0) {
|
if (staticSymbol.members.length > 0) {
|
||||||
return this._resolveSymbolMembers(staticSymbol);
|
return this._resolveSymbolMembers(staticSymbol);
|
||||||
}
|
}
|
||||||
let result = this.resolvedSymbols.get(staticSymbol);
|
let result = this._resolveSymbolFromSummary(staticSymbol);
|
||||||
if (result) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
result = this._resolveSymbolFromSummary(staticSymbol);
|
|
||||||
if (result) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
// Note: Some users use libraries that were not compiled with ngc, i.e. they don't
|
|
||||||
// have summaries, only .d.ts files. So we always need to check both, the summary
|
|
||||||
// and metadata.
|
|
||||||
this._createSymbolsOf(staticSymbol.filePath);
|
|
||||||
result = this.resolvedSymbols.get(staticSymbol);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
getImportAs(staticSymbol: StaticSymbol): StaticSymbol {
|
|
||||||
if (staticSymbol.members.length) {
|
|
||||||
const baseSymbol = this.getStaticSymbol(staticSymbol.filePath, staticSymbol.name);
|
|
||||||
const baseImportAs = this.getImportAs(baseSymbol);
|
|
||||||
return baseImportAs ?
|
|
||||||
this.getStaticSymbol(baseImportAs.filePath, baseImportAs.name, staticSymbol.members) :
|
|
||||||
null;
|
|
||||||
}
|
|
||||||
let result = this.summaryResolver.getImportAs(staticSymbol);
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
result = this.importAs.get(staticSymbol);
|
// Note: Some users use libraries that were not compiled with ngc, i.e. they don't
|
||||||
|
// have summaries, only .d.ts files. So we always need to check both, the summary
|
||||||
|
// and metadata.
|
||||||
|
this._createSymbolsOf(staticSymbol.filePath);
|
||||||
|
result = this.resolvedSymbols.get(staticSymbol);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -162,13 +135,10 @@ export class StaticSymbolResolver {
|
|||||||
const metadata = this.getModuleMetadata(filePath);
|
const metadata = this.getModuleMetadata(filePath);
|
||||||
if (metadata['metadata']) {
|
if (metadata['metadata']) {
|
||||||
// handle direct declarations of the symbol
|
// handle direct declarations of the symbol
|
||||||
const topLevelSymbolNames =
|
Object.keys(metadata['metadata']).forEach((symbolName) => {
|
||||||
new Set<string>(Object.keys(metadata['metadata']).map(unescapeIdentifier));
|
const symbolMeta = metadata['metadata'][symbolName];
|
||||||
Object.keys(metadata['metadata']).forEach((metadataKey) => {
|
resolvedSymbols.push(
|
||||||
const symbolMeta = metadata['metadata'][metadataKey];
|
this.createResolvedSymbol(this.getStaticSymbol(filePath, symbolName), symbolMeta));
|
||||||
resolvedSymbols.push(this.createResolvedSymbol(
|
|
||||||
this.getStaticSymbol(filePath, unescapeIdentifier(metadataKey)), topLevelSymbolNames,
|
|
||||||
symbolMeta));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,16 +154,15 @@ export class StaticSymbolResolver {
|
|||||||
} else {
|
} else {
|
||||||
symbolName = exportSymbol.as;
|
symbolName = exportSymbol.as;
|
||||||
}
|
}
|
||||||
symbolName = unescapeIdentifier(symbolName);
|
|
||||||
let symName = symbolName;
|
let symName = symbolName;
|
||||||
if (typeof exportSymbol !== 'string') {
|
if (typeof exportSymbol !== 'string') {
|
||||||
symName = unescapeIdentifier(exportSymbol.name);
|
symName = exportSymbol.name;
|
||||||
}
|
}
|
||||||
const resolvedModule = this.resolveModule(moduleExport.from, filePath);
|
const resolvedModule = this.resolveModule(moduleExport.from, filePath);
|
||||||
if (resolvedModule) {
|
if (resolvedModule) {
|
||||||
const targetSymbol = this.getStaticSymbol(resolvedModule, symName);
|
const targetSymbol = this.getStaticSymbol(resolvedModule, symName);
|
||||||
const sourceSymbol = this.getStaticSymbol(filePath, symbolName);
|
const sourceSymbol = this.getStaticSymbol(filePath, symbolName);
|
||||||
resolvedSymbols.push(this.createExport(sourceSymbol, targetSymbol));
|
resolvedSymbols.push(new ResolvedStaticSymbol(sourceSymbol, targetSymbol));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -203,7 +172,7 @@ export class StaticSymbolResolver {
|
|||||||
const nestedExports = this.getSymbolsOf(resolvedModule);
|
const nestedExports = this.getSymbolsOf(resolvedModule);
|
||||||
nestedExports.forEach((targetSymbol) => {
|
nestedExports.forEach((targetSymbol) => {
|
||||||
const sourceSymbol = this.getStaticSymbol(filePath, targetSymbol.name);
|
const sourceSymbol = this.getStaticSymbol(filePath, targetSymbol.name);
|
||||||
resolvedSymbols.push(this.createExport(sourceSymbol, targetSymbol));
|
resolvedSymbols.push(new ResolvedStaticSymbol(sourceSymbol, targetSymbol));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -213,9 +182,7 @@ export class StaticSymbolResolver {
|
|||||||
(resolvedSymbol) => this.resolvedSymbols.set(resolvedSymbol.symbol, resolvedSymbol));
|
(resolvedSymbol) => this.resolvedSymbols.set(resolvedSymbol.symbol, resolvedSymbol));
|
||||||
}
|
}
|
||||||
|
|
||||||
private createResolvedSymbol(
|
private createResolvedSymbol(sourceSymbol: StaticSymbol, metadata: any): ResolvedStaticSymbol {
|
||||||
sourceSymbol: StaticSymbol, topLevelSymbolNames: Set<string>,
|
|
||||||
metadata: any): ResolvedStaticSymbol {
|
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
class ReferenceTransformer extends ValueTransformer {
|
class ReferenceTransformer extends ValueTransformer {
|
||||||
@ -229,7 +196,7 @@ export class StaticSymbolResolver {
|
|||||||
return result;
|
return result;
|
||||||
} else if (symbolic === 'reference') {
|
} else if (symbolic === 'reference') {
|
||||||
const module = map['module'];
|
const module = map['module'];
|
||||||
const name = map['name'] ? unescapeIdentifier(map['name']) : map['name'];
|
const name = map['name'];
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -242,41 +209,26 @@ export class StaticSymbolResolver {
|
|||||||
message: `Could not resolve ${module} relative to ${sourceSymbol.filePath}.`
|
message: `Could not resolve ${module} relative to ${sourceSymbol.filePath}.`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const isFunctionParam = functionParams.indexOf(name) >= 0;
|
||||||
|
if (!isFunctionParam) {
|
||||||
|
filePath = sourceSymbol.filePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filePath) {
|
||||||
return self.getStaticSymbol(filePath, name);
|
return self.getStaticSymbol(filePath, name);
|
||||||
} else if (functionParams.indexOf(name) >= 0) {
|
} else {
|
||||||
// reference to a function parameter
|
// reference to a function parameter
|
||||||
return {__symbolic: 'reference', name: name};
|
return {__symbolic: 'reference', name: name};
|
||||||
} else {
|
|
||||||
if (topLevelSymbolNames.has(name)) {
|
|
||||||
return self.getStaticSymbol(sourceSymbol.filePath, name);
|
|
||||||
}
|
|
||||||
// ambient value
|
|
||||||
null;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return super.visitStringMap(map, functionParams);
|
return super.visitStringMap(map, functionParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const transformedMeta = visitValue(metadata, new ReferenceTransformer(), []);
|
|
||||||
if (transformedMeta instanceof StaticSymbol) {
|
|
||||||
return this.createExport(sourceSymbol, transformedMeta);
|
|
||||||
}
|
|
||||||
return new ResolvedStaticSymbol(sourceSymbol, transformedMeta);
|
|
||||||
}
|
|
||||||
|
|
||||||
private createExport(sourceSymbol: StaticSymbol, targetSymbol: StaticSymbol):
|
const transformedMeta = visitValue(metadata, new ReferenceTransformer(), []);
|
||||||
ResolvedStaticSymbol {
|
return new ResolvedStaticSymbol(sourceSymbol, transformedMeta);
|
||||||
sourceSymbol.assertNoMembers();
|
|
||||||
targetSymbol.assertNoMembers();
|
|
||||||
if (this.summaryResolver.isLibraryFile(sourceSymbol.filePath)) {
|
|
||||||
// This case is for an ng library importing symbols from a plain ts library
|
|
||||||
// transitively.
|
|
||||||
// Note: We rely on the fact that we discover symbols in the direction
|
|
||||||
// from source files to library files
|
|
||||||
this.importAs.set(targetSymbol, this.getImportAs(sourceSymbol) || sourceSymbol);
|
|
||||||
}
|
|
||||||
return new ResolvedStaticSymbol(sourceSymbol, targetSymbol);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private reportError(error: Error, context: StaticSymbol, path?: string) {
|
private reportError(error: Error, context: StaticSymbol, path?: string) {
|
||||||
@ -321,7 +273,12 @@ export class StaticSymbolResolver {
|
|||||||
getSymbolByModule(module: string, symbolName: string, containingFile?: string): StaticSymbol {
|
getSymbolByModule(module: string, symbolName: string, containingFile?: string): StaticSymbol {
|
||||||
const filePath = this.resolveModule(module, containingFile);
|
const filePath = this.resolveModule(module, containingFile);
|
||||||
if (!filePath) {
|
if (!filePath) {
|
||||||
throw new Error(`Could not resolve module ${module} relative to ${containingFile}`);
|
this.reportError(
|
||||||
|
new Error(`Could not resolve module ${module}${containingFile ? ` relative to $ {
|
||||||
|
containingFile
|
||||||
|
} `: ''}`),
|
||||||
|
null);
|
||||||
|
return this.getStaticSymbol(`ERROR:${module}`, symbolName);
|
||||||
}
|
}
|
||||||
return this.getStaticSymbol(filePath, symbolName);
|
return this.getStaticSymbol(filePath, symbolName);
|
||||||
}
|
}
|
||||||
@ -335,11 +292,3 @@ export class StaticSymbolResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const UNDERSCORE_CHAR_CODE = 95;
|
|
||||||
|
|
||||||
// Remove extra underscore from escaped identifier.
|
|
||||||
// See https://github.com/Microsoft/TypeScript/blob/master/src/compiler/utilities.ts
|
|
||||||
export function unescapeIdentifier(identifier: string): string {
|
|
||||||
return identifier.startsWith('___') ? identifier.substr(1) : identifier;
|
|
||||||
}
|
|
@ -5,13 +5,14 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {CompileSummaryKind, CompileTypeSummary} from '../compile_metadata';
|
|
||||||
import {Summary, SummaryResolver} from '../summary_resolver';
|
import {Summary, SummaryResolver} from '../summary_resolver';
|
||||||
|
|
||||||
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
||||||
import {ResolvedStaticSymbol} from './static_symbol_resolver';
|
import {ResolvedStaticSymbol} from './static_symbol_resolver';
|
||||||
import {deserializeSummaries} from './summary_serializer';
|
import {deserializeSummaries, summaryFileName} from './summary_serializer';
|
||||||
import {ngfactoryFilePath, stripNgFactory, summaryFileName} from './util';
|
|
||||||
|
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||||
|
|
||||||
export interface AotSummaryResolverHost {
|
export interface AotSummaryResolverHost {
|
||||||
/**
|
/**
|
||||||
@ -23,34 +24,23 @@ export interface AotSummaryResolverHost {
|
|||||||
* Returns whether a file is a source file or not.
|
* Returns whether a file is a source file or not.
|
||||||
*/
|
*/
|
||||||
isSourceFile(sourceFilePath: string): boolean;
|
isSourceFile(sourceFilePath: string): boolean;
|
||||||
/**
|
|
||||||
* Returns the output file path of a source file.
|
|
||||||
* E.g.
|
|
||||||
* `some_file.ts` -> `some_file.d.ts`
|
|
||||||
*/
|
|
||||||
getOutputFileName(sourceFilePath: string): string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
|
export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
|
||||||
// Note: this will only contain StaticSymbols without members!
|
|
||||||
private summaryCache = new Map<StaticSymbol, Summary<StaticSymbol>>();
|
private summaryCache = new Map<StaticSymbol, Summary<StaticSymbol>>();
|
||||||
private loadedFilePaths = new Set<string>();
|
private loadedFilePaths = new Set<string>();
|
||||||
// Note: this will only contain StaticSymbols without members!
|
|
||||||
private importAs = new Map<StaticSymbol, StaticSymbol>();
|
|
||||||
|
|
||||||
constructor(private host: AotSummaryResolverHost, private staticSymbolCache: StaticSymbolCache) {}
|
constructor(private host: AotSummaryResolverHost, private staticSymbolCache: StaticSymbolCache) {}
|
||||||
|
|
||||||
isLibraryFile(filePath: string): boolean {
|
private _assertNoMembers(symbol: StaticSymbol) {
|
||||||
// Note: We need to strip the .ngfactory. file path,
|
if (symbol.members.length) {
|
||||||
// so this method also works for generated files
|
throw new Error(
|
||||||
// (for which host.isSourceFile will always return false).
|
`Internal state: StaticSymbols in summaries can't have members! ${JSON.stringify(symbol)}`);
|
||||||
return !this.host.isSourceFile(stripNgFactory(filePath));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getLibraryFileName(filePath: string) { return this.host.getOutputFileName(filePath); }
|
|
||||||
|
|
||||||
resolveSummary(staticSymbol: StaticSymbol): Summary<StaticSymbol> {
|
resolveSummary(staticSymbol: StaticSymbol): Summary<StaticSymbol> {
|
||||||
staticSymbol.assertNoMembers();
|
this._assertNoMembers(staticSymbol);
|
||||||
let summary = this.summaryCache.get(staticSymbol);
|
let summary = this.summaryCache.get(staticSymbol);
|
||||||
if (!summary) {
|
if (!summary) {
|
||||||
this._loadSummaryFile(staticSymbol.filePath);
|
this._loadSummaryFile(staticSymbol.filePath);
|
||||||
@ -64,17 +54,12 @@ export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
|
|||||||
return Array.from(this.summaryCache.keys()).filter((symbol) => symbol.filePath === filePath);
|
return Array.from(this.summaryCache.keys()).filter((symbol) => symbol.filePath === filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
getImportAs(staticSymbol: StaticSymbol): StaticSymbol {
|
|
||||||
staticSymbol.assertNoMembers();
|
|
||||||
return this.importAs.get(staticSymbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _loadSummaryFile(filePath: string) {
|
private _loadSummaryFile(filePath: string) {
|
||||||
if (this.loadedFilePaths.has(filePath)) {
|
if (this.loadedFilePaths.has(filePath)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.loadedFilePaths.add(filePath);
|
this.loadedFilePaths.add(filePath);
|
||||||
if (this.isLibraryFile(filePath)) {
|
if (!this.host.isSourceFile(filePath)) {
|
||||||
const summaryFilePath = summaryFileName(filePath);
|
const summaryFilePath = summaryFileName(filePath);
|
||||||
let json: string;
|
let json: string;
|
||||||
try {
|
try {
|
||||||
@ -84,13 +69,8 @@ export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
if (json) {
|
if (json) {
|
||||||
const {summaries, importAs} = deserializeSummaries(this.staticSymbolCache, json);
|
const readSummaries = deserializeSummaries(this.staticSymbolCache, json);
|
||||||
summaries.forEach((summary) => this.summaryCache.set(summary.symbol, summary));
|
readSummaries.forEach((summary) => { this.summaryCache.set(summary.symbol, summary); });
|
||||||
importAs.forEach((importAs) => {
|
|
||||||
this.importAs.set(
|
|
||||||
importAs.symbol,
|
|
||||||
this.staticSymbolCache.get(ngfactoryFilePath(filePath), importAs.importAs));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,21 +5,35 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {CompileDirectiveSummary, CompileIdentifierMetadata, CompileNgModuleSummary, CompilePipeSummary, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary, identifierModuleUrl, identifierName} from '../compile_metadata';
|
import {CompileNgModuleSummary, CompileSummaryKind, CompileTypeSummary} from '../compile_metadata';
|
||||||
import {Summary, SummaryResolver} from '../summary_resolver';
|
import {Summary, SummaryResolver} from '../summary_resolver';
|
||||||
import {ValueTransformer, visitValue} from '../util';
|
import {ValueTransformer, visitValue} from '../util';
|
||||||
|
|
||||||
import {GeneratedFile} from './generated_file';
|
|
||||||
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
||||||
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
|
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
|
||||||
|
|
||||||
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||||
|
|
||||||
|
|
||||||
|
export interface AotSummarySerializerHost {
|
||||||
|
/**
|
||||||
|
* Returns the output file path of a source file.
|
||||||
|
* E.g.
|
||||||
|
* `some_file.ts` -> `some_file.d.ts`
|
||||||
|
*/
|
||||||
|
getOutputFileName(sourceFilePath: string): string;
|
||||||
|
/**
|
||||||
|
* Returns whether a file is a source file or not.
|
||||||
|
*/
|
||||||
|
isSourceFile(sourceFilePath: string): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export function serializeSummaries(
|
export function serializeSummaries(
|
||||||
summaryResolver: SummaryResolver<StaticSymbol>, symbolResolver: StaticSymbolResolver,
|
host: AotSummarySerializerHost, summaryResolver: SummaryResolver<StaticSymbol>,
|
||||||
symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]):
|
symbolResolver: StaticSymbolResolver,
|
||||||
{json: string, exportAs: {symbol: StaticSymbol, exportAs: string}[]} {
|
|
||||||
const serializer = new Serializer(symbolResolver, summaryResolver);
|
symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]): string {
|
||||||
|
const serializer = new Serializer(host);
|
||||||
|
|
||||||
// for symbols, we use everything except for the class metadata itself
|
// for symbols, we use everything except for the class metadata itself
|
||||||
// (we keep the statics though), as the class metadata is contained in the
|
// (we keep the statics though), as the class metadata is contained in the
|
||||||
@ -32,7 +46,7 @@ export function serializeSummaries(
|
|||||||
// we execute the loop!
|
// we execute the loop!
|
||||||
for (let processedIndex = 0; processedIndex < serializer.symbols.length; processedIndex++) {
|
for (let processedIndex = 0; processedIndex < serializer.symbols.length; processedIndex++) {
|
||||||
const symbol = serializer.symbols[processedIndex];
|
const symbol = serializer.symbols[processedIndex];
|
||||||
if (summaryResolver.isLibraryFile(symbol.filePath)) {
|
if (!host.isSourceFile(symbol.filePath)) {
|
||||||
let summary = summaryResolver.resolveSummary(symbol);
|
let summary = summaryResolver.resolveSummary(symbol);
|
||||||
if (!summary) {
|
if (!summary) {
|
||||||
// some symbols might originate from a plain typescript library
|
// some symbols might originate from a plain typescript library
|
||||||
@ -60,11 +74,8 @@ export function serializeSummaries(
|
|||||||
const ngModuleSummary = <CompileNgModuleSummary>typeSummary;
|
const ngModuleSummary = <CompileNgModuleSummary>typeSummary;
|
||||||
ngModuleSummary.exportedDirectives.concat(ngModuleSummary.exportedPipes).forEach((id) => {
|
ngModuleSummary.exportedDirectives.concat(ngModuleSummary.exportedPipes).forEach((id) => {
|
||||||
const symbol: StaticSymbol = id.reference;
|
const symbol: StaticSymbol = id.reference;
|
||||||
if (summaryResolver.isLibraryFile(symbol.filePath)) {
|
if (!host.isSourceFile(symbol.filePath)) {
|
||||||
const summary = summaryResolver.resolveSummary(symbol);
|
serializer.addOrMergeSummary(summaryResolver.resolveSummary(symbol));
|
||||||
if (summary) {
|
|
||||||
serializer.addOrMergeSummary(summary);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -72,14 +83,18 @@ export function serializeSummaries(
|
|||||||
return serializer.serialize();
|
return serializer.serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deserializeSummaries(symbolCache: StaticSymbolCache, json: string):
|
export function deserializeSummaries(
|
||||||
{summaries: Summary<StaticSymbol>[], importAs: {symbol: StaticSymbol, importAs: string}[]} {
|
symbolCache: StaticSymbolCache, json: string): Summary<StaticSymbol>[] {
|
||||||
const deserializer = new Deserializer(symbolCache);
|
const deserializer = new Deserializer(symbolCache);
|
||||||
return deserializer.deserialize(json);
|
return deserializer.deserialize(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function summaryFileName(fileName: string): string {
|
||||||
|
const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
|
||||||
|
return `${fileNameWithoutSuffix}.ngsummary.json`;
|
||||||
|
}
|
||||||
|
|
||||||
class Serializer extends ValueTransformer {
|
class Serializer extends ValueTransformer {
|
||||||
// Note: This only contains symbols without members.
|
|
||||||
symbols: StaticSymbol[] = [];
|
symbols: StaticSymbol[] = [];
|
||||||
private indexBySymbol = new Map<StaticSymbol, number>();
|
private indexBySymbol = new Map<StaticSymbol, number>();
|
||||||
// This now contains a `__symbol: number` in the place of
|
// This now contains a `__symbol: number` in the place of
|
||||||
@ -87,11 +102,7 @@ class Serializer extends ValueTransformer {
|
|||||||
private processedSummaryBySymbol = new Map<StaticSymbol, any>();
|
private processedSummaryBySymbol = new Map<StaticSymbol, any>();
|
||||||
private processedSummaries: any[] = [];
|
private processedSummaries: any[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(private host: AotSummarySerializerHost) { super(); }
|
||||||
private symbolResolver: StaticSymbolResolver,
|
|
||||||
private summaryResolver: SummaryResolver<StaticSymbol>) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
addOrMergeSummary(summary: Summary<StaticSymbol>) {
|
addOrMergeSummary(summary: Summary<StaticSymbol>) {
|
||||||
let symbolMeta = summary.metadata;
|
let symbolMeta = summary.metadata;
|
||||||
@ -118,44 +129,34 @@ class Serializer extends ValueTransformer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(): {json: string, exportAs: {symbol: StaticSymbol, exportAs: string}[]} {
|
serialize(): string {
|
||||||
const exportAs: {symbol: StaticSymbol, exportAs: string}[] = [];
|
return JSON.stringify({
|
||||||
const json = JSON.stringify({
|
|
||||||
summaries: this.processedSummaries,
|
summaries: this.processedSummaries,
|
||||||
symbols: this.symbols.map((symbol, index) => {
|
symbols: this.symbols.map((symbol, index) => {
|
||||||
symbol.assertNoMembers();
|
|
||||||
let importAs: string;
|
|
||||||
if (this.summaryResolver.isLibraryFile(symbol.filePath)) {
|
|
||||||
importAs = `${symbol.name}_${index}`;
|
|
||||||
exportAs.push({symbol, exportAs: importAs});
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
__symbol: index,
|
__symbol: index,
|
||||||
name: symbol.name,
|
name: symbol.name,
|
||||||
// We convert the source filenames tinto output filenames,
|
// We convert the source filenames tinto output filenames,
|
||||||
// as the generated summary file will be used when teh current
|
// as the generated summary file will be used when teh current
|
||||||
// compilation unit is used as a library
|
// compilation unit is used as a library
|
||||||
filePath: this.summaryResolver.getLibraryFileName(symbol.filePath),
|
filePath: this.host.getOutputFileName(symbol.filePath)
|
||||||
importAs: importAs
|
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
return {json, exportAs};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private processValue(value: any): any { return visitValue(value, this, null); }
|
private processValue(value: any): any { return visitValue(value, this, null); }
|
||||||
|
|
||||||
visitOther(value: any, context: any): any {
|
visitOther(value: any, context: any): any {
|
||||||
if (value instanceof StaticSymbol) {
|
if (value instanceof StaticSymbol) {
|
||||||
const baseSymbol = this.symbolResolver.getStaticSymbol(value.filePath, value.name);
|
let index = this.indexBySymbol.get(value);
|
||||||
let index = this.indexBySymbol.get(baseSymbol);
|
|
||||||
// Note: == by purpose to compare with undefined!
|
// Note: == by purpose to compare with undefined!
|
||||||
if (index == null) {
|
if (index == null) {
|
||||||
index = this.indexBySymbol.size;
|
index = this.indexBySymbol.size;
|
||||||
this.indexBySymbol.set(baseSymbol, index);
|
this.indexBySymbol.set(value, index);
|
||||||
this.symbols.push(baseSymbol);
|
this.symbols.push(value);
|
||||||
}
|
}
|
||||||
return {__symbol: index, members: value.members};
|
return {__symbol: index};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,30 +166,18 @@ class Deserializer extends ValueTransformer {
|
|||||||
|
|
||||||
constructor(private symbolCache: StaticSymbolCache) { super(); }
|
constructor(private symbolCache: StaticSymbolCache) { super(); }
|
||||||
|
|
||||||
deserialize(json: string):
|
deserialize(json: string): Summary<StaticSymbol>[] {
|
||||||
{summaries: Summary<StaticSymbol>[], importAs: {symbol: StaticSymbol, importAs: string}[]} {
|
|
||||||
const data: {summaries: any[], symbols: any[]} = JSON.parse(json);
|
const data: {summaries: any[], symbols: any[]} = JSON.parse(json);
|
||||||
const importAs: {symbol: StaticSymbol, importAs: string}[] = [];
|
this.symbols = data.symbols.map(
|
||||||
this.symbols = [];
|
serializedSymbol => this.symbolCache.get(serializedSymbol.filePath, serializedSymbol.name));
|
||||||
data.symbols.forEach((serializedSymbol) => {
|
return visitValue(data.summaries, this, null);
|
||||||
const symbol = this.symbolCache.get(serializedSymbol.filePath, serializedSymbol.name);
|
|
||||||
this.symbols.push(symbol);
|
|
||||||
if (serializedSymbol.importAs) {
|
|
||||||
importAs.push({symbol: symbol, importAs: serializedSymbol.importAs});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const summaries = visitValue(data.summaries, this, null);
|
|
||||||
return {summaries, importAs};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visitStringMap(map: {[key: string]: any}, context: any): any {
|
visitStringMap(map: {[key: string]: any}, context: any): any {
|
||||||
if ('__symbol' in map) {
|
if ('__symbol' in map) {
|
||||||
const baseSymbol = this.symbols[map['__symbol']];
|
return this.symbols[map['__symbol']];
|
||||||
const members = map['members'];
|
|
||||||
return members.length ? this.symbolCache.get(baseSymbol.filePath, baseSymbol.name, members) :
|
|
||||||
baseSymbol;
|
|
||||||
} else {
|
} else {
|
||||||
return super.visitStringMap(map, context);
|
return super.visitStringMap(map, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +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 STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
|
||||||
|
|
||||||
export function ngfactoryFilePath(filePath: string): string {
|
|
||||||
const urlWithSuffix = splitTypescriptSuffix(filePath);
|
|
||||||
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function stripNgFactory(filePath: string): string {
|
|
||||||
return filePath.replace(/\.ngfactory\./, '.');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function splitTypescriptSuffix(path: string): string[] {
|
|
||||||
if (path.endsWith('.d.ts')) {
|
|
||||||
return [path.slice(0, -5), '.ts'];
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastDot = path.lastIndexOf('.');
|
|
||||||
|
|
||||||
if (lastDot !== -1) {
|
|
||||||
return [path.substring(0, lastDot), path.substring(lastDot)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [path, ''];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function summaryFileName(fileName: string): string {
|
|
||||||
const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
|
|
||||||
return `${fileNameWithoutSuffix}.ngsummary.json`;
|
|
||||||
}
|
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ChangeDetectionStrategy, ComponentFactory, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
|
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
import {StaticSymbol} from './aot/static_symbol';
|
import {StaticSymbol} from './aot/static_symbol';
|
||||||
import {ListWrapper} from './facade/collection';
|
import {ListWrapper} from './facade/collection';
|
||||||
@ -15,10 +15,6 @@ import {LifecycleHooks, reflector} from './private_import_core';
|
|||||||
import {CssSelector} from './selector';
|
import {CssSelector} from './selector';
|
||||||
import {splitAtColon} from './util';
|
import {splitAtColon} from './util';
|
||||||
|
|
||||||
function unimplemented(): any {
|
|
||||||
throw new Error('unimplemented');
|
|
||||||
}
|
|
||||||
|
|
||||||
// group 0: "[prop] or (event) or @trigger"
|
// group 0: "[prop] or (event) or @trigger"
|
||||||
// group 1: "prop" from "[prop]"
|
// group 1: "prop" from "[prop]"
|
||||||
// group 2: "event" from "(event)"
|
// group 2: "event" from "(event)"
|
||||||
@ -112,24 +108,6 @@ export function identifierModuleUrl(compileIdentifier: CompileIdentifierMetadata
|
|||||||
return reflector.importUri(ref);
|
return reflector.importUri(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function viewClassName(compType: any, embeddedTemplateIndex: number): string {
|
|
||||||
return `View_${identifierName({reference: compType})}_${embeddedTemplateIndex}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hostViewClassName(compType: any): string {
|
|
||||||
return `HostView_${identifierName({reference: compType})}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function dirWrapperClassName(dirType: any) {
|
|
||||||
return `Wrapper_${identifierName({reference: dirType})}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function componentFactoryName(compType: any): string {
|
|
||||||
return `${identifierName({reference: compType})}NgFactory`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ProxyClass { setDelegate(delegate: any): void; }
|
|
||||||
|
|
||||||
export interface CompileIdentifierMetadata { reference: any; }
|
export interface CompileIdentifierMetadata { reference: any; }
|
||||||
|
|
||||||
export enum CompileSummaryKind {
|
export enum CompileSummaryKind {
|
||||||
@ -259,7 +237,7 @@ export class CompileTemplateMetadata {
|
|||||||
externalStylesheets?: CompileStylesheetMetadata[],
|
externalStylesheets?: CompileStylesheetMetadata[],
|
||||||
ngContentSelectors?: string[],
|
ngContentSelectors?: string[],
|
||||||
animations?: CompileAnimationEntryMetadata[],
|
animations?: CompileAnimationEntryMetadata[],
|
||||||
interpolation?: [string, string],
|
interpolation?: [string, string]
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.encapsulation = encapsulation;
|
this.encapsulation = encapsulation;
|
||||||
this.template = template;
|
this.template = template;
|
||||||
@ -279,16 +257,11 @@ export class CompileTemplateMetadata {
|
|||||||
return {
|
return {
|
||||||
animations: this.animations.map(anim => anim.name),
|
animations: this.animations.map(anim => anim.name),
|
||||||
ngContentSelectors: this.ngContentSelectors,
|
ngContentSelectors: this.ngContentSelectors,
|
||||||
encapsulation: this.encapsulation,
|
encapsulation: this.encapsulation
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompileEntryComponentMetadata {
|
|
||||||
componentType: any;
|
|
||||||
componentFactory: StaticSymbol|ComponentFactory<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: This should only use interfaces as nested data types
|
// Note: This should only use interfaces as nested data types
|
||||||
// as we need to be able to serialize this from/to JSON!
|
// as we need to be able to serialize this from/to JSON!
|
||||||
export interface CompileDirectiveSummary extends CompileTypeSummary {
|
export interface CompileDirectiveSummary extends CompileTypeSummary {
|
||||||
@ -304,12 +277,9 @@ export interface CompileDirectiveSummary extends CompileTypeSummary {
|
|||||||
providers: CompileProviderMetadata[];
|
providers: CompileProviderMetadata[];
|
||||||
viewProviders: CompileProviderMetadata[];
|
viewProviders: CompileProviderMetadata[];
|
||||||
queries: CompileQueryMetadata[];
|
queries: CompileQueryMetadata[];
|
||||||
entryComponents: CompileEntryComponentMetadata[];
|
entryComponents: CompileIdentifierMetadata[];
|
||||||
changeDetection: ChangeDetectionStrategy;
|
changeDetection: ChangeDetectionStrategy;
|
||||||
template: CompileTemplateSummary;
|
template: CompileTemplateSummary;
|
||||||
wrapperType: StaticSymbol|ProxyClass;
|
|
||||||
componentViewType: StaticSymbol|ProxyClass;
|
|
||||||
componentFactory: StaticSymbol|ComponentFactory<any>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -318,8 +288,7 @@ export interface CompileDirectiveSummary extends CompileTypeSummary {
|
|||||||
export class CompileDirectiveMetadata {
|
export class CompileDirectiveMetadata {
|
||||||
static create(
|
static create(
|
||||||
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host,
|
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host,
|
||||||
providers, viewProviders, queries, viewQueries, entryComponents, template, wrapperType,
|
providers, viewProviders, queries, viewQueries, entryComponents, template}: {
|
||||||
componentViewType, componentFactory}: {
|
|
||||||
isHost?: boolean,
|
isHost?: boolean,
|
||||||
type?: CompileTypeMetadata,
|
type?: CompileTypeMetadata,
|
||||||
isComponent?: boolean,
|
isComponent?: boolean,
|
||||||
@ -333,11 +302,8 @@ export class CompileDirectiveMetadata {
|
|||||||
viewProviders?: CompileProviderMetadata[],
|
viewProviders?: CompileProviderMetadata[],
|
||||||
queries?: CompileQueryMetadata[],
|
queries?: CompileQueryMetadata[],
|
||||||
viewQueries?: CompileQueryMetadata[],
|
viewQueries?: CompileQueryMetadata[],
|
||||||
entryComponents?: CompileEntryComponentMetadata[],
|
entryComponents?: CompileIdentifierMetadata[],
|
||||||
template?: CompileTemplateMetadata,
|
template?: CompileTemplateMetadata
|
||||||
wrapperType?: StaticSymbol|ProxyClass,
|
|
||||||
componentViewType?: StaticSymbol|ProxyClass,
|
|
||||||
componentFactory?: StaticSymbol|ComponentFactory<any>,
|
|
||||||
} = {}): CompileDirectiveMetadata {
|
} = {}): CompileDirectiveMetadata {
|
||||||
const hostListeners: {[key: string]: string} = {};
|
const hostListeners: {[key: string]: string} = {};
|
||||||
const hostProperties: {[key: string]: string} = {};
|
const hostProperties: {[key: string]: string} = {};
|
||||||
@ -389,9 +355,6 @@ export class CompileDirectiveMetadata {
|
|||||||
viewQueries,
|
viewQueries,
|
||||||
entryComponents,
|
entryComponents,
|
||||||
template,
|
template,
|
||||||
wrapperType,
|
|
||||||
componentViewType,
|
|
||||||
componentFactory,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
isHost: boolean;
|
isHost: boolean;
|
||||||
@ -409,39 +372,32 @@ export class CompileDirectiveMetadata {
|
|||||||
viewProviders: CompileProviderMetadata[];
|
viewProviders: CompileProviderMetadata[];
|
||||||
queries: CompileQueryMetadata[];
|
queries: CompileQueryMetadata[];
|
||||||
viewQueries: CompileQueryMetadata[];
|
viewQueries: CompileQueryMetadata[];
|
||||||
entryComponents: CompileEntryComponentMetadata[];
|
entryComponents: CompileIdentifierMetadata[];
|
||||||
|
|
||||||
template: CompileTemplateMetadata;
|
template: CompileTemplateMetadata;
|
||||||
|
|
||||||
wrapperType: StaticSymbol|ProxyClass;
|
constructor(
|
||||||
componentViewType: StaticSymbol|ProxyClass;
|
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs,
|
||||||
componentFactory: StaticSymbol|ComponentFactory<any>;
|
hostListeners, hostProperties, hostAttributes, providers, viewProviders, queries,
|
||||||
|
viewQueries, entryComponents, template}: {
|
||||||
constructor({isHost, type, isComponent, selector, exportAs,
|
isHost?: boolean,
|
||||||
changeDetection, inputs, outputs, hostListeners, hostProperties,
|
type?: CompileTypeMetadata,
|
||||||
hostAttributes, providers, viewProviders, queries, viewQueries,
|
isComponent?: boolean,
|
||||||
entryComponents, template, wrapperType, componentViewType, componentFactory}: {
|
selector?: string,
|
||||||
isHost?: boolean,
|
exportAs?: string,
|
||||||
type?: CompileTypeMetadata,
|
changeDetection?: ChangeDetectionStrategy,
|
||||||
isComponent?: boolean,
|
inputs?: {[key: string]: string},
|
||||||
selector?: string,
|
outputs?: {[key: string]: string},
|
||||||
exportAs?: string,
|
hostListeners?: {[key: string]: string},
|
||||||
changeDetection?: ChangeDetectionStrategy,
|
hostProperties?: {[key: string]: string},
|
||||||
inputs?: {[key: string]: string},
|
hostAttributes?: {[key: string]: string},
|
||||||
outputs?: {[key: string]: string},
|
providers?: CompileProviderMetadata[],
|
||||||
hostListeners?: {[key: string]: string},
|
viewProviders?: CompileProviderMetadata[],
|
||||||
hostProperties?: {[key: string]: string},
|
queries?: CompileQueryMetadata[],
|
||||||
hostAttributes?: {[key: string]: string},
|
viewQueries?: CompileQueryMetadata[],
|
||||||
providers?: CompileProviderMetadata[],
|
entryComponents?: CompileIdentifierMetadata[],
|
||||||
viewProviders?: CompileProviderMetadata[],
|
template?: CompileTemplateMetadata,
|
||||||
queries?: CompileQueryMetadata[],
|
} = {}) {
|
||||||
viewQueries?: CompileQueryMetadata[],
|
|
||||||
entryComponents?: CompileEntryComponentMetadata[],
|
|
||||||
template?: CompileTemplateMetadata,
|
|
||||||
wrapperType?: StaticSymbol|ProxyClass,
|
|
||||||
componentViewType?: StaticSymbol|ProxyClass,
|
|
||||||
componentFactory?: StaticSymbol|ComponentFactory<any>,
|
|
||||||
} = {}) {
|
|
||||||
this.isHost = !!isHost;
|
this.isHost = !!isHost;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.isComponent = isComponent;
|
this.isComponent = isComponent;
|
||||||
@ -458,11 +414,8 @@ export class CompileDirectiveMetadata {
|
|||||||
this.queries = _normalizeArray(queries);
|
this.queries = _normalizeArray(queries);
|
||||||
this.viewQueries = _normalizeArray(viewQueries);
|
this.viewQueries = _normalizeArray(viewQueries);
|
||||||
this.entryComponents = _normalizeArray(entryComponents);
|
this.entryComponents = _normalizeArray(entryComponents);
|
||||||
this.template = template;
|
|
||||||
|
|
||||||
this.wrapperType = wrapperType;
|
this.template = template;
|
||||||
this.componentViewType = componentViewType;
|
|
||||||
this.componentFactory = componentFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toSummary(): CompileDirectiveSummary {
|
toSummary(): CompileDirectiveSummary {
|
||||||
@ -482,10 +435,7 @@ export class CompileDirectiveMetadata {
|
|||||||
queries: this.queries,
|
queries: this.queries,
|
||||||
entryComponents: this.entryComponents,
|
entryComponents: this.entryComponents,
|
||||||
changeDetection: this.changeDetection,
|
changeDetection: this.changeDetection,
|
||||||
template: this.template && this.template.toSummary(),
|
template: this.template && this.template.toSummary()
|
||||||
wrapperType: this.wrapperType,
|
|
||||||
componentViewType: this.componentViewType,
|
|
||||||
componentFactory: this.componentFactory
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -494,12 +444,11 @@ export class CompileDirectiveMetadata {
|
|||||||
* Construct {@link CompileDirectiveMetadata} from {@link ComponentTypeMetadata} and a selector.
|
* Construct {@link CompileDirectiveMetadata} from {@link ComponentTypeMetadata} and a selector.
|
||||||
*/
|
*/
|
||||||
export function createHostComponentMeta(
|
export function createHostComponentMeta(
|
||||||
hostTypeReference: any, compMeta: CompileDirectiveMetadata,
|
typeReference: any, compMeta: CompileDirectiveMetadata): CompileDirectiveMetadata {
|
||||||
hostViewType: StaticSymbol | ProxyClass): CompileDirectiveMetadata {
|
|
||||||
const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
|
const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
|
||||||
return CompileDirectiveMetadata.create({
|
return CompileDirectiveMetadata.create({
|
||||||
isHost: true,
|
isHost: true,
|
||||||
type: {reference: hostTypeReference, diDeps: [], lifecycleHooks: []},
|
type: {reference: typeReference, diDeps: [], lifecycleHooks: []},
|
||||||
template: new CompileTemplateMetadata({
|
template: new CompileTemplateMetadata({
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
template: template,
|
template: template,
|
||||||
@ -518,8 +467,7 @@ export function createHostComponentMeta(
|
|||||||
providers: [],
|
providers: [],
|
||||||
viewProviders: [],
|
viewProviders: [],
|
||||||
queries: [],
|
queries: [],
|
||||||
viewQueries: [],
|
viewQueries: []
|
||||||
componentViewType: hostViewType
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,7 +513,7 @@ export interface CompileNgModuleSummary extends CompileTypeSummary {
|
|||||||
exportedPipes: CompileIdentifierMetadata[];
|
exportedPipes: CompileIdentifierMetadata[];
|
||||||
|
|
||||||
// Note: This is transitive.
|
// Note: This is transitive.
|
||||||
entryComponents: CompileEntryComponentMetadata[];
|
entryComponents: CompileIdentifierMetadata[];
|
||||||
// Note: This is transitive.
|
// Note: This is transitive.
|
||||||
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[];
|
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[];
|
||||||
// Note: This is transitive.
|
// Note: This is transitive.
|
||||||
@ -582,7 +530,7 @@ export class CompileNgModuleMetadata {
|
|||||||
declaredPipes: CompileIdentifierMetadata[];
|
declaredPipes: CompileIdentifierMetadata[];
|
||||||
|
|
||||||
exportedPipes: CompileIdentifierMetadata[];
|
exportedPipes: CompileIdentifierMetadata[];
|
||||||
entryComponents: CompileEntryComponentMetadata[];
|
entryComponents: CompileIdentifierMetadata[];
|
||||||
bootstrapComponents: CompileIdentifierMetadata[];
|
bootstrapComponents: CompileIdentifierMetadata[];
|
||||||
providers: CompileProviderMetadata[];
|
providers: CompileProviderMetadata[];
|
||||||
|
|
||||||
@ -603,7 +551,7 @@ export class CompileNgModuleMetadata {
|
|||||||
exportedDirectives?: CompileIdentifierMetadata[],
|
exportedDirectives?: CompileIdentifierMetadata[],
|
||||||
declaredPipes?: CompileIdentifierMetadata[],
|
declaredPipes?: CompileIdentifierMetadata[],
|
||||||
exportedPipes?: CompileIdentifierMetadata[],
|
exportedPipes?: CompileIdentifierMetadata[],
|
||||||
entryComponents?: CompileEntryComponentMetadata[],
|
entryComponents?: CompileIdentifierMetadata[],
|
||||||
bootstrapComponents?: CompileIdentifierMetadata[],
|
bootstrapComponents?: CompileIdentifierMetadata[],
|
||||||
importedModules?: CompileNgModuleSummary[],
|
importedModules?: CompileNgModuleSummary[],
|
||||||
exportedModules?: CompileNgModuleSummary[],
|
exportedModules?: CompileNgModuleSummary[],
|
||||||
@ -651,7 +599,7 @@ export class TransitiveCompileNgModuleMetadata {
|
|||||||
modulesSet = new Set<any>();
|
modulesSet = new Set<any>();
|
||||||
modules: CompileTypeMetadata[] = [];
|
modules: CompileTypeMetadata[] = [];
|
||||||
entryComponentsSet = new Set<any>();
|
entryComponentsSet = new Set<any>();
|
||||||
entryComponents: CompileEntryComponentMetadata[] = [];
|
entryComponents: CompileIdentifierMetadata[] = [];
|
||||||
|
|
||||||
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[] = [];
|
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[] = [];
|
||||||
|
|
||||||
@ -689,10 +637,10 @@ export class TransitiveCompileNgModuleMetadata {
|
|||||||
this.modules.push(id);
|
this.modules.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addEntryComponent(ec: CompileEntryComponentMetadata) {
|
addEntryComponent(id: CompileIdentifierMetadata) {
|
||||||
if (!this.entryComponentsSet.has(ec.componentType)) {
|
if (!this.entryComponentsSet.has(id.reference)) {
|
||||||
this.entryComponentsSet.add(ec.componentType);
|
this.entryComponentsSet.add(id.reference);
|
||||||
this.entryComponents.push(ec);
|
this.entryComponents.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Identifiers, createIdentifier} from '../identifiers';
|
import {Identifiers, createIdentifier} from '../identifiers';
|
||||||
import {ClassBuilder} from '../output/class_builder';
|
import {ClassBuilder} from '../output/class_builder';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
@ -21,14 +20,28 @@ export function createCheckBindingField(builder: ClassBuilder): CheckBindingFiel
|
|||||||
const fieldExpr = createBindFieldExpr(bindingId);
|
const fieldExpr = createBindFieldExpr(bindingId);
|
||||||
// private is fine here as no child view will reference the cached value...
|
// private is fine here as no child view will reference the cached value...
|
||||||
builder.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
|
builder.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
|
||||||
builder.ctorStmts.push(o.THIS_EXPR.prop(fieldExpr.name).set(o.literal(undefined)).toStmt());
|
builder.ctorStmts.push(o.THIS_EXPR.prop(fieldExpr.name)
|
||||||
|
.set(o.importExpr(createIdentifier(Identifiers.UNINITIALIZED)))
|
||||||
|
.toStmt());
|
||||||
return new CheckBindingField(fieldExpr, bindingId);
|
return new CheckBindingField(fieldExpr, bindingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createCheckBindingStmt(
|
||||||
|
evalResult: ConvertPropertyBindingResult, fieldExpr: o.ReadPropExpr,
|
||||||
|
throwOnChangeVar: o.Expression, actions: o.Statement[]): o.Statement[] {
|
||||||
|
let condition: o.Expression = o.importExpr(createIdentifier(Identifiers.checkBinding)).callFn([
|
||||||
|
throwOnChangeVar, fieldExpr, evalResult.currValExpr
|
||||||
|
]);
|
||||||
|
if (evalResult.forceUpdate) {
|
||||||
|
condition = evalResult.forceUpdate.or(condition);
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
...evalResult.stmts, new o.IfStmt(condition, actions.concat([
|
||||||
|
<o.Statement>o.THIS_EXPR.prop(fieldExpr.name).set(evalResult.currValExpr).toStmt()
|
||||||
|
]))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
function createBindFieldExpr(bindingId: string): o.ReadPropExpr {
|
function createBindFieldExpr(bindingId: string): o.ReadPropExpr {
|
||||||
return o.THIS_EXPR.prop(`_expr_${bindingId}`);
|
return o.THIS_EXPR.prop(`_expr_${bindingId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isFirstViewCheck(view: o.Expression): o.Expression {
|
|
||||||
return o.not(view.prop('numberOfChecks'));
|
|
||||||
}
|
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import * as cdAst from '../expression_parser/ast';
|
import * as cdAst from '../expression_parser/ast';
|
||||||
import {isBlank, isPresent} from '../facade/lang';
|
import {isBlank} from '../facade/lang';
|
||||||
import {Identifiers, createIdentifier} from '../identifiers';
|
import {Identifiers, createIdentifier} from '../identifiers';
|
||||||
import {ClassBuilder} from '../output/class_builder';
|
import {ClassBuilder} from '../output/class_builder';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
@ -338,7 +338,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
const receiver = this.visit(ast.receiver, _Mode.Expression);
|
const receiver = this.visit(ast.receiver, _Mode.Expression);
|
||||||
if (receiver === this._implicitReceiver) {
|
if (receiver === this._implicitReceiver) {
|
||||||
const varExpr = this._getLocal(ast.name);
|
const varExpr = this._getLocal(ast.name);
|
||||||
if (isPresent(varExpr)) {
|
if (varExpr) {
|
||||||
result = varExpr.callFn(args);
|
result = varExpr.callFn(args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -374,7 +374,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
const receiver: o.Expression = this.visit(ast.receiver, _Mode.Expression);
|
const receiver: o.Expression = this.visit(ast.receiver, _Mode.Expression);
|
||||||
if (receiver === this._implicitReceiver) {
|
if (receiver === this._implicitReceiver) {
|
||||||
const varExpr = this._getLocal(ast.name);
|
const varExpr = this._getLocal(ast.name);
|
||||||
if (isPresent(varExpr)) {
|
if (varExpr) {
|
||||||
throw new Error('Cannot assign to a reference or variable!');
|
throw new Error('Cannot assign to a reference or variable!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,64 +13,69 @@ import * as o from '../output/output_ast';
|
|||||||
import {EMPTY_STATE as EMPTY_ANIMATION_STATE} from '../private_import_core';
|
import {EMPTY_STATE as EMPTY_ANIMATION_STATE} from '../private_import_core';
|
||||||
import {BoundElementPropertyAst, BoundEventAst, PropertyBindingType} from '../template_parser/template_ast';
|
import {BoundElementPropertyAst, BoundEventAst, PropertyBindingType} from '../template_parser/template_ast';
|
||||||
|
|
||||||
import {isFirstViewCheck} from './binding_util';
|
|
||||||
import {ConvertPropertyBindingResult} from './expression_converter';
|
|
||||||
import {createEnumExpression} from './identifier_util';
|
import {createEnumExpression} from './identifier_util';
|
||||||
|
|
||||||
export function createCheckRenderBindingStmt(
|
export function writeToRenderer(
|
||||||
view: o.Expression, renderElement: o.Expression, boundProp: BoundElementPropertyAst,
|
view: o.Expression, boundProp: BoundElementPropertyAst, renderElement: o.Expression,
|
||||||
oldValue: o.ReadPropExpr, evalResult: ConvertPropertyBindingResult,
|
renderValue: o.Expression, logBindingUpdate: boolean,
|
||||||
securityContextExpression?: o.Expression): o.Statement[] {
|
securityContextExpression?: o.Expression): o.Statement[] {
|
||||||
const checkStmts: o.Statement[] = [...evalResult.stmts];
|
const updateStmts: o.Statement[] = [];
|
||||||
const securityContext = calcSecurityContext(boundProp, securityContextExpression);
|
const renderer = view.prop('renderer');
|
||||||
|
renderValue = sanitizedValue(view, boundProp, renderValue, securityContextExpression);
|
||||||
switch (boundProp.type) {
|
switch (boundProp.type) {
|
||||||
case PropertyBindingType.Property:
|
case PropertyBindingType.Property:
|
||||||
checkStmts.push(o.importExpr(createIdentifier(Identifiers.checkRenderProperty))
|
if (logBindingUpdate) {
|
||||||
.callFn([
|
updateStmts.push(
|
||||||
view, renderElement, o.literal(boundProp.name), oldValue,
|
o.importExpr(createIdentifier(Identifiers.setBindingDebugInfo))
|
||||||
oldValue.set(evalResult.currValExpr),
|
.callFn([renderer, renderElement, o.literal(boundProp.name), renderValue])
|
||||||
evalResult.forceUpdate || o.literal(false), securityContext
|
.toStmt());
|
||||||
])
|
}
|
||||||
.toStmt());
|
updateStmts.push(
|
||||||
|
renderer
|
||||||
|
.callMethod(
|
||||||
|
'setElementProperty', [renderElement, o.literal(boundProp.name), renderValue])
|
||||||
|
.toStmt());
|
||||||
break;
|
break;
|
||||||
case PropertyBindingType.Attribute:
|
case PropertyBindingType.Attribute:
|
||||||
checkStmts.push(o.importExpr(createIdentifier(Identifiers.checkRenderAttribute))
|
renderValue =
|
||||||
.callFn([
|
renderValue.isBlank().conditional(o.NULL_EXPR, renderValue.callMethod('toString', []));
|
||||||
view, renderElement, o.literal(boundProp.name), oldValue,
|
updateStmts.push(
|
||||||
oldValue.set(evalResult.currValExpr),
|
renderer
|
||||||
evalResult.forceUpdate || o.literal(false), securityContext
|
.callMethod(
|
||||||
])
|
'setElementAttribute', [renderElement, o.literal(boundProp.name), renderValue])
|
||||||
.toStmt());
|
.toStmt());
|
||||||
break;
|
break;
|
||||||
case PropertyBindingType.Class:
|
case PropertyBindingType.Class:
|
||||||
checkStmts.push(
|
updateStmts.push(
|
||||||
o.importExpr(createIdentifier(Identifiers.checkRenderClass))
|
renderer
|
||||||
.callFn([
|
.callMethod(
|
||||||
view, renderElement, o.literal(boundProp.name), oldValue,
|
'setElementClass', [renderElement, o.literal(boundProp.name), renderValue])
|
||||||
oldValue.set(evalResult.currValExpr), evalResult.forceUpdate || o.literal(false)
|
|
||||||
])
|
|
||||||
.toStmt());
|
.toStmt());
|
||||||
break;
|
break;
|
||||||
case PropertyBindingType.Style:
|
case PropertyBindingType.Style:
|
||||||
checkStmts.push(
|
let strValue: o.Expression = renderValue.callMethod('toString', []);
|
||||||
o.importExpr(createIdentifier(Identifiers.checkRenderStyle))
|
if (isPresent(boundProp.unit)) {
|
||||||
.callFn([
|
strValue = strValue.plus(o.literal(boundProp.unit));
|
||||||
view, renderElement, o.literal(boundProp.name), o.literal(boundProp.unit), oldValue,
|
}
|
||||||
oldValue.set(evalResult.currValExpr), evalResult.forceUpdate || o.literal(false),
|
|
||||||
securityContext
|
renderValue = renderValue.isBlank().conditional(o.NULL_EXPR, strValue);
|
||||||
])
|
updateStmts.push(
|
||||||
|
renderer
|
||||||
|
.callMethod(
|
||||||
|
'setElementStyle', [renderElement, o.literal(boundProp.name), renderValue])
|
||||||
.toStmt());
|
.toStmt());
|
||||||
break;
|
break;
|
||||||
case PropertyBindingType.Animation:
|
case PropertyBindingType.Animation:
|
||||||
throw new Error('Illegal state: Should not come here!');
|
throw new Error('Illegal state: Should not come here!');
|
||||||
}
|
}
|
||||||
return checkStmts;
|
return updateStmts;
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcSecurityContext(
|
function sanitizedValue(
|
||||||
boundProp: BoundElementPropertyAst, securityContextExpression?: o.Expression): o.Expression {
|
view: o.Expression, boundProp: BoundElementPropertyAst, renderValue: o.Expression,
|
||||||
|
securityContextExpression?: o.Expression): o.Expression {
|
||||||
if (boundProp.securityContext === SecurityContext.NONE) {
|
if (boundProp.securityContext === SecurityContext.NONE) {
|
||||||
return o.NULL_EXPR; // No sanitization needed.
|
return renderValue; // No sanitization needed.
|
||||||
}
|
}
|
||||||
if (!boundProp.needsRuntimeSecurityContext) {
|
if (!boundProp.needsRuntimeSecurityContext) {
|
||||||
securityContextExpression =
|
securityContextExpression =
|
||||||
@ -79,13 +84,15 @@ function calcSecurityContext(
|
|||||||
if (!securityContextExpression) {
|
if (!securityContextExpression) {
|
||||||
throw new Error(`internal error, no SecurityContext given ${boundProp.name}`);
|
throw new Error(`internal error, no SecurityContext given ${boundProp.name}`);
|
||||||
}
|
}
|
||||||
return securityContextExpression;
|
const ctx = view.prop('viewUtils').prop('sanitizer');
|
||||||
|
const args = [securityContextExpression, renderValue];
|
||||||
|
return ctx.callMethod('sanitize', args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createCheckAnimationBindingStmts(
|
export function triggerAnimation(
|
||||||
view: o.Expression, componentView: o.Expression, boundProp: BoundElementPropertyAst,
|
view: o.Expression, componentView: o.Expression, boundProp: BoundElementPropertyAst,
|
||||||
boundOutputs: BoundEventAst[], eventListener: o.Expression, renderElement: o.Expression,
|
boundOutputs: BoundEventAst[], eventListener: o.Expression, renderElement: o.Expression,
|
||||||
oldValue: o.ReadPropExpr, evalResult: ConvertPropertyBindingResult) {
|
renderValue: o.Expression, lastRenderValue: o.Expression) {
|
||||||
const detachStmts: o.Statement[] = [];
|
const detachStmts: o.Statement[] = [];
|
||||||
const updateStmts: o.Statement[] = [];
|
const updateStmts: o.Statement[] = [];
|
||||||
|
|
||||||
@ -97,21 +104,22 @@ export function createCheckAnimationBindingStmts(
|
|||||||
// it's important to normalize the void value as `void` explicitly
|
// it's important to normalize the void value as `void` explicitly
|
||||||
// so that the styles data can be obtained from the stringmap
|
// so that the styles data can be obtained from the stringmap
|
||||||
const emptyStateValue = o.literal(EMPTY_ANIMATION_STATE);
|
const emptyStateValue = o.literal(EMPTY_ANIMATION_STATE);
|
||||||
|
const unitializedValue = o.importExpr(createIdentifier(Identifiers.UNINITIALIZED));
|
||||||
const animationTransitionVar = o.variable('animationTransition_' + animationName);
|
const animationTransitionVar = o.variable('animationTransition_' + animationName);
|
||||||
|
|
||||||
updateStmts.push(
|
updateStmts.push(
|
||||||
animationTransitionVar
|
animationTransitionVar
|
||||||
.set(animationFnExpr.callFn([
|
.set(animationFnExpr.callFn([
|
||||||
view, renderElement, isFirstViewCheck(view).conditional(emptyStateValue, oldValue),
|
view, renderElement,
|
||||||
evalResult.currValExpr
|
lastRenderValue.equals(unitializedValue).conditional(emptyStateValue, lastRenderValue),
|
||||||
|
renderValue.equals(unitializedValue).conditional(emptyStateValue, renderValue)
|
||||||
]))
|
]))
|
||||||
.toDeclStmt());
|
.toDeclStmt());
|
||||||
updateStmts.push(oldValue.set(evalResult.currValExpr).toStmt());
|
|
||||||
|
|
||||||
detachStmts.push(animationTransitionVar
|
detachStmts.push(
|
||||||
.set(animationFnExpr.callFn(
|
animationTransitionVar
|
||||||
[view, renderElement, evalResult.currValExpr, emptyStateValue]))
|
.set(animationFnExpr.callFn([view, renderElement, lastRenderValue, emptyStateValue]))
|
||||||
.toDeclStmt());
|
.toDeclStmt());
|
||||||
|
|
||||||
const registerStmts: o.Statement[] = [];
|
const registerStmts: o.Statement[] = [];
|
||||||
const animationStartMethodExists = boundOutputs.find(
|
const animationStartMethodExists = boundOutputs.find(
|
||||||
@ -143,14 +151,5 @@ export function createCheckAnimationBindingStmts(
|
|||||||
updateStmts.push(...registerStmts);
|
updateStmts.push(...registerStmts);
|
||||||
detachStmts.push(...registerStmts);
|
detachStmts.push(...registerStmts);
|
||||||
|
|
||||||
const checkUpdateStmts: o.Statement[] = [
|
return {updateStmts, detachStmts};
|
||||||
...evalResult.stmts,
|
|
||||||
new o.IfStmt(
|
|
||||||
o.importExpr(createIdentifier(Identifiers.checkBinding)).callFn([
|
|
||||||
view, oldValue, evalResult.currValExpr, evalResult.forceUpdate || o.literal(false)
|
|
||||||
]),
|
|
||||||
updateStmts)
|
|
||||||
];
|
|
||||||
const checkDetachStmts: o.Statement[] = [...evalResult.stmts, ...detachStmts];
|
|
||||||
return {checkUpdateStmts, checkDetachStmts};
|
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,6 @@ import {ViewEncapsulation, isDevMode} from '@angular/core';
|
|||||||
import {CompileIdentifierMetadata} from './compile_metadata';
|
import {CompileIdentifierMetadata} from './compile_metadata';
|
||||||
import {Identifiers, createIdentifier} from './identifiers';
|
import {Identifiers, createIdentifier} from './identifiers';
|
||||||
|
|
||||||
function unimplemented(): any {
|
|
||||||
throw new Error('unimplemented');
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CompilerConfig {
|
export class CompilerConfig {
|
||||||
public renderTypes: RenderTypes;
|
public renderTypes: RenderTypes;
|
||||||
public defaultEncapsulation: ViewEncapsulation;
|
public defaultEncapsulation: ViewEncapsulation;
|
||||||
@ -52,12 +48,12 @@ export class CompilerConfig {
|
|||||||
* to help tree shaking.
|
* to help tree shaking.
|
||||||
*/
|
*/
|
||||||
export abstract class RenderTypes {
|
export abstract class RenderTypes {
|
||||||
get renderer(): CompileIdentifierMetadata { return unimplemented(); }
|
abstract get renderer(): CompileIdentifierMetadata;
|
||||||
get renderText(): CompileIdentifierMetadata { return unimplemented(); }
|
abstract get renderText(): CompileIdentifierMetadata;
|
||||||
get renderElement(): CompileIdentifierMetadata { return unimplemented(); }
|
abstract get renderElement(): CompileIdentifierMetadata;
|
||||||
get renderComment(): CompileIdentifierMetadata { return unimplemented(); }
|
abstract get renderComment(): CompileIdentifierMetadata;
|
||||||
get renderNode(): CompileIdentifierMetadata { return unimplemented(); }
|
abstract get renderNode(): CompileIdentifierMetadata;
|
||||||
get renderEvent(): CompileIdentifierMetadata { return unimplemented(); }
|
abstract get renderEvent(): CompileIdentifierMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DefaultRenderTypes implements RenderTypes {
|
export class DefaultRenderTypes implements RenderTypes {
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
|
|
||||||
import {ViewEncapsulation} from '@angular/core';
|
import {ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from './compile_metadata';
|
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata} from './compile_metadata';
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
import {stringify} from './facade/lang';
|
import {isBlank, isPresent, stringify} from './facade/lang';
|
||||||
import {CompilerInjectable} from './injectable';
|
import {CompilerInjectable} from './injectable';
|
||||||
import * as html from './ml_parser/ast';
|
import * as html from './ml_parser/ast';
|
||||||
import {HtmlParser} from './ml_parser/html_parser';
|
import {HtmlParser} from './ml_parser/html_parser';
|
||||||
@ -41,9 +41,9 @@ export class DirectiveNormalizer {
|
|||||||
private _resourceLoader: ResourceLoader, private _urlResolver: UrlResolver,
|
private _resourceLoader: ResourceLoader, private _urlResolver: UrlResolver,
|
||||||
private _htmlParser: HtmlParser, private _config: CompilerConfig) {}
|
private _htmlParser: HtmlParser, private _config: CompilerConfig) {}
|
||||||
|
|
||||||
clearCache(): void { this._resourceLoaderCache.clear(); }
|
clearCache() { this._resourceLoaderCache.clear(); }
|
||||||
|
|
||||||
clearCacheFor(normalizedDirective: CompileDirectiveMetadata): void {
|
clearCacheFor(normalizedDirective: CompileDirectiveMetadata) {
|
||||||
if (!normalizedDirective.isComponent) {
|
if (!normalizedDirective.isComponent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -65,18 +65,10 @@ export class DirectiveNormalizer {
|
|||||||
SyncAsyncResult<CompileTemplateMetadata> {
|
SyncAsyncResult<CompileTemplateMetadata> {
|
||||||
let normalizedTemplateSync: CompileTemplateMetadata = null;
|
let normalizedTemplateSync: CompileTemplateMetadata = null;
|
||||||
let normalizedTemplateAsync: Promise<CompileTemplateMetadata>;
|
let normalizedTemplateAsync: Promise<CompileTemplateMetadata>;
|
||||||
if (prenormData.template != null) {
|
if (isPresent(prenormData.template)) {
|
||||||
if (typeof prenormData.template !== 'string') {
|
|
||||||
throw new SyntaxError(
|
|
||||||
`The template specified for component ${stringify(prenormData.componentType)} is not a string`);
|
|
||||||
}
|
|
||||||
normalizedTemplateSync = this.normalizeTemplateSync(prenormData);
|
normalizedTemplateSync = this.normalizeTemplateSync(prenormData);
|
||||||
normalizedTemplateAsync = Promise.resolve(normalizedTemplateSync);
|
normalizedTemplateAsync = Promise.resolve(normalizedTemplateSync);
|
||||||
} else if (prenormData.templateUrl) {
|
} else if (prenormData.templateUrl) {
|
||||||
if (typeof prenormData.templateUrl !== 'string') {
|
|
||||||
throw new SyntaxError(
|
|
||||||
`The templateUrl specified for component ${stringify(prenormData.componentType)} is not a string`);
|
|
||||||
}
|
|
||||||
normalizedTemplateAsync = this.normalizeTemplateAsync(prenormData);
|
normalizedTemplateAsync = this.normalizeTemplateAsync(prenormData);
|
||||||
} else {
|
} else {
|
||||||
throw new SyntaxError(
|
throw new SyntaxError(
|
||||||
@ -128,7 +120,7 @@ export class DirectiveNormalizer {
|
|||||||
{styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl}));
|
{styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl}));
|
||||||
|
|
||||||
let encapsulation = prenomData.encapsulation;
|
let encapsulation = prenomData.encapsulation;
|
||||||
if (encapsulation == null) {
|
if (isBlank(encapsulation)) {
|
||||||
encapsulation = this._config.defaultEncapsulation;
|
encapsulation = this._config.defaultEncapsulation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,8 +14,6 @@ import {CompilerInjectable} from './injectable';
|
|||||||
import {ReflectorReader, reflector} from './private_import_core';
|
import {ReflectorReader, reflector} from './private_import_core';
|
||||||
import {splitAtColon} from './util';
|
import {splitAtColon} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Resolve a `Type` for {@link Directive}.
|
* Resolve a `Type` for {@link Directive}.
|
||||||
*
|
*
|
||||||
|
@ -6,10 +6,10 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, dirWrapperClassName, identifierModuleUrl, identifierName} from './compile_metadata';
|
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
|
||||||
import {createCheckBindingField, isFirstViewCheck} from './compiler_util/binding_util';
|
import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util';
|
||||||
import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
|
import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
|
||||||
import {createCheckAnimationBindingStmts, createCheckRenderBindingStmt} from './compiler_util/render_util';
|
import {triggerAnimation, writeToRenderer} from './compiler_util/render_util';
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
import {Parser} from './expression_parser/parser';
|
import {Parser} from './expression_parser/parser';
|
||||||
import {Identifiers, createIdentifier} from './identifiers';
|
import {Identifiers, createIdentifier} from './identifiers';
|
||||||
@ -32,8 +32,8 @@ const CHANGES_FIELD_NAME = '_changes';
|
|||||||
const CHANGED_FIELD_NAME = '_changed';
|
const CHANGED_FIELD_NAME = '_changed';
|
||||||
const EVENT_HANDLER_FIELD_NAME = '_eventHandler';
|
const EVENT_HANDLER_FIELD_NAME = '_eventHandler';
|
||||||
|
|
||||||
const CHANGE_VAR = o.variable('change');
|
|
||||||
const CURR_VALUE_VAR = o.variable('currValue');
|
const CURR_VALUE_VAR = o.variable('currValue');
|
||||||
|
const THROW_ON_CHANGE_VAR = o.variable('throwOnChange');
|
||||||
const FORCE_UPDATE_VAR = o.variable('forceUpdate');
|
const FORCE_UPDATE_VAR = o.variable('forceUpdate');
|
||||||
const VIEW_VAR = o.variable('view');
|
const VIEW_VAR = o.variable('view');
|
||||||
const COMPONENT_VIEW_VAR = o.variable('componentView');
|
const COMPONENT_VIEW_VAR = o.variable('componentView');
|
||||||
@ -52,6 +52,10 @@ const RESET_CHANGES_STMT = o.THIS_EXPR.prop(CHANGES_FIELD_NAME).set(o.literalMap
|
|||||||
*/
|
*/
|
||||||
@CompilerInjectable()
|
@CompilerInjectable()
|
||||||
export class DirectiveWrapperCompiler {
|
export class DirectiveWrapperCompiler {
|
||||||
|
static dirWrapperClassName(id: CompileIdentifierMetadata) {
|
||||||
|
return `Wrapper_${identifierName(id)}`;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private compilerConfig: CompilerConfig, private _exprParser: Parser,
|
private compilerConfig: CompilerConfig, private _exprParser: Parser,
|
||||||
private _schemaRegistry: ElementSchemaRegistry, private _console: Console) {}
|
private _schemaRegistry: ElementSchemaRegistry, private _console: Console) {}
|
||||||
@ -124,15 +128,13 @@ class DirectiveWrapperBuilder implements ClassBuilder {
|
|||||||
new o.ClassMethod('ngOnDestroy', [], this.destroyStmts),
|
new o.ClassMethod('ngOnDestroy', [], this.destroyStmts),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
const fields: o.ClassField[] = [
|
const fields: o.ClassField[] = [
|
||||||
new o.ClassField(EVENT_HANDLER_FIELD_NAME, o.FUNCTION_TYPE, [o.StmtModifier.Private]),
|
new o.ClassField(EVENT_HANDLER_FIELD_NAME, o.FUNCTION_TYPE, [o.StmtModifier.Private]),
|
||||||
new o.ClassField(CONTEXT_FIELD_NAME, o.importType(this.dirMeta.type)),
|
new o.ClassField(CONTEXT_FIELD_NAME, o.importType(this.dirMeta.type)),
|
||||||
new o.ClassField(CHANGED_FIELD_NAME, o.BOOL_TYPE, [o.StmtModifier.Private]),
|
new o.ClassField(CHANGED_FIELD_NAME, o.BOOL_TYPE, [o.StmtModifier.Private]),
|
||||||
];
|
];
|
||||||
const ctorStmts: o.Statement[] = [
|
const ctorStmts: o.Statement[] =
|
||||||
o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(false)).toStmt(),
|
[o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(false)).toStmt()];
|
||||||
];
|
|
||||||
if (this.genChanges) {
|
if (this.genChanges) {
|
||||||
fields.push(new o.ClassField(
|
fields.push(new o.ClassField(
|
||||||
CHANGES_FIELD_NAME, new o.MapType(o.DYNAMIC_TYPE), [o.StmtModifier.Private]));
|
CHANGES_FIELD_NAME, new o.MapType(o.DYNAMIC_TYPE), [o.StmtModifier.Private]));
|
||||||
@ -146,7 +148,7 @@ class DirectiveWrapperBuilder implements ClassBuilder {
|
|||||||
.toStmt());
|
.toStmt());
|
||||||
|
|
||||||
return createClassStmt({
|
return createClassStmt({
|
||||||
name: dirWrapperClassName(this.dirMeta.type.reference),
|
name: DirectiveWrapperCompiler.dirWrapperClassName(this.dirMeta.type),
|
||||||
ctorParams: dirDepParamNames.map((paramName) => new o.FnParam(paramName, o.DYNAMIC_TYPE)),
|
ctorParams: dirDepParamNames.map((paramName) => new o.FnParam(paramName, o.DYNAMIC_TYPE)),
|
||||||
builders: [{fields, ctorStmts, methods}, this]
|
builders: [{fields, ctorStmts, methods}, this]
|
||||||
});
|
});
|
||||||
@ -181,14 +183,14 @@ function addNgDoCheckMethod(builder: DirectiveWrapperBuilder) {
|
|||||||
|
|
||||||
if (builder.ngOnInit) {
|
if (builder.ngOnInit) {
|
||||||
lifecycleStmts.push(new o.IfStmt(
|
lifecycleStmts.push(new o.IfStmt(
|
||||||
isFirstViewCheck(VIEW_VAR),
|
VIEW_VAR.prop('numberOfChecks').identical(new o.LiteralExpr(0)),
|
||||||
[o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).callMethod('ngOnInit', []).toStmt()]));
|
[o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).callMethod('ngOnInit', []).toStmt()]));
|
||||||
}
|
}
|
||||||
if (builder.ngDoCheck) {
|
if (builder.ngDoCheck) {
|
||||||
lifecycleStmts.push(o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).callMethod('ngDoCheck', []).toStmt());
|
lifecycleStmts.push(o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).callMethod('ngDoCheck', []).toStmt());
|
||||||
}
|
}
|
||||||
if (lifecycleStmts.length > 0) {
|
if (lifecycleStmts.length > 0) {
|
||||||
stmts.push(new o.IfStmt(o.not(VIEW_VAR.prop('throwOnChange')), lifecycleStmts));
|
stmts.push(new o.IfStmt(o.not(THROW_ON_CHANGE_VAR), lifecycleStmts));
|
||||||
}
|
}
|
||||||
stmts.push(new o.ReturnStatement(changedVar));
|
stmts.push(new o.ReturnStatement(changedVar));
|
||||||
|
|
||||||
@ -198,6 +200,7 @@ function addNgDoCheckMethod(builder: DirectiveWrapperBuilder) {
|
|||||||
new o.FnParam(
|
new o.FnParam(
|
||||||
VIEW_VAR.name, o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
VIEW_VAR.name, o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
|
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
|
||||||
|
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
|
||||||
],
|
],
|
||||||
stmts, o.BOOL_TYPE));
|
stmts, o.BOOL_TYPE));
|
||||||
}
|
}
|
||||||
@ -207,35 +210,24 @@ function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) {
|
|||||||
const onChangeStatements: o.Statement[] = [
|
const onChangeStatements: o.Statement[] = [
|
||||||
o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(true)).toStmt(),
|
o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(true)).toStmt(),
|
||||||
o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).prop(input).set(CURR_VALUE_VAR).toStmt(),
|
o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).prop(input).set(CURR_VALUE_VAR).toStmt(),
|
||||||
field.expression.set(CURR_VALUE_VAR).toStmt()
|
|
||||||
];
|
];
|
||||||
let methodBody: o.Statement[];
|
|
||||||
if (builder.genChanges) {
|
if (builder.genChanges) {
|
||||||
onChangeStatements.push(
|
onChangeStatements.push(o.THIS_EXPR.prop(CHANGES_FIELD_NAME)
|
||||||
o.THIS_EXPR.prop(CHANGES_FIELD_NAME).key(o.literal(input)).set(CHANGE_VAR).toStmt());
|
.key(o.literal(input))
|
||||||
methodBody = [
|
.set(o.importExpr(createIdentifier(Identifiers.SimpleChange))
|
||||||
CHANGE_VAR
|
.instantiate([field.expression, CURR_VALUE_VAR]))
|
||||||
.set(o.importExpr(createIdentifier(Identifiers.checkBindingChange)).callFn([
|
.toStmt());
|
||||||
VIEW_VAR, field.expression, CURR_VALUE_VAR, FORCE_UPDATE_VAR
|
|
||||||
]))
|
|
||||||
.toDeclStmt(),
|
|
||||||
new o.IfStmt(CHANGE_VAR, onChangeStatements)
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
methodBody = [new o.IfStmt(
|
|
||||||
o.importExpr(createIdentifier(Identifiers.checkBinding)).callFn([
|
|
||||||
VIEW_VAR, field.expression, CURR_VALUE_VAR, FORCE_UPDATE_VAR
|
|
||||||
]),
|
|
||||||
onChangeStatements)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const methodBody: o.Statement[] = createCheckBindingStmt(
|
||||||
|
{currValExpr: CURR_VALUE_VAR, forceUpdate: FORCE_UPDATE_VAR, stmts: []}, field.expression,
|
||||||
|
THROW_ON_CHANGE_VAR, onChangeStatements);
|
||||||
builder.methods.push(new o.ClassMethod(
|
builder.methods.push(new o.ClassMethod(
|
||||||
`check_${input}`,
|
`check_${input}`,
|
||||||
[
|
[
|
||||||
new o.FnParam(
|
|
||||||
VIEW_VAR.name, o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
|
||||||
new o.FnParam(CURR_VALUE_VAR.name, o.DYNAMIC_TYPE),
|
new o.FnParam(CURR_VALUE_VAR.name, o.DYNAMIC_TYPE),
|
||||||
new o.FnParam(FORCE_UPDATE_VAR.name, o.BOOL_TYPE)
|
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
|
||||||
|
new o.FnParam(FORCE_UPDATE_VAR.name, o.BOOL_TYPE),
|
||||||
],
|
],
|
||||||
methodBody));
|
methodBody));
|
||||||
}
|
}
|
||||||
@ -251,6 +243,7 @@ function addCheckHostMethod(
|
|||||||
COMPONENT_VIEW_VAR.name,
|
COMPONENT_VIEW_VAR.name,
|
||||||
o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
|
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
|
||||||
|
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
|
||||||
];
|
];
|
||||||
hostProps.forEach((hostProp, hostPropIdx) => {
|
hostProps.forEach((hostProp, hostPropIdx) => {
|
||||||
const field = createCheckBindingField(builder);
|
const field = createCheckBindingField(builder);
|
||||||
@ -265,18 +258,23 @@ function addCheckHostMethod(
|
|||||||
methodParams.push(new o.FnParam(
|
methodParams.push(new o.FnParam(
|
||||||
securityContextExpr.name, o.importType(createIdentifier(Identifiers.SecurityContext))));
|
securityContextExpr.name, o.importType(createIdentifier(Identifiers.SecurityContext))));
|
||||||
}
|
}
|
||||||
|
let checkBindingStmts: o.Statement[];
|
||||||
if (hostProp.isAnimation) {
|
if (hostProp.isAnimation) {
|
||||||
const {checkUpdateStmts, checkDetachStmts} = createCheckAnimationBindingStmts(
|
const {updateStmts, detachStmts} = triggerAnimation(
|
||||||
VIEW_VAR, COMPONENT_VIEW_VAR, hostProp, hostEvents,
|
VIEW_VAR, COMPONENT_VIEW_VAR, hostProp, hostEvents,
|
||||||
o.THIS_EXPR.prop(EVENT_HANDLER_FIELD_NAME)
|
o.THIS_EXPR.prop(EVENT_HANDLER_FIELD_NAME)
|
||||||
.or(o.importExpr(createIdentifier(Identifiers.noop))),
|
.or(o.importExpr(createIdentifier(Identifiers.noop))),
|
||||||
RENDER_EL_VAR, field.expression, evalResult);
|
RENDER_EL_VAR, evalResult.currValExpr, field.expression);
|
||||||
builder.detachStmts.push(...checkDetachStmts);
|
checkBindingStmts = updateStmts;
|
||||||
stmts.push(...checkUpdateStmts);
|
builder.detachStmts.push(...detachStmts);
|
||||||
} else {
|
} else {
|
||||||
stmts.push(...createCheckRenderBindingStmt(
|
checkBindingStmts = writeToRenderer(
|
||||||
VIEW_VAR, RENDER_EL_VAR, hostProp, field.expression, evalResult, securityContextExpr));
|
VIEW_VAR, hostProp, RENDER_EL_VAR, evalResult.currValExpr,
|
||||||
|
builder.compilerConfig.logBindingUpdate, securityContextExpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stmts.push(...createCheckBindingStmt(
|
||||||
|
evalResult, field.expression, THROW_ON_CHANGE_VAR, checkBindingStmts));
|
||||||
});
|
});
|
||||||
builder.methods.push(new o.ClassMethod('checkHost', methodParams, stmts));
|
builder.methods.push(new o.ClassMethod('checkHost', methodParams, stmts));
|
||||||
}
|
}
|
||||||
@ -386,19 +384,20 @@ export class DirectiveWrapperExpressions {
|
|||||||
return dirWrapper.prop(CONTEXT_FIELD_NAME);
|
return dirWrapper.prop(CONTEXT_FIELD_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ngDoCheck(dirWrapper: o.Expression, view: o.Expression, renderElement: o.Expression, ):
|
static ngDoCheck(
|
||||||
o.Expression {
|
dirWrapper: o.Expression, view: o.Expression, renderElement: o.Expression,
|
||||||
return dirWrapper.callMethod('ngDoCheck', [view, renderElement]);
|
throwOnChange: o.Expression): o.Expression {
|
||||||
|
return dirWrapper.callMethod('ngDoCheck', [view, renderElement, throwOnChange]);
|
||||||
}
|
}
|
||||||
static checkHost(
|
static checkHost(
|
||||||
hostProps: BoundElementPropertyAst[], dirWrapper: o.Expression, view: o.Expression,
|
hostProps: BoundElementPropertyAst[], dirWrapper: o.Expression, view: o.Expression,
|
||||||
componentView: o.Expression, renderElement: o.Expression,
|
componentView: o.Expression, renderElement: o.Expression, throwOnChange: o.Expression,
|
||||||
runtimeSecurityContexts: o.Expression[]): o.Statement[] {
|
runtimeSecurityContexts: o.Expression[]): o.Statement[] {
|
||||||
if (hostProps.length) {
|
if (hostProps.length) {
|
||||||
return [dirWrapper
|
return [dirWrapper
|
||||||
.callMethod(
|
.callMethod(
|
||||||
'checkHost',
|
'checkHost', [view, componentView, renderElement, throwOnChange].concat(
|
||||||
[view, componentView, renderElement].concat(runtimeSecurityContexts))
|
runtimeSecurityContexts))
|
||||||
.toStmt()];
|
.toStmt()];
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as chars from '../chars';
|
import * as chars from '../chars';
|
||||||
import {NumberWrapper, isPresent} from '../facade/lang';
|
import {NumberWrapper} from '../facade/lang';
|
||||||
import {CompilerInjectable} from '../injectable';
|
import {CompilerInjectable} from '../injectable';
|
||||||
|
|
||||||
export enum TokenType {
|
export enum TokenType {
|
||||||
@ -241,7 +241,7 @@ class _Scanner {
|
|||||||
this.advance();
|
this.advance();
|
||||||
str += two;
|
str += two;
|
||||||
}
|
}
|
||||||
if (isPresent(threeCode) && this.peek == threeCode) {
|
if (threeCode != null && this.peek == threeCode) {
|
||||||
this.advance();
|
this.advance();
|
||||||
str += three;
|
str += three;
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,13 @@
|
|||||||
import * as i18n from './i18n_ast';
|
import * as i18n from './i18n_ast';
|
||||||
|
|
||||||
export function digest(message: i18n.Message): string {
|
export function digest(message: i18n.Message): string {
|
||||||
return message.id || sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
|
return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decimalDigest(message: i18n.Message): string {
|
export function decimalDigest(message: i18n.Message): string {
|
||||||
const visitor = new _SerializerIgnoreIcuExpVisitor();
|
const visitor = new _SerializerIgnoreIcuExpVisitor();
|
||||||
const parts = message.nodes.map(a => a.visit(visitor, null));
|
const parts = message.nodes.map(a => a.visit(visitor, null));
|
||||||
return message.id || computeMsgId(parts.join(''), message.meaning);
|
return computeMsgId(parts.join(''), message.meaning);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,7 +52,7 @@ export class Extractor {
|
|||||||
|
|
||||||
extract(rootFiles: string[]): Promise<MessageBundle> {
|
extract(rootFiles: string[]): Promise<MessageBundle> {
|
||||||
const programSymbols = extractProgramSymbols(this.staticSymbolResolver, rootFiles, this.host);
|
const programSymbols = extractProgramSymbols(this.staticSymbolResolver, rootFiles, this.host);
|
||||||
const {ngModuleByPipeOrDirective, files, ngModules} =
|
const {files, ngModules} =
|
||||||
analyzeAndValidateNgModules(programSymbols, this.host, this.metadataResolver);
|
analyzeAndValidateNgModules(programSymbols, this.host, this.metadataResolver);
|
||||||
return Promise
|
return Promise
|
||||||
.all(ngModules.map(
|
.all(ngModules.map(
|
||||||
@ -109,7 +109,7 @@ export class Extractor {
|
|||||||
const resolver = new CompileMetadataResolver(
|
const resolver = new CompileMetadataResolver(
|
||||||
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
||||||
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
||||||
symbolCache, staticReflector);
|
staticReflector);
|
||||||
|
|
||||||
// TODO(vicb): implicit tags & attributes
|
// TODO(vicb): implicit tags & attributes
|
||||||
const messageBundle = new MessageBundle(htmlParser, [], {});
|
const messageBundle = new MessageBundle(htmlParser, [], {});
|
||||||
|
@ -18,8 +18,6 @@ import {TranslationBundle} from './translation_bundle';
|
|||||||
const _I18N_ATTR = 'i18n';
|
const _I18N_ATTR = 'i18n';
|
||||||
const _I18N_ATTR_PREFIX = 'i18n-';
|
const _I18N_ATTR_PREFIX = 'i18n-';
|
||||||
const _I18N_COMMENT_PREFIX_REGEXP = /^i18n:?/;
|
const _I18N_COMMENT_PREFIX_REGEXP = /^i18n:?/;
|
||||||
const MEANING_SEPARATOR = '|';
|
|
||||||
const ID_SEPARATOR = '@@';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract translatable messages from an html AST
|
* Extract translatable messages from an html AST
|
||||||
@ -55,20 +53,22 @@ enum _VisitorMode {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
class _Visitor implements html.Visitor {
|
class _Visitor implements html.Visitor {
|
||||||
|
private _depth: number;
|
||||||
|
|
||||||
// <el i18n>...</el>
|
// <el i18n>...</el>
|
||||||
private _inI18nNode: boolean;
|
private _inI18nNode: boolean;
|
||||||
private _depth: number;
|
|
||||||
private _inImplicitNode: boolean;
|
private _inImplicitNode: boolean;
|
||||||
|
|
||||||
// <!--i18n-->...<!--/i18n-->
|
// <!--i18n-->...<!--/i18n-->
|
||||||
|
private _inI18nBlock: boolean;
|
||||||
private _blockMeaningAndDesc: string;
|
private _blockMeaningAndDesc: string;
|
||||||
private _blockChildren: html.Node[];
|
private _blockChildren: html.Node[];
|
||||||
private _blockStartDepth: number;
|
private _blockStartDepth: number;
|
||||||
private _inI18nBlock: boolean;
|
|
||||||
|
|
||||||
// {<icu message>}
|
// {<icu message>}
|
||||||
private _inIcu: boolean;
|
private _inIcu: boolean;
|
||||||
|
|
||||||
|
// set to void 0 when not in a section
|
||||||
private _msgCountAtSectionStart: number;
|
private _msgCountAtSectionStart: number;
|
||||||
private _errors: I18nError[];
|
private _errors: I18nError[];
|
||||||
private _mode: _VisitorMode;
|
private _mode: _VisitorMode;
|
||||||
@ -79,7 +79,7 @@ class _Visitor implements html.Visitor {
|
|||||||
// _VisitorMode.Merge only
|
// _VisitorMode.Merge only
|
||||||
private _translations: TranslationBundle;
|
private _translations: TranslationBundle;
|
||||||
private _createI18nMessage:
|
private _createI18nMessage:
|
||||||
(msg: html.Node[], meaning: string, description: string, id: string) => i18n.Message;
|
(msg: html.Node[], meaning: string, description: string) => i18n.Message;
|
||||||
|
|
||||||
|
|
||||||
constructor(private _implicitTags: string[], private _implicitAttrs: {[k: string]: string[]}) {}
|
constructor(private _implicitTags: string[], private _implicitAttrs: {[k: string]: string[]}) {}
|
||||||
@ -210,50 +210,31 @@ class _Visitor implements html.Visitor {
|
|||||||
this._depth++;
|
this._depth++;
|
||||||
const wasInI18nNode = this._inI18nNode;
|
const wasInI18nNode = this._inI18nNode;
|
||||||
const wasInImplicitNode = this._inImplicitNode;
|
const wasInImplicitNode = this._inImplicitNode;
|
||||||
let childNodes: html.Node[];
|
let childNodes: html.Node[] = [];
|
||||||
|
let translatedChildNodes: html.Node[];
|
||||||
|
|
||||||
// Extract only top level nodes with the (implicit) "i18n" attribute if not in a block or an ICU
|
// Extract:
|
||||||
// message
|
// - top level nodes with the (implicit) "i18n" attribute if not already in a section
|
||||||
|
// - ICU messages
|
||||||
const i18nAttr = _getI18nAttr(el);
|
const i18nAttr = _getI18nAttr(el);
|
||||||
|
const i18nMeta = i18nAttr ? i18nAttr.value : '';
|
||||||
const isImplicit = this._implicitTags.some(tag => el.name === tag) && !this._inIcu &&
|
const isImplicit = this._implicitTags.some(tag => el.name === tag) && !this._inIcu &&
|
||||||
!this._isInTranslatableSection;
|
!this._isInTranslatableSection;
|
||||||
const isTopLevelImplicit = !wasInImplicitNode && isImplicit;
|
const isTopLevelImplicit = !wasInImplicitNode && isImplicit;
|
||||||
this._inImplicitNode = this._inImplicitNode || isImplicit;
|
this._inImplicitNode = wasInImplicitNode || isImplicit;
|
||||||
|
|
||||||
if (!this._isInTranslatableSection && !this._inIcu) {
|
if (!this._isInTranslatableSection && !this._inIcu) {
|
||||||
if (i18nAttr) {
|
if (i18nAttr || isTopLevelImplicit) {
|
||||||
// explicit translation
|
|
||||||
this._inI18nNode = true;
|
this._inI18nNode = true;
|
||||||
const message = this._addMessage(el.children, i18nAttr.value);
|
const message = this._addMessage(el.children, i18nMeta);
|
||||||
childNodes = this._translateMessage(el, message);
|
translatedChildNodes = this._translateMessage(el, message);
|
||||||
} else if (isTopLevelImplicit) {
|
|
||||||
// implicit translation
|
|
||||||
this._inI18nNode = true;
|
|
||||||
const message = this._addMessage(el.children);
|
|
||||||
childNodes = this._translateMessage(el, message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._mode == _VisitorMode.Extract) {
|
if (this._mode == _VisitorMode.Extract) {
|
||||||
const isTranslatable = i18nAttr || isTopLevelImplicit;
|
const isTranslatable = i18nAttr || isTopLevelImplicit;
|
||||||
if (isTranslatable) {
|
if (isTranslatable) this._openTranslatableSection(el);
|
||||||
this._openTranslatableSection(el);
|
|
||||||
}
|
|
||||||
html.visitAll(this, el.children);
|
html.visitAll(this, el.children);
|
||||||
if (isTranslatable) {
|
if (isTranslatable) this._closeTranslatableSection(el, el.children);
|
||||||
this._closeTranslatableSection(el, el.children);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._mode === _VisitorMode.Merge && !i18nAttr && !isTopLevelImplicit) {
|
|
||||||
childNodes = [];
|
|
||||||
el.children.forEach(child => {
|
|
||||||
const visited = child.visit(this, context);
|
|
||||||
if (visited && !this._isInTranslatableSection) {
|
|
||||||
// Do not add the children from translatable sections (= i18n blocks here)
|
|
||||||
// They will be added when the section is close (i.e. on `<!-- /i18n -->`)
|
|
||||||
childNodes = childNodes.concat(visited);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (i18nAttr || isTopLevelImplicit) {
|
if (i18nAttr || isTopLevelImplicit) {
|
||||||
@ -265,19 +246,18 @@ class _Visitor implements html.Visitor {
|
|||||||
// Descend into child nodes for extraction
|
// Descend into child nodes for extraction
|
||||||
html.visitAll(this, el.children);
|
html.visitAll(this, el.children);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this._mode == _VisitorMode.Merge) {
|
if (this._mode === _VisitorMode.Merge) {
|
||||||
// Translate attributes in ICU messages
|
const visitNodes = translatedChildNodes || el.children;
|
||||||
childNodes = [];
|
visitNodes.forEach(child => {
|
||||||
el.children.forEach(child => {
|
const visited = child.visit(this, context);
|
||||||
const visited = child.visit(this, context);
|
if (visited && !this._isInTranslatableSection) {
|
||||||
if (visited && !this._isInTranslatableSection) {
|
// Do not add the children from translatable sections (= i18n blocks here)
|
||||||
// Do not add the children from translatable sections (= i18n blocks here)
|
// They will be added later in this loop when the block closes (i.e. on `<!-- /i18n -->`)
|
||||||
// They will be added when the section is close (i.e. on `<!-- /i18n -->`)
|
childNodes = childNodes.concat(visited);
|
||||||
childNodes = childNodes.concat(visited);
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._visitAttributesOf(el);
|
this._visitAttributesOf(el);
|
||||||
@ -287,7 +267,6 @@ class _Visitor implements html.Visitor {
|
|||||||
this._inImplicitNode = wasInImplicitNode;
|
this._inImplicitNode = wasInImplicitNode;
|
||||||
|
|
||||||
if (this._mode === _VisitorMode.Merge) {
|
if (this._mode === _VisitorMode.Merge) {
|
||||||
// There are no childNodes in translatable sections - those nodes will be replace anyway
|
|
||||||
const translatedAttrs = this._translateAttributes(el);
|
const translatedAttrs = this._translateAttributes(el);
|
||||||
return new html.Element(
|
return new html.Element(
|
||||||
el.name, translatedAttrs, childNodes, el.sourceSpan, el.startSourceSpan,
|
el.name, translatedAttrs, childNodes, el.sourceSpan, el.startSourceSpan,
|
||||||
@ -332,20 +311,21 @@ class _Visitor implements html.Visitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add a translatable message
|
// add a translatable message
|
||||||
private _addMessage(ast: html.Node[], msgMeta?: string): i18n.Message {
|
private _addMessage(ast: html.Node[], meaningAndDesc?: string): i18n.Message {
|
||||||
if (ast.length == 0 ||
|
if (ast.length == 0 ||
|
||||||
ast.length == 1 && ast[0] instanceof html.Attribute && !(<html.Attribute>ast[0]).value) {
|
ast.length == 1 && ast[0] instanceof html.Attribute && !(<html.Attribute>ast[0]).value) {
|
||||||
// Do not create empty messages
|
// Do not create empty messages
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {meaning, description, id} = _parseMessageMeta(msgMeta);
|
const [meaning, description] = _splitMeaningAndDesc(meaningAndDesc);
|
||||||
const message = this._createI18nMessage(ast, meaning, description, id);
|
const message = this._createI18nMessage(ast, meaning, description);
|
||||||
this._messages.push(message);
|
this._messages.push(message);
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translates the given message given the `TranslationBundle`
|
// Translates the given message given the `TranslationBundle`
|
||||||
|
// This is used for translating elements / blocks - see `_translateAttributes` for attributes
|
||||||
// no-op when called in extraction mode (returns [])
|
// no-op when called in extraction mode (returns [])
|
||||||
private _translateMessage(el: html.Node, message: i18n.Message): html.Node[] {
|
private _translateMessage(el: html.Node, message: i18n.Message): html.Node[] {
|
||||||
if (message && this._mode === _VisitorMode.Merge) {
|
if (message && this._mode === _VisitorMode.Merge) {
|
||||||
@ -370,7 +350,7 @@ class _Visitor implements html.Visitor {
|
|||||||
attributes.forEach(attr => {
|
attributes.forEach(attr => {
|
||||||
if (attr.name.startsWith(_I18N_ATTR_PREFIX)) {
|
if (attr.name.startsWith(_I18N_ATTR_PREFIX)) {
|
||||||
i18nAttributeMeanings[attr.name.slice(_I18N_ATTR_PREFIX.length)] =
|
i18nAttributeMeanings[attr.name.slice(_I18N_ATTR_PREFIX.length)] =
|
||||||
_parseMessageMeta(attr.value).meaning;
|
_splitMeaningAndDesc(attr.value)[0];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -384,10 +364,12 @@ class _Visitor implements html.Visitor {
|
|||||||
|
|
||||||
if (attr.value && attr.value != '' && i18nAttributeMeanings.hasOwnProperty(attr.name)) {
|
if (attr.value && attr.value != '' && i18nAttributeMeanings.hasOwnProperty(attr.name)) {
|
||||||
const meaning = i18nAttributeMeanings[attr.name];
|
const meaning = i18nAttributeMeanings[attr.name];
|
||||||
const message: i18n.Message = this._createI18nMessage([attr], meaning, '', '');
|
const message: i18n.Message = this._createI18nMessage([attr], meaning, '');
|
||||||
const nodes = this._translations.get(message);
|
const nodes = this._translations.get(message);
|
||||||
if (nodes) {
|
if (nodes) {
|
||||||
if (nodes[0] instanceof html.Text) {
|
if (nodes.length == 0) {
|
||||||
|
translatedAttributes.push(new html.Attribute(attr.name, '', attr.sourceSpan));
|
||||||
|
} else if (nodes[0] instanceof html.Text) {
|
||||||
const value = (nodes[0] as html.Text).value;
|
const value = (nodes[0] as html.Text).value;
|
||||||
translatedAttributes.push(new html.Attribute(attr.name, value, attr.sourceSpan));
|
translatedAttributes.push(new html.Attribute(attr.name, value, attr.sourceSpan));
|
||||||
} else {
|
} else {
|
||||||
@ -434,7 +416,7 @@ class _Visitor implements html.Visitor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A translatable section could be:
|
* A translatable section could be:
|
||||||
* - a translatable element,
|
* - the content of translatable element,
|
||||||
* - nodes between `<!-- i18n -->` and `<!-- /i18n -->` comments
|
* - nodes between `<!-- i18n -->` and `<!-- /i18n -->` comments
|
||||||
*/
|
*/
|
||||||
private get _isInTranslatableSection(): boolean {
|
private get _isInTranslatableSection(): boolean {
|
||||||
@ -498,16 +480,8 @@ function _getI18nAttr(p: html.Element): html.Attribute {
|
|||||||
return p.attrs.find(attr => attr.name === _I18N_ATTR) || null;
|
return p.attrs.find(attr => attr.name === _I18N_ATTR) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _parseMessageMeta(i18n: string): {meaning: string, description: string, id: string} {
|
function _splitMeaningAndDesc(i18n: string): [string, string] {
|
||||||
if (!i18n) return {meaning: '', description: '', id: ''};
|
if (!i18n) return ['', ''];
|
||||||
|
const pipeIndex = i18n.indexOf('|');
|
||||||
const idIndex = i18n.indexOf(ID_SEPARATOR);
|
return pipeIndex == -1 ? ['', i18n] : [i18n.slice(0, pipeIndex), i18n.slice(pipeIndex + 1)];
|
||||||
const descIndex = i18n.indexOf(MEANING_SEPARATOR);
|
|
||||||
const [meaningAndDesc, id] =
|
|
||||||
(idIndex > -1) ? [i18n.slice(0, idIndex), i18n.slice(idIndex + 2)] : [i18n, ''];
|
|
||||||
const [meaning, description] = (descIndex > -1) ?
|
|
||||||
[meaningAndDesc.slice(0, descIndex), meaningAndDesc.slice(descIndex + 1)] :
|
|
||||||
['', meaningAndDesc];
|
|
||||||
|
|
||||||
return {meaning, description, id};
|
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,11 @@ export class Message {
|
|||||||
* @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages)
|
* @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages)
|
||||||
* @param meaning
|
* @param meaning
|
||||||
* @param description
|
* @param description
|
||||||
* @param id
|
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
public nodes: Node[], public placeholders: {[phName: string]: string},
|
public nodes: Node[], public placeholders: {[phName: string]: string},
|
||||||
public placeholderToMessage: {[phName: string]: Message}, public meaning: string,
|
public placeholderToMessage: {[phName: string]: Message}, public meaning: string,
|
||||||
public description: string, public id: string) {}
|
public description: string) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Node {
|
export interface Node {
|
||||||
|
@ -22,11 +22,11 @@ const _expParser = new ExpressionParser(new ExpressionLexer());
|
|||||||
* Returns a function converting html nodes to an i18n Message given an interpolationConfig
|
* Returns a function converting html nodes to an i18n Message given an interpolationConfig
|
||||||
*/
|
*/
|
||||||
export function createI18nMessageFactory(interpolationConfig: InterpolationConfig): (
|
export function createI18nMessageFactory(interpolationConfig: InterpolationConfig): (
|
||||||
nodes: html.Node[], meaning: string, description: string, id: string) => i18n.Message {
|
nodes: html.Node[], meaning: string, description: string) => i18n.Message {
|
||||||
const visitor = new _I18nVisitor(_expParser, interpolationConfig);
|
const visitor = new _I18nVisitor(_expParser, interpolationConfig);
|
||||||
|
|
||||||
return (nodes: html.Node[], meaning: string, description: string, id: string) =>
|
return (nodes: html.Node[], meaning: string, description: string) =>
|
||||||
visitor.toI18nMessage(nodes, meaning, description, id);
|
visitor.toI18nMessage(nodes, meaning, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _I18nVisitor implements html.Visitor {
|
class _I18nVisitor implements html.Visitor {
|
||||||
@ -40,8 +40,7 @@ class _I18nVisitor implements html.Visitor {
|
|||||||
private _expressionParser: ExpressionParser,
|
private _expressionParser: ExpressionParser,
|
||||||
private _interpolationConfig: InterpolationConfig) {}
|
private _interpolationConfig: InterpolationConfig) {}
|
||||||
|
|
||||||
public toI18nMessage(nodes: html.Node[], meaning: string, description: string, id: string):
|
public toI18nMessage(nodes: html.Node[], meaning: string, description: string): i18n.Message {
|
||||||
i18n.Message {
|
|
||||||
this._isIcu = nodes.length == 1 && nodes[0] instanceof html.Expansion;
|
this._isIcu = nodes.length == 1 && nodes[0] instanceof html.Expansion;
|
||||||
this._icuDepth = 0;
|
this._icuDepth = 0;
|
||||||
this._placeholderRegistry = new PlaceholderRegistry();
|
this._placeholderRegistry = new PlaceholderRegistry();
|
||||||
@ -51,7 +50,7 @@ class _I18nVisitor implements html.Visitor {
|
|||||||
const i18nodes: i18n.Node[] = html.visitAll(this, nodes, {});
|
const i18nodes: i18n.Node[] = html.visitAll(this, nodes, {});
|
||||||
|
|
||||||
return new i18n.Message(
|
return new i18n.Message(
|
||||||
i18nodes, this._placeholderToContent, this._placeholderToMessage, meaning, description, id);
|
i18nodes, this._placeholderToContent, this._placeholderToMessage, meaning, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitElement(el: html.Element, context: any): i18n.Node {
|
visitElement(el: html.Element, context: any): i18n.Node {
|
||||||
@ -116,7 +115,7 @@ class _I18nVisitor implements html.Visitor {
|
|||||||
// TODO(vicb): add a html.Node -> i18n.Message cache to avoid having to re-create the msg
|
// TODO(vicb): add a html.Node -> i18n.Message cache to avoid having to re-create the msg
|
||||||
const phName = this._placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString());
|
const phName = this._placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString());
|
||||||
const visitor = new _I18nVisitor(this._expressionParser, this._interpolationConfig);
|
const visitor = new _I18nVisitor(this._expressionParser, this._interpolationConfig);
|
||||||
this._placeholderToMessage[phName] = visitor.toI18nMessage([icu], '', '', '');
|
this._placeholderToMessage[phName] = visitor.toI18nMessage([icu], '', '');
|
||||||
return new i18n.IcuPlaceholder(i18nIcu, phName, icu.sourceSpan);
|
return new i18n.IcuPlaceholder(i18nIcu, phName, icu.sourceSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,8 +111,14 @@ export class PlaceholderRegistry {
|
|||||||
private _hashClosingTag(tag: string): string { return this._hashTag(`/${tag}`, {}, false); }
|
private _hashClosingTag(tag: string): string { return this._hashTag(`/${tag}`, {}, false); }
|
||||||
|
|
||||||
private _generateUniqueName(base: string): string {
|
private _generateUniqueName(base: string): string {
|
||||||
const next = this._placeHolderNameCounts[base];
|
const seen = this._placeHolderNameCounts.hasOwnProperty(base);
|
||||||
this._placeHolderNameCounts[base] = next ? next + 1 : 1;
|
if (!seen) {
|
||||||
return next ? `${base}_${next}` : base;
|
this._placeHolderNameCounts[base] = 1;
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = this._placeHolderNameCounts[base];
|
||||||
|
this._placeHolderNameCounts[base] = id + 1;
|
||||||
|
return `${base}_${id}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,26 @@
|
|||||||
|
|
||||||
import * as i18n from '../i18n_ast';
|
import * as i18n from '../i18n_ast';
|
||||||
|
|
||||||
export interface Serializer {
|
export abstract class Serializer {
|
||||||
write(messages: i18n.Message[]): string;
|
abstract write(messages: i18n.Message[]): string;
|
||||||
|
|
||||||
load(content: string, url: string): {[msgId: string]: i18n.Node[]};
|
abstract load(content: string, url: string): {[msgId: string]: i18n.Node[]};
|
||||||
|
|
||||||
digest(message: i18n.Message): string;
|
abstract digest(message: i18n.Message): string;
|
||||||
|
|
||||||
|
// Creates a name mapper, see `PlaceholderMapper`
|
||||||
|
// Returning `null` means that no name mapping is used.
|
||||||
|
createNameMapper(message: i18n.Message): PlaceholderMapper { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A `PlaceholderMapper` converts placeholder names from internal to serialized representation and
|
||||||
|
* back.
|
||||||
|
*
|
||||||
|
* It should be used for serialization format that put constraints on the placeholder names.
|
||||||
|
*/
|
||||||
|
export interface PlaceholderMapper {
|
||||||
|
toPublicName(internalName: string): string;
|
||||||
|
|
||||||
|
toInternalName(publicName: string): string;
|
||||||
}
|
}
|
@ -27,7 +27,7 @@ const _UNIT_TAG = 'trans-unit';
|
|||||||
|
|
||||||
// http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html
|
// http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html
|
||||||
// http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html
|
// http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html
|
||||||
export class Xliff implements Serializer {
|
export class Xliff extends Serializer {
|
||||||
write(messages: i18n.Message[]): string {
|
write(messages: i18n.Message[]): string {
|
||||||
const visitor = new _WriteVisitor();
|
const visitor = new _WriteVisitor();
|
||||||
const visited: {[id: string]: boolean} = {};
|
const visited: {[id: string]: boolean} = {};
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {decimalDigest} from '../digest';
|
import {decimalDigest} from '../digest';
|
||||||
import * as i18n from '../i18n_ast';
|
import * as i18n from '../i18n_ast';
|
||||||
|
|
||||||
import {Serializer} from './serializer';
|
import {PlaceholderMapper, Serializer} from './serializer';
|
||||||
import * as xml from './xml_helper';
|
import * as xml from './xml_helper';
|
||||||
|
|
||||||
const _MESSAGES_TAG = 'messagebundle';
|
const _MESSAGES_TAG = 'messagebundle';
|
||||||
@ -37,7 +37,7 @@ const _DOCTYPE = `<!ELEMENT messagebundle (msg)*>
|
|||||||
|
|
||||||
<!ELEMENT ex (#PCDATA)>`;
|
<!ELEMENT ex (#PCDATA)>`;
|
||||||
|
|
||||||
export class Xmb implements Serializer {
|
export class Xmb extends Serializer {
|
||||||
write(messages: i18n.Message[]): string {
|
write(messages: i18n.Message[]): string {
|
||||||
const exampleVisitor = new ExampleVisitor();
|
const exampleVisitor = new ExampleVisitor();
|
||||||
const visitor = new _Visitor();
|
const visitor = new _Visitor();
|
||||||
@ -51,6 +51,8 @@ export class Xmb implements Serializer {
|
|||||||
if (visited[id]) return;
|
if (visited[id]) return;
|
||||||
visited[id] = true;
|
visited[id] = true;
|
||||||
|
|
||||||
|
const mapper = this.createNameMapper(message);
|
||||||
|
|
||||||
const attrs: {[k: string]: string} = {id};
|
const attrs: {[k: string]: string} = {id};
|
||||||
|
|
||||||
if (message.description) {
|
if (message.description) {
|
||||||
@ -62,7 +64,8 @@ export class Xmb implements Serializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rootNode.children.push(
|
rootNode.children.push(
|
||||||
new xml.CR(2), new xml.Tag(_MESSAGE_TAG, attrs, visitor.serialize(message.nodes)));
|
new xml.CR(2),
|
||||||
|
new xml.Tag(_MESSAGE_TAG, attrs, visitor.serialize(message.nodes, {mapper})));
|
||||||
});
|
});
|
||||||
|
|
||||||
rootNode.children.push(new xml.CR());
|
rootNode.children.push(new xml.CR());
|
||||||
@ -82,22 +85,29 @@ export class Xmb implements Serializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
digest(message: i18n.Message): string { return digest(message); }
|
digest(message: i18n.Message): string { return digest(message); }
|
||||||
|
|
||||||
|
|
||||||
|
createNameMapper(message: i18n.Message): PlaceholderMapper {
|
||||||
|
return new XmbPlaceholderMapper(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Visitor implements i18n.Visitor {
|
class _Visitor implements i18n.Visitor {
|
||||||
visitText(text: i18n.Text, context?: any): xml.Node[] { return [new xml.Text(text.value)]; }
|
visitText(text: i18n.Text, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
||||||
|
return [new xml.Text(text.value)];
|
||||||
|
}
|
||||||
|
|
||||||
visitContainer(container: i18n.Container, context?: any): xml.Node[] {
|
visitContainer(container: i18n.Container, ctx: any): xml.Node[] {
|
||||||
const nodes: xml.Node[] = [];
|
const nodes: xml.Node[] = [];
|
||||||
container.children.forEach((node: i18n.Node) => nodes.push(...node.visit(this)));
|
container.children.forEach((node: i18n.Node) => nodes.push(...node.visit(this, ctx)));
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitIcu(icu: i18n.Icu, context?: any): xml.Node[] {
|
visitIcu(icu: i18n.Icu, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
||||||
const nodes = [new xml.Text(`{${icu.expressionPlaceholder}, ${icu.type}, `)];
|
const nodes = [new xml.Text(`{${icu.expressionPlaceholder}, ${icu.type}, `)];
|
||||||
|
|
||||||
Object.keys(icu.cases).forEach((c: string) => {
|
Object.keys(icu.cases).forEach((c: string) => {
|
||||||
nodes.push(new xml.Text(`${c} {`), ...icu.cases[c].visit(this), new xml.Text(`} `));
|
nodes.push(new xml.Text(`${c} {`), ...icu.cases[c].visit(this, ctx), new xml.Text(`} `));
|
||||||
});
|
});
|
||||||
|
|
||||||
nodes.push(new xml.Text(`}`));
|
nodes.push(new xml.Text(`}`));
|
||||||
@ -105,30 +115,34 @@ class _Visitor implements i18n.Visitor {
|
|||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitTagPlaceholder(ph: i18n.TagPlaceholder, context?: any): xml.Node[] {
|
visitTagPlaceholder(ph: i18n.TagPlaceholder, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
||||||
const startEx = new xml.Tag(_EXEMPLE_TAG, {}, [new xml.Text(`<${ph.tag}>`)]);
|
const startEx = new xml.Tag(_EXEMPLE_TAG, {}, [new xml.Text(`<${ph.tag}>`)]);
|
||||||
const startTagPh = new xml.Tag(_PLACEHOLDER_TAG, {name: ph.startName}, [startEx]);
|
let name = ctx.mapper.toPublicName(ph.startName);
|
||||||
|
const startTagPh = new xml.Tag(_PLACEHOLDER_TAG, {name}, [startEx]);
|
||||||
if (ph.isVoid) {
|
if (ph.isVoid) {
|
||||||
// void tags have no children nor closing tags
|
// void tags have no children nor closing tags
|
||||||
return [startTagPh];
|
return [startTagPh];
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeEx = new xml.Tag(_EXEMPLE_TAG, {}, [new xml.Text(`</${ph.tag}>`)]);
|
const closeEx = new xml.Tag(_EXEMPLE_TAG, {}, [new xml.Text(`</${ph.tag}>`)]);
|
||||||
const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {name: ph.closeName}, [closeEx]);
|
name = ctx.mapper.toPublicName(ph.closeName);
|
||||||
|
const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {name}, [closeEx]);
|
||||||
|
|
||||||
return [startTagPh, ...this.serialize(ph.children), closeTagPh];
|
return [startTagPh, ...this.serialize(ph.children, ctx), closeTagPh];
|
||||||
}
|
}
|
||||||
|
|
||||||
visitPlaceholder(ph: i18n.Placeholder, context?: any): xml.Node[] {
|
visitPlaceholder(ph: i18n.Placeholder, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
||||||
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name})];
|
const name = ctx.mapper.toPublicName(ph.name);
|
||||||
|
return [new xml.Tag(_PLACEHOLDER_TAG, {name})];
|
||||||
}
|
}
|
||||||
|
|
||||||
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): xml.Node[] {
|
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
||||||
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name})];
|
const name = ctx.mapper.toPublicName(ph.name);
|
||||||
|
return [new xml.Tag(_PLACEHOLDER_TAG, {name})];
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(nodes: i18n.Node[]): xml.Node[] {
|
serialize(nodes: i18n.Node[], ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
||||||
return [].concat(...nodes.map(node => node.visit(this)));
|
return [].concat(...nodes.map(node => node.visit(this, ctx)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,3 +172,69 @@ class ExampleVisitor implements xml.IVisitor {
|
|||||||
visitDeclaration(decl: xml.Declaration): void {}
|
visitDeclaration(decl: xml.Declaration): void {}
|
||||||
visitDoctype(doctype: xml.Doctype): void {}
|
visitDoctype(doctype: xml.Doctype): void {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XMB/XTB placeholders can only contain A-Z, 0-9 and _
|
||||||
|
*
|
||||||
|
* Because such restrictions do not exist on placeholder names generated locally, the
|
||||||
|
* `PlaceholderMapper` is used to convert internal names to XMB names when the XMB file is
|
||||||
|
* serialized and back from XTB to internal names when an XTB is loaded.
|
||||||
|
*/
|
||||||
|
export class XmbPlaceholderMapper implements PlaceholderMapper, i18n.Visitor {
|
||||||
|
private internalToXmb: {[k: string]: string} = {};
|
||||||
|
private xmbToNextId: {[k: string]: number} = {};
|
||||||
|
private xmbToInternal: {[k: string]: string} = {};
|
||||||
|
|
||||||
|
// create a mapping from the message
|
||||||
|
constructor(message: i18n.Message) { message.nodes.forEach(node => node.visit(this)); }
|
||||||
|
|
||||||
|
toPublicName(internalName: string): string {
|
||||||
|
return this.internalToXmb.hasOwnProperty(internalName) ? this.internalToXmb[internalName] :
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
|
||||||
|
toInternalName(publicName: string): string {
|
||||||
|
return this.xmbToInternal.hasOwnProperty(publicName) ? this.xmbToInternal[publicName] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitText(text: i18n.Text, ctx?: any): any { return null; }
|
||||||
|
|
||||||
|
visitContainer(container: i18n.Container, ctx?: any): any {
|
||||||
|
container.children.forEach(child => child.visit(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitIcu(icu: i18n.Icu, ctx?: any): any {
|
||||||
|
Object.keys(icu.cases).forEach(k => { icu.cases[k].visit(this); });
|
||||||
|
}
|
||||||
|
|
||||||
|
visitTagPlaceholder(ph: i18n.TagPlaceholder, ctx?: any): any {
|
||||||
|
this.addPlaceholder(ph.startName);
|
||||||
|
ph.children.forEach(child => child.visit(this));
|
||||||
|
this.addPlaceholder(ph.closeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitPlaceholder(ph: i18n.Placeholder, ctx?: any): any { this.addPlaceholder(ph.name); }
|
||||||
|
|
||||||
|
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, ctx?: any): any { this.addPlaceholder(ph.name); }
|
||||||
|
|
||||||
|
// XMB placeholders could only contains A-Z, 0-9 and _
|
||||||
|
private addPlaceholder(internalName: string): void {
|
||||||
|
if (!internalName || this.internalToXmb.hasOwnProperty(internalName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let xmbName = internalName.toUpperCase().replace(/[^A-Z0-9_]/g, '_');
|
||||||
|
|
||||||
|
if (this.xmbToInternal.hasOwnProperty(xmbName)) {
|
||||||
|
// Create a new XMB when it has already been used
|
||||||
|
const nextId = this.xmbToNextId[xmbName];
|
||||||
|
this.xmbToNextId[xmbName] = nextId + 1;
|
||||||
|
xmbName = `${xmbName}_${nextId}`;
|
||||||
|
} else {
|
||||||
|
this.xmbToNextId[xmbName] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.internalToXmb[internalName] = xmbName;
|
||||||
|
this.xmbToInternal[xmbName] = internalName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,14 +11,14 @@ import {XmlParser} from '../../ml_parser/xml_parser';
|
|||||||
import * as i18n from '../i18n_ast';
|
import * as i18n from '../i18n_ast';
|
||||||
import {I18nError} from '../parse_util';
|
import {I18nError} from '../parse_util';
|
||||||
|
|
||||||
import {Serializer} from './serializer';
|
import {PlaceholderMapper, Serializer} from './serializer';
|
||||||
import {digest} from './xmb';
|
import {XmbPlaceholderMapper, digest} from './xmb';
|
||||||
|
|
||||||
const _TRANSLATIONS_TAG = 'translationbundle';
|
const _TRANSLATIONS_TAG = 'translationbundle';
|
||||||
const _TRANSLATION_TAG = 'translation';
|
const _TRANSLATION_TAG = 'translation';
|
||||||
const _PLACEHOLDER_TAG = 'ph';
|
const _PLACEHOLDER_TAG = 'ph';
|
||||||
|
|
||||||
export class Xtb implements Serializer {
|
export class Xtb extends Serializer {
|
||||||
write(messages: i18n.Message[]): string { throw new Error('Unsupported'); }
|
write(messages: i18n.Message[]): string { throw new Error('Unsupported'); }
|
||||||
|
|
||||||
load(content: string, url: string): {[msgId: string]: i18n.Node[]} {
|
load(content: string, url: string): {[msgId: string]: i18n.Node[]} {
|
||||||
@ -43,6 +43,10 @@ export class Xtb implements Serializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
digest(message: i18n.Message): string { return digest(message); }
|
digest(message: i18n.Message): string { return digest(message); }
|
||||||
|
|
||||||
|
createNameMapper(message: i18n.Message): PlaceholderMapper {
|
||||||
|
return new XmbPlaceholderMapper(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract messages as xml nodes from the xtb file
|
// Extract messages as xml nodes from the xtb file
|
||||||
|
@ -11,7 +11,7 @@ import {HtmlParser} from '../ml_parser/html_parser';
|
|||||||
|
|
||||||
import * as i18n from './i18n_ast';
|
import * as i18n from './i18n_ast';
|
||||||
import {I18nError} from './parse_util';
|
import {I18nError} from './parse_util';
|
||||||
import {Serializer} from './serializers/serializer';
|
import {PlaceholderMapper, Serializer} from './serializers/serializer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A container for translated messages
|
* A container for translated messages
|
||||||
@ -21,16 +21,20 @@ export class TranslationBundle {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
|
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
|
||||||
public digest: (m: i18n.Message) => string) {
|
public digest: (m: i18n.Message) => string,
|
||||||
this._i18nToHtml = new I18nToHtmlVisitor(_i18nNodesByMsgId, digest);
|
public mapperFactory?: (m: i18n.Message) => PlaceholderMapper) {
|
||||||
|
this._i18nToHtml = new I18nToHtmlVisitor(_i18nNodesByMsgId, digest, mapperFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creates a `TranslationBundle` by parsing the given `content` with the `serializer`.
|
||||||
static load(content: string, url: string, serializer: Serializer): TranslationBundle {
|
static load(content: string, url: string, serializer: Serializer): TranslationBundle {
|
||||||
const i18nNodesByMsgId = serializer.load(content, url);
|
const i18nNodesByMsgId = serializer.load(content, url);
|
||||||
const digestFn = (m: i18n.Message) => serializer.digest(m);
|
const digestFn = (m: i18n.Message) => serializer.digest(m);
|
||||||
return new TranslationBundle(i18nNodesByMsgId, digestFn);
|
const mapperFactory = (m: i18n.Message) => serializer.createNameMapper(m);
|
||||||
|
return new TranslationBundle(i18nNodesByMsgId, digestFn, mapperFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the translation as HTML nodes from the given source message.
|
||||||
get(srcMsg: i18n.Message): html.Node[] {
|
get(srcMsg: i18n.Message): html.Node[] {
|
||||||
const html = this._i18nToHtml.convert(srcMsg);
|
const html = this._i18nToHtml.convert(srcMsg);
|
||||||
|
|
||||||
@ -46,15 +50,17 @@ export class TranslationBundle {
|
|||||||
|
|
||||||
class I18nToHtmlVisitor implements i18n.Visitor {
|
class I18nToHtmlVisitor implements i18n.Visitor {
|
||||||
private _srcMsg: i18n.Message;
|
private _srcMsg: i18n.Message;
|
||||||
private _srcMsgStack: i18n.Message[] = [];
|
private _contextStack: {msg: i18n.Message, mapper: (name: string) => string}[] = [];
|
||||||
private _errors: I18nError[] = [];
|
private _errors: I18nError[] = [];
|
||||||
|
private _mapper: (name: string) => string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
|
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
|
||||||
private _digest: (m: i18n.Message) => string) {}
|
private _digest: (m: i18n.Message) => string,
|
||||||
|
private _mapperFactory: (m: i18n.Message) => PlaceholderMapper) {}
|
||||||
|
|
||||||
convert(srcMsg: i18n.Message): {nodes: html.Node[], errors: I18nError[]} {
|
convert(srcMsg: i18n.Message): {nodes: html.Node[], errors: I18nError[]} {
|
||||||
this._srcMsgStack.length = 0;
|
this._contextStack.length = 0;
|
||||||
this._errors.length = 0;
|
this._errors.length = 0;
|
||||||
// i18n to text
|
// i18n to text
|
||||||
const text = this._convertToText(srcMsg);
|
const text = this._convertToText(srcMsg);
|
||||||
@ -88,7 +94,7 @@ class I18nToHtmlVisitor implements i18n.Visitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitPlaceholder(ph: i18n.Placeholder, context?: any): string {
|
visitPlaceholder(ph: i18n.Placeholder, context?: any): string {
|
||||||
const phName = ph.name;
|
const phName = this._mapper(ph.name);
|
||||||
if (this._srcMsg.placeholders.hasOwnProperty(phName)) {
|
if (this._srcMsg.placeholders.hasOwnProperty(phName)) {
|
||||||
return this._srcMsg.placeholders[phName];
|
return this._srcMsg.placeholders[phName];
|
||||||
}
|
}
|
||||||
@ -105,14 +111,26 @@ class I18nToHtmlVisitor implements i18n.Visitor {
|
|||||||
|
|
||||||
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any { throw 'unreachable code'; }
|
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any { throw 'unreachable code'; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a source message to a translated text string:
|
||||||
|
* - text nodes are replaced with their translation,
|
||||||
|
* - placeholders are replaced with their content,
|
||||||
|
* - ICU nodes are converted to ICU expressions.
|
||||||
|
*/
|
||||||
private _convertToText(srcMsg: i18n.Message): string {
|
private _convertToText(srcMsg: i18n.Message): string {
|
||||||
const digest = this._digest(srcMsg);
|
const digest = this._digest(srcMsg);
|
||||||
|
const mapper = this._mapperFactory ? this._mapperFactory(srcMsg) : null;
|
||||||
|
|
||||||
if (this._i18nNodesByMsgId.hasOwnProperty(digest)) {
|
if (this._i18nNodesByMsgId.hasOwnProperty(digest)) {
|
||||||
this._srcMsgStack.push(this._srcMsg);
|
this._contextStack.push({msg: this._srcMsg, mapper: this._mapper});
|
||||||
this._srcMsg = srcMsg;
|
this._srcMsg = srcMsg;
|
||||||
|
this._mapper = (name: string) => mapper ? mapper.toInternalName(name) : name;
|
||||||
|
|
||||||
const nodes = this._i18nNodesByMsgId[digest];
|
const nodes = this._i18nNodesByMsgId[digest];
|
||||||
const text = nodes.map(node => node.visit(this)).join('');
|
const text = nodes.map(node => node.visit(this)).join('');
|
||||||
this._srcMsg = this._srcMsgStack.pop();
|
const context = this._contextStack.pop();
|
||||||
|
this._srcMsg = context.msg;
|
||||||
|
this._mapper = context.mapper;
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,11 +6,11 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, AnimationTransitionEvent, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
import {StaticSymbol} from './aot/static_symbol';
|
import {StaticSymbol} from './aot/static_symbol';
|
||||||
import {CompileIdentifierMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
|
import {CompileIdentifierMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
|
||||||
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, ComponentRef_, DebugAppView, DebugContext, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, ValueUnwrapper, ViewContainer, ViewType, balanceAnimationKeyframes, clearStyles, collectAndResolveStyles, devModeEqual, prepareFinalAnimationStyles, reflector, registerModuleFactory, renderStyles, view_utils} from './private_import_core';
|
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, ComponentRef_, DebugAppView, DebugContext, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewContainer, ViewType, balanceAnimationKeyframes, clearStyles, collectAndResolveStyles, devModeEqual, prepareFinalAnimationStyles, reflector, registerModuleFactory, renderStyles, view_utils} from './private_import_core';
|
||||||
|
|
||||||
const APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
|
const APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
|
||||||
const VIEW_UTILS_MODULE_URL = assetUrl('core', 'linker/view_utils');
|
const VIEW_UTILS_MODULE_URL = assetUrl('core', 'linker/view_utils');
|
||||||
@ -161,6 +161,8 @@ export class Identifiers {
|
|||||||
};
|
};
|
||||||
static SimpleChange:
|
static SimpleChange:
|
||||||
IdentifierSpec = {name: 'SimpleChange', moduleUrl: CD_MODULE_URL, runtime: SimpleChange};
|
IdentifierSpec = {name: 'SimpleChange', moduleUrl: CD_MODULE_URL, runtime: SimpleChange};
|
||||||
|
static UNINITIALIZED:
|
||||||
|
IdentifierSpec = {name: 'UNINITIALIZED', moduleUrl: CD_MODULE_URL, runtime: UNINITIALIZED};
|
||||||
static ChangeDetectorStatus: IdentifierSpec = {
|
static ChangeDetectorStatus: IdentifierSpec = {
|
||||||
name: 'ChangeDetectorStatus',
|
name: 'ChangeDetectorStatus',
|
||||||
moduleUrl: CD_MODULE_URL,
|
moduleUrl: CD_MODULE_URL,
|
||||||
@ -171,36 +173,6 @@ export class Identifiers {
|
|||||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||||
runtime: view_utils.checkBinding
|
runtime: view_utils.checkBinding
|
||||||
};
|
};
|
||||||
static checkBindingChange: IdentifierSpec = {
|
|
||||||
name: 'checkBindingChange',
|
|
||||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
|
||||||
runtime: view_utils.checkBindingChange
|
|
||||||
};
|
|
||||||
static checkRenderText: IdentifierSpec = {
|
|
||||||
name: 'checkRenderText',
|
|
||||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
|
||||||
runtime: view_utils.checkRenderText
|
|
||||||
};
|
|
||||||
static checkRenderProperty: IdentifierSpec = {
|
|
||||||
name: 'checkRenderProperty',
|
|
||||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
|
||||||
runtime: view_utils.checkRenderProperty
|
|
||||||
};
|
|
||||||
static checkRenderAttribute: IdentifierSpec = {
|
|
||||||
name: 'checkRenderAttribute',
|
|
||||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
|
||||||
runtime: view_utils.checkRenderAttribute
|
|
||||||
};
|
|
||||||
static checkRenderClass: IdentifierSpec = {
|
|
||||||
name: 'checkRenderClass',
|
|
||||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
|
||||||
runtime: view_utils.checkRenderClass
|
|
||||||
};
|
|
||||||
static checkRenderStyle: IdentifierSpec = {
|
|
||||||
name: 'checkRenderStyle',
|
|
||||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
|
||||||
runtime: view_utils.checkRenderStyle
|
|
||||||
};
|
|
||||||
static devModeEqual:
|
static devModeEqual:
|
||||||
IdentifierSpec = {name: 'devModeEqual', moduleUrl: CD_MODULE_URL, runtime: devModeEqual};
|
IdentifierSpec = {name: 'devModeEqual', moduleUrl: CD_MODULE_URL, runtime: devModeEqual};
|
||||||
static inlineInterpolate: IdentifierSpec = {
|
static inlineInterpolate: IdentifierSpec = {
|
||||||
|
@ -10,7 +10,7 @@ import {Compiler, ComponentFactory, Injector, ModuleWithComponentFactories, NgMo
|
|||||||
|
|
||||||
import {AnimationCompiler} from '../animation/animation_compiler';
|
import {AnimationCompiler} from '../animation/animation_compiler';
|
||||||
import {AnimationParser} from '../animation/animation_parser';
|
import {AnimationParser} from '../animation/animation_parser';
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, ProxyClass, createHostComponentMeta, identifierName} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta, identifierName} from '../compile_metadata';
|
||||||
import {CompilerConfig} from '../config';
|
import {CompilerConfig} from '../config';
|
||||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||||
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||||
@ -21,11 +21,10 @@ import {NgModuleCompiler} from '../ng_module_compiler';
|
|||||||
import * as ir from '../output/output_ast';
|
import * as ir from '../output/output_ast';
|
||||||
import {interpretStatements} from '../output/output_interpreter';
|
import {interpretStatements} from '../output/output_interpreter';
|
||||||
import {jitStatements} from '../output/output_jit';
|
import {jitStatements} from '../output/output_jit';
|
||||||
import {view_utils} from '../private_import_core';
|
|
||||||
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
|
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
|
||||||
import {TemplateParser} from '../template_parser/template_parser';
|
import {TemplateParser} from '../template_parser/template_parser';
|
||||||
import {SyncAsyncResult} from '../util';
|
import {SyncAsyncResult} from '../util';
|
||||||
import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency, ViewCompiler} from '../view_compiler/view_compiler';
|
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompiler} from '../view_compiler/view_compiler';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -131,6 +130,10 @@ export class JitCompiler implements Compiler {
|
|||||||
const extraProviders = [this._metadataResolver.getProviderMetadata(new ProviderMeta(
|
const extraProviders = [this._metadataResolver.getProviderMetadata(new ProviderMeta(
|
||||||
Compiler, {useFactory: () => new ModuleBoundCompiler(this, moduleMeta.type.reference)}))];
|
Compiler, {useFactory: () => new ModuleBoundCompiler(this, moduleMeta.type.reference)}))];
|
||||||
const compileResult = this._ngModuleCompiler.compile(moduleMeta, extraProviders);
|
const compileResult = this._ngModuleCompiler.compile(moduleMeta, extraProviders);
|
||||||
|
compileResult.dependencies.forEach((dep) => {
|
||||||
|
dep.placeholder.reference =
|
||||||
|
this._assertComponentKnown(dep.comp.reference, true).proxyComponentFactory;
|
||||||
|
});
|
||||||
if (!this._compilerConfig.useJit) {
|
if (!this._compilerConfig.useJit) {
|
||||||
ngModuleFactory =
|
ngModuleFactory =
|
||||||
interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar);
|
interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar);
|
||||||
@ -165,7 +168,7 @@ export class JitCompiler implements Compiler {
|
|||||||
const template =
|
const template =
|
||||||
this._createCompiledHostTemplate(dirMeta.type.reference, localModuleMeta);
|
this._createCompiledHostTemplate(dirMeta.type.reference, localModuleMeta);
|
||||||
templates.add(template);
|
templates.add(template);
|
||||||
allComponentFactories.push(<ComponentFactory<any>>dirMeta.componentFactory);
|
allComponentFactories.push(template.proxyComponentFactory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -177,16 +180,15 @@ export class JitCompiler implements Compiler {
|
|||||||
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
|
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
|
||||||
if (dirMeta.isComponent) {
|
if (dirMeta.isComponent) {
|
||||||
dirMeta.entryComponents.forEach((entryComponentType) => {
|
dirMeta.entryComponents.forEach((entryComponentType) => {
|
||||||
const moduleMeta = moduleByDirective.get(entryComponentType.componentType);
|
const moduleMeta = moduleByDirective.get(entryComponentType.reference);
|
||||||
templates.add(
|
templates.add(
|
||||||
this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta));
|
this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
localModuleMeta.entryComponents.forEach((entryComponentType) => {
|
localModuleMeta.entryComponents.forEach((entryComponentType) => {
|
||||||
const moduleMeta = moduleByDirective.get(entryComponentType.componentType);
|
const moduleMeta = moduleByDirective.get(entryComponentType.reference);
|
||||||
templates.add(
|
templates.add(this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta));
|
||||||
this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
templates.forEach((template) => this._compileTemplate(template));
|
templates.forEach((template) => this._compileTemplate(template));
|
||||||
@ -220,12 +222,12 @@ export class JitCompiler implements Compiler {
|
|||||||
const compMeta = this._metadataResolver.getDirectiveMetadata(compType);
|
const compMeta = this._metadataResolver.getDirectiveMetadata(compType);
|
||||||
assertComponent(compMeta);
|
assertComponent(compMeta);
|
||||||
|
|
||||||
const componentFactory = <ComponentFactory<any>>compMeta.componentFactory;
|
const HostClass = function HostClass() {};
|
||||||
const hostClass = this._metadataResolver.getHostComponentType(compType);
|
(<any>HostClass).overriddenName = `${identifierName(compMeta.type)}_Host`;
|
||||||
const hostMeta = createHostComponentMeta(
|
|
||||||
hostClass, compMeta, <any>view_utils.getComponentFactoryViewClass(componentFactory));
|
const hostMeta = createHostComponentMeta(HostClass, compMeta);
|
||||||
compiledTemplate =
|
compiledTemplate = new CompiledTemplate(
|
||||||
new CompiledTemplate(true, compMeta.type, hostMeta, ngModule, [compMeta.type]);
|
true, compMeta.selector, compMeta.type, hostMeta, ngModule, [compMeta.type]);
|
||||||
this._compiledHostTemplateCache.set(compType, compiledTemplate);
|
this._compiledHostTemplateCache.set(compType, compiledTemplate);
|
||||||
}
|
}
|
||||||
return compiledTemplate;
|
return compiledTemplate;
|
||||||
@ -237,7 +239,8 @@ export class JitCompiler implements Compiler {
|
|||||||
if (!compiledTemplate) {
|
if (!compiledTemplate) {
|
||||||
assertComponent(compMeta);
|
assertComponent(compMeta);
|
||||||
compiledTemplate = new CompiledTemplate(
|
compiledTemplate = new CompiledTemplate(
|
||||||
false, compMeta.type, compMeta, ngModule, ngModule.transitiveModule.directives);
|
false, compMeta.selector, compMeta.type, compMeta, ngModule,
|
||||||
|
ngModule.transitiveModule.directives);
|
||||||
this._compiledTemplateCache.set(compMeta.type.reference, compiledTemplate);
|
this._compiledTemplateCache.set(compMeta.type.reference, compiledTemplate);
|
||||||
}
|
}
|
||||||
return compiledTemplate;
|
return compiledTemplate;
|
||||||
@ -274,7 +277,6 @@ export class JitCompiler implements Compiler {
|
|||||||
`/${identifierName(moduleMeta.type)}/${identifierName(dirMeta.type)}/wrapper.ngfactory.js`,
|
`/${identifierName(moduleMeta.type)}/${identifierName(dirMeta.type)}/wrapper.ngfactory.js`,
|
||||||
statements, compileResult.dirWrapperClassVar);
|
statements, compileResult.dirWrapperClassVar);
|
||||||
}
|
}
|
||||||
(<ProxyClass>dirMeta.wrapperType).setDelegate(directiveWrapperClass);
|
|
||||||
this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass);
|
this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,6 +304,21 @@ export class JitCompiler implements Compiler {
|
|||||||
const compileResult = this._viewCompiler.compileComponent(
|
const compileResult = this._viewCompiler.compileComponent(
|
||||||
compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar),
|
compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar),
|
||||||
pipes, compiledAnimations);
|
pipes, compiledAnimations);
|
||||||
|
compileResult.dependencies.forEach((dep) => {
|
||||||
|
let depTemplate: CompiledTemplate;
|
||||||
|
if (dep instanceof ViewClassDependency) {
|
||||||
|
const vfd = <ViewClassDependency>dep;
|
||||||
|
depTemplate = this._assertComponentKnown(vfd.comp.reference, false);
|
||||||
|
vfd.placeholder.reference = depTemplate.proxyViewClass;
|
||||||
|
} else if (dep instanceof ComponentFactoryDependency) {
|
||||||
|
const cfd = <ComponentFactoryDependency>dep;
|
||||||
|
depTemplate = this._assertComponentKnown(cfd.comp.reference, true);
|
||||||
|
cfd.placeholder.reference = depTemplate.proxyComponentFactory;
|
||||||
|
} else if (dep instanceof DirectiveWrapperDependency) {
|
||||||
|
const dwd = <DirectiveWrapperDependency>dep;
|
||||||
|
dwd.placeholder.reference = this._assertDirectiveWrapper(dwd.dir.reference);
|
||||||
|
}
|
||||||
|
});
|
||||||
const statements = stylesCompileResult.componentStylesheet.statements
|
const statements = stylesCompileResult.componentStylesheet.statements
|
||||||
.concat(...compiledAnimations.map(ca => ca.statements))
|
.concat(...compiledAnimations.map(ca => ca.statements))
|
||||||
.concat(compileResult.statements);
|
.concat(compileResult.statements);
|
||||||
@ -341,18 +358,30 @@ export class JitCompiler implements Compiler {
|
|||||||
|
|
||||||
class CompiledTemplate {
|
class CompiledTemplate {
|
||||||
private _viewClass: Function = null;
|
private _viewClass: Function = null;
|
||||||
|
proxyViewClass: Type<any>;
|
||||||
|
proxyComponentFactory: ComponentFactory<any>;
|
||||||
isCompiled = false;
|
isCompiled = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public isHost: boolean, public compType: CompileIdentifierMetadata,
|
public isHost: boolean, selector: string, public compType: CompileIdentifierMetadata,
|
||||||
public compMeta: CompileDirectiveMetadata, public ngModule: CompileNgModuleMetadata,
|
public compMeta: CompileDirectiveMetadata, public ngModule: CompileNgModuleMetadata,
|
||||||
public directives: CompileIdentifierMetadata[]) {
|
public directives: CompileIdentifierMetadata[]) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
this.proxyViewClass = <any>function() {
|
||||||
|
if (!self._viewClass) {
|
||||||
|
throw new Error(
|
||||||
|
`Illegal state: CompiledTemplate for ${stringify(self.compType)} is not compiled yet!`);
|
||||||
|
}
|
||||||
|
return self._viewClass.apply(this, arguments);
|
||||||
|
};
|
||||||
|
this.proxyComponentFactory = isHost ?
|
||||||
|
new ComponentFactory<any>(selector, this.proxyViewClass, compType.reference) :
|
||||||
|
null;
|
||||||
}
|
}
|
||||||
|
|
||||||
compiled(viewClass: Function) {
|
compiled(viewClass: Function) {
|
||||||
this._viewClass = viewClass;
|
this._viewClass = viewClass;
|
||||||
(<ProxyClass>this.compMeta.componentViewType).setDelegate(viewClass);
|
this.proxyViewClass.prototype = viewClass.prototype;
|
||||||
this.isCompiled = true;
|
this.isCompiled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,16 +6,16 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, ComponentFactory, Directive, Host, Inject, Injectable, ModuleWithProviders, OpaqueToken, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
|
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, Directive, Host, Inject, Injectable, ModuleWithProviders, OpaqueToken, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
|
||||||
|
|
||||||
import {StaticSymbol, StaticSymbolCache} from './aot/static_symbol';
|
import {StaticSymbol} from './aot/static_symbol';
|
||||||
import {ngfactoryFilePath} from './aot/util';
|
|
||||||
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
|
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
|
||||||
import * as cpl from './compile_metadata';
|
import * as cpl from './compile_metadata';
|
||||||
import {DirectiveNormalizer} from './directive_normalizer';
|
import {DirectiveNormalizer} from './directive_normalizer';
|
||||||
import {DirectiveResolver} from './directive_resolver';
|
import {DirectiveResolver} from './directive_resolver';
|
||||||
import {stringify} from './facade/lang';
|
import {ListWrapper, StringMapWrapper} from './facade/collection';
|
||||||
import {Identifiers, resolveIdentifier} from './identifiers';
|
import {isBlank, isPresent, stringify} from './facade/lang';
|
||||||
|
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
|
||||||
import {CompilerInjectable} from './injectable';
|
import {CompilerInjectable} from './injectable';
|
||||||
import {hasLifecycleHook} from './lifecycle_reflector';
|
import {hasLifecycleHook} from './lifecycle_reflector';
|
||||||
import {NgModuleResolver} from './ng_module_resolver';
|
import {NgModuleResolver} from './ng_module_resolver';
|
||||||
@ -24,7 +24,7 @@ import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, ref
|
|||||||
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
||||||
import {SummaryResolver} from './summary_resolver';
|
import {SummaryResolver} from './summary_resolver';
|
||||||
import {getUrlScheme} from './url_resolver';
|
import {getUrlScheme} from './url_resolver';
|
||||||
import {MODULE_SUFFIX, SyntaxError, ValueTransformer, visitValue} from './util';
|
import {MODULE_SUFFIX, SyncAsyncResult, SyntaxError, ValueTransformer, visitValue} from './util';
|
||||||
|
|
||||||
export type ErrorCollector = (error: any, type?: any) => void;
|
export type ErrorCollector = (error: any, type?: any) => void;
|
||||||
export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
|
export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
|
||||||
@ -38,8 +38,6 @@ export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
|
|||||||
// to wait correctly.
|
// to wait correctly.
|
||||||
@CompilerInjectable()
|
@CompilerInjectable()
|
||||||
export class CompileMetadataResolver {
|
export class CompileMetadataResolver {
|
||||||
private _nonNormalizedDirectiveCache =
|
|
||||||
new Map<Type<any>, {annotation: Directive, metadata: cpl.CompileDirectiveMetadata}>();
|
|
||||||
private _directiveCache = new Map<Type<any>, cpl.CompileDirectiveMetadata>();
|
private _directiveCache = new Map<Type<any>, cpl.CompileDirectiveMetadata>();
|
||||||
private _summaryCache = new Map<Type<any>, cpl.CompileTypeSummary>();
|
private _summaryCache = new Map<Type<any>, cpl.CompileTypeSummary>();
|
||||||
private _pipeCache = new Map<Type<any>, cpl.CompilePipeMetadata>();
|
private _pipeCache = new Map<Type<any>, cpl.CompilePipeMetadata>();
|
||||||
@ -51,14 +49,12 @@ export class CompileMetadataResolver {
|
|||||||
private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver<any>,
|
private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver<any>,
|
||||||
private _schemaRegistry: ElementSchemaRegistry,
|
private _schemaRegistry: ElementSchemaRegistry,
|
||||||
private _directiveNormalizer: DirectiveNormalizer,
|
private _directiveNormalizer: DirectiveNormalizer,
|
||||||
@Optional() private _staticSymbolCache: StaticSymbolCache,
|
|
||||||
private _reflector: ReflectorReader = reflector,
|
private _reflector: ReflectorReader = reflector,
|
||||||
@Optional() @Inject(ERROR_COLLECTOR_TOKEN) private _errorCollector?: ErrorCollector) {}
|
@Optional() @Inject(ERROR_COLLECTOR_TOKEN) private _errorCollector?: ErrorCollector) {}
|
||||||
|
|
||||||
clearCacheFor(type: Type<any>) {
|
clearCacheFor(type: Type<any>) {
|
||||||
const dirMeta = this._directiveCache.get(type);
|
const dirMeta = this._directiveCache.get(type);
|
||||||
this._directiveCache.delete(type);
|
this._directiveCache.delete(type);
|
||||||
this._nonNormalizedDirectiveCache.delete(type);
|
|
||||||
this._summaryCache.delete(type);
|
this._summaryCache.delete(type);
|
||||||
this._pipeCache.delete(type);
|
this._pipeCache.delete(type);
|
||||||
this._ngModuleOfTypes.delete(type);
|
this._ngModuleOfTypes.delete(type);
|
||||||
@ -69,9 +65,8 @@ export class CompileMetadataResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCache(): void {
|
clearCache() {
|
||||||
this._directiveCache.clear();
|
this._directiveCache.clear();
|
||||||
this._nonNormalizedDirectiveCache.clear();
|
|
||||||
this._summaryCache.clear();
|
this._summaryCache.clear();
|
||||||
this._pipeCache.clear();
|
this._pipeCache.clear();
|
||||||
this._ngModuleCache.clear();
|
this._ngModuleCache.clear();
|
||||||
@ -79,66 +74,6 @@ export class CompileMetadataResolver {
|
|||||||
this._directiveNormalizer.clearCache();
|
this._directiveNormalizer.clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createProxyClass(baseType: any, name: string): cpl.ProxyClass {
|
|
||||||
let delegate: any = null;
|
|
||||||
const proxyClass: cpl.ProxyClass = <any>function() {
|
|
||||||
if (!delegate) {
|
|
||||||
throw new Error(
|
|
||||||
`Illegal state: Class ${name} for type ${stringify(baseType)} is not compiled yet!`);
|
|
||||||
}
|
|
||||||
return delegate.apply(this, arguments);
|
|
||||||
};
|
|
||||||
proxyClass.setDelegate = (d) => {
|
|
||||||
delegate = d;
|
|
||||||
(<any>proxyClass).prototype = d.prototype;
|
|
||||||
};
|
|
||||||
// Make stringify work correctly
|
|
||||||
(<any>proxyClass).overriddenName = name;
|
|
||||||
return proxyClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getGeneratedClass(dirType: any, name: string): StaticSymbol|cpl.ProxyClass {
|
|
||||||
if (dirType instanceof StaticSymbol) {
|
|
||||||
return this._staticSymbolCache.get(ngfactoryFilePath(dirType.filePath), name);
|
|
||||||
} else {
|
|
||||||
return this._createProxyClass(dirType, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getDirectiveWrapperClass(dirType: any): StaticSymbol|cpl.ProxyClass {
|
|
||||||
return this.getGeneratedClass(dirType, cpl.dirWrapperClassName(dirType));
|
|
||||||
}
|
|
||||||
|
|
||||||
private getComponentViewClass(dirType: any): StaticSymbol|cpl.ProxyClass {
|
|
||||||
return this.getGeneratedClass(dirType, cpl.viewClassName(dirType, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
getHostComponentViewClass(dirType: any): StaticSymbol|cpl.ProxyClass {
|
|
||||||
return this.getGeneratedClass(dirType, cpl.hostViewClassName(dirType));
|
|
||||||
}
|
|
||||||
|
|
||||||
getHostComponentType(dirType: any): StaticSymbol|Type<any> {
|
|
||||||
const name = `${cpl.identifierName({reference: dirType})}_Host`;
|
|
||||||
if (dirType instanceof StaticSymbol) {
|
|
||||||
return this._staticSymbolCache.get(dirType.filePath, name);
|
|
||||||
} else {
|
|
||||||
const HostClass = <any>function HostClass() {};
|
|
||||||
HostClass.overriddenName = name;
|
|
||||||
|
|
||||||
return HostClass;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getComponentFactory(selector: string, dirType: any): StaticSymbol|ComponentFactory<any> {
|
|
||||||
if (dirType instanceof StaticSymbol) {
|
|
||||||
return this._staticSymbolCache.get(
|
|
||||||
ngfactoryFilePath(dirType.filePath), cpl.componentFactoryName(dirType));
|
|
||||||
} else {
|
|
||||||
const hostView = this.getHostComponentViewClass(dirType);
|
|
||||||
return new ComponentFactory(selector, <any>hostView, dirType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata {
|
getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata {
|
||||||
const defs = entry.definitions.map(def => this._getAnimationStateMetadata(def));
|
const defs = entry.definitions.map(def => this._getAnimationStateMetadata(def));
|
||||||
return new cpl.CompileAnimationEntryMetadata(entry.name, defs);
|
return new cpl.CompileAnimationEntryMetadata(entry.name, defs);
|
||||||
@ -227,9 +162,6 @@ export class CompileMetadataResolver {
|
|||||||
queries: metadata.queries,
|
queries: metadata.queries,
|
||||||
viewQueries: metadata.viewQueries,
|
viewQueries: metadata.viewQueries,
|
||||||
entryComponents: metadata.entryComponents,
|
entryComponents: metadata.entryComponents,
|
||||||
wrapperType: metadata.wrapperType,
|
|
||||||
componentViewType: metadata.componentViewType,
|
|
||||||
componentFactory: metadata.componentFactory,
|
|
||||||
template: templateMetadata
|
template: templateMetadata
|
||||||
});
|
});
|
||||||
this._directiveCache.set(directiveType, normalizedDirMeta);
|
this._directiveCache.set(directiveType, normalizedDirMeta);
|
||||||
@ -269,14 +201,7 @@ export class CompileMetadataResolver {
|
|||||||
getNonNormalizedDirectiveMetadata(directiveType: any):
|
getNonNormalizedDirectiveMetadata(directiveType: any):
|
||||||
{annotation: Directive, metadata: cpl.CompileDirectiveMetadata} {
|
{annotation: Directive, metadata: cpl.CompileDirectiveMetadata} {
|
||||||
directiveType = resolveForwardRef(directiveType);
|
directiveType = resolveForwardRef(directiveType);
|
||||||
if (!directiveType) {
|
const dirMeta = this._directiveResolver.resolve(directiveType);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
let cacheEntry = this._nonNormalizedDirectiveCache.get(directiveType);
|
|
||||||
if (cacheEntry) {
|
|
||||||
return cacheEntry;
|
|
||||||
}
|
|
||||||
const dirMeta = this._directiveResolver.resolve(directiveType, false);
|
|
||||||
if (!dirMeta) {
|
if (!dirMeta) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -305,7 +230,7 @@ export class CompileMetadataResolver {
|
|||||||
|
|
||||||
let changeDetectionStrategy: ChangeDetectionStrategy = null;
|
let changeDetectionStrategy: ChangeDetectionStrategy = null;
|
||||||
let viewProviders: cpl.CompileProviderMetadata[] = [];
|
let viewProviders: cpl.CompileProviderMetadata[] = [];
|
||||||
let entryComponentMetadata: cpl.CompileEntryComponentMetadata[] = [];
|
let entryComponentMetadata: cpl.CompileIdentifierMetadata[] = [];
|
||||||
let selector = dirMeta.selector;
|
let selector = dirMeta.selector;
|
||||||
|
|
||||||
if (dirMeta instanceof Component) {
|
if (dirMeta instanceof Component) {
|
||||||
@ -318,7 +243,7 @@ export class CompileMetadataResolver {
|
|||||||
}
|
}
|
||||||
if (dirMeta.entryComponents) {
|
if (dirMeta.entryComponents) {
|
||||||
entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents)
|
entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents)
|
||||||
.map((type) => this._getEntryComponentMetadata(type))
|
.map((type) => this._getIdentifierMetadata(type))
|
||||||
.concat(entryComponentMetadata);
|
.concat(entryComponentMetadata);
|
||||||
}
|
}
|
||||||
if (!selector) {
|
if (!selector) {
|
||||||
@ -336,14 +261,14 @@ export class CompileMetadataResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let providers: cpl.CompileProviderMetadata[] = [];
|
let providers: cpl.CompileProviderMetadata[] = [];
|
||||||
if (dirMeta.providers != null) {
|
if (isPresent(dirMeta.providers)) {
|
||||||
providers = this._getProvidersMetadata(
|
providers = this._getProvidersMetadata(
|
||||||
dirMeta.providers, entryComponentMetadata,
|
dirMeta.providers, entryComponentMetadata,
|
||||||
`providers for "${stringifyType(directiveType)}"`, [], directiveType);
|
`providers for "${stringifyType(directiveType)}"`, [], directiveType);
|
||||||
}
|
}
|
||||||
let queries: cpl.CompileQueryMetadata[] = [];
|
let queries: cpl.CompileQueryMetadata[] = [];
|
||||||
let viewQueries: cpl.CompileQueryMetadata[] = [];
|
let viewQueries: cpl.CompileQueryMetadata[] = [];
|
||||||
if (dirMeta.queries != null) {
|
if (isPresent(dirMeta.queries)) {
|
||||||
queries = this._getQueriesMetadata(dirMeta.queries, false, directiveType);
|
queries = this._getQueriesMetadata(dirMeta.queries, false, directiveType);
|
||||||
viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType);
|
viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType);
|
||||||
}
|
}
|
||||||
@ -362,17 +287,9 @@ export class CompileMetadataResolver {
|
|||||||
viewProviders: viewProviders,
|
viewProviders: viewProviders,
|
||||||
queries: queries,
|
queries: queries,
|
||||||
viewQueries: viewQueries,
|
viewQueries: viewQueries,
|
||||||
entryComponents: entryComponentMetadata,
|
entryComponents: entryComponentMetadata
|
||||||
wrapperType: this.getDirectiveWrapperClass(directiveType),
|
|
||||||
componentViewType: nonNormalizedTemplateMetadata ? this.getComponentViewClass(directiveType) :
|
|
||||||
undefined,
|
|
||||||
componentFactory: nonNormalizedTemplateMetadata ?
|
|
||||||
this.getComponentFactory(selector, directiveType) :
|
|
||||||
undefined
|
|
||||||
});
|
});
|
||||||
cacheEntry = {metadata, annotation: dirMeta};
|
return {metadata, annotation: dirMeta};
|
||||||
this._nonNormalizedDirectiveCache.set(directiveType, cacheEntry);
|
|
||||||
return cacheEntry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -454,7 +371,7 @@ export class CompileMetadataResolver {
|
|||||||
const importedModules: cpl.CompileNgModuleSummary[] = [];
|
const importedModules: cpl.CompileNgModuleSummary[] = [];
|
||||||
const exportedModules: cpl.CompileNgModuleSummary[] = [];
|
const exportedModules: cpl.CompileNgModuleSummary[] = [];
|
||||||
const providers: cpl.CompileProviderMetadata[] = [];
|
const providers: cpl.CompileProviderMetadata[] = [];
|
||||||
const entryComponents: cpl.CompileEntryComponentMetadata[] = [];
|
const entryComponents: cpl.CompileIdentifierMetadata[] = [];
|
||||||
const bootstrapComponents: cpl.CompileIdentifierMetadata[] = [];
|
const bootstrapComponents: cpl.CompileIdentifierMetadata[] = [];
|
||||||
const schemas: SchemaMetadata[] = [];
|
const schemas: SchemaMetadata[] = [];
|
||||||
|
|
||||||
@ -571,7 +488,7 @@ export class CompileMetadataResolver {
|
|||||||
|
|
||||||
if (meta.entryComponents) {
|
if (meta.entryComponents) {
|
||||||
entryComponents.push(...flattenAndDedupeArray(meta.entryComponents)
|
entryComponents.push(...flattenAndDedupeArray(meta.entryComponents)
|
||||||
.map(type => this._getEntryComponentMetadata(type)));
|
.map(type => this._getIdentifierMetadata(type)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.bootstrap) {
|
if (meta.bootstrap) {
|
||||||
@ -587,8 +504,7 @@ export class CompileMetadataResolver {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
entryComponents.push(
|
entryComponents.push(...bootstrapComponents);
|
||||||
...bootstrapComponents.map(type => this._getEntryComponentMetadata(type.reference)));
|
|
||||||
|
|
||||||
if (meta.schemas) {
|
if (meta.schemas) {
|
||||||
schemas.push(...flattenAndDedupeArray(meta.schemas));
|
schemas.push(...flattenAndDedupeArray(meta.schemas));
|
||||||
@ -806,14 +722,14 @@ export class CompileMetadataResolver {
|
|||||||
token = paramEntry.attributeName;
|
token = paramEntry.attributeName;
|
||||||
} else if (paramEntry instanceof Inject) {
|
} else if (paramEntry instanceof Inject) {
|
||||||
token = paramEntry.token;
|
token = paramEntry.token;
|
||||||
} else if (isValidType(paramEntry) && token == null) {
|
} else if (isValidType(paramEntry) && isBlank(token)) {
|
||||||
token = paramEntry;
|
token = paramEntry;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
token = param;
|
token = param;
|
||||||
}
|
}
|
||||||
if (token == null) {
|
if (isBlank(token)) {
|
||||||
hasUnknownDeps = true;
|
hasUnknownDeps = true;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -853,7 +769,7 @@ export class CompileMetadataResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _getProvidersMetadata(
|
private _getProvidersMetadata(
|
||||||
providers: Provider[], targetEntryComponents: cpl.CompileEntryComponentMetadata[],
|
providers: Provider[], targetEntryComponents: cpl.CompileIdentifierMetadata[],
|
||||||
debugInfo?: string, compileProviders: cpl.CompileProviderMetadata[] = [],
|
debugInfo?: string, compileProviders: cpl.CompileProviderMetadata[] = [],
|
||||||
type?: any): cpl.CompileProviderMetadata[] {
|
type?: any): cpl.CompileProviderMetadata[] {
|
||||||
providers.forEach((provider: any, providerIdx: number) => {
|
providers.forEach((provider: any, providerIdx: number) => {
|
||||||
@ -862,14 +778,10 @@ export class CompileMetadataResolver {
|
|||||||
} else {
|
} else {
|
||||||
provider = resolveForwardRef(provider);
|
provider = resolveForwardRef(provider);
|
||||||
let providerMeta: cpl.ProviderMeta;
|
let providerMeta: cpl.ProviderMeta;
|
||||||
if (provider && typeof provider === 'object' && provider.hasOwnProperty('provide')) {
|
if (provider && typeof provider == 'object' && provider.hasOwnProperty('provide')) {
|
||||||
this._validateProvider(provider);
|
|
||||||
providerMeta = new cpl.ProviderMeta(provider.provide, provider);
|
providerMeta = new cpl.ProviderMeta(provider.provide, provider);
|
||||||
} else if (isValidType(provider)) {
|
} else if (isValidType(provider)) {
|
||||||
providerMeta = new cpl.ProviderMeta(provider, {useClass: provider});
|
providerMeta = new cpl.ProviderMeta(provider, {useClass: provider});
|
||||||
} else if (provider === void 0) {
|
|
||||||
this._reportError(new SyntaxError(
|
|
||||||
`Encountered undefined provider! Usually this means you have a circular dependencies (might be caused by using 'barrel' index.ts files.`));
|
|
||||||
} else {
|
} else {
|
||||||
const providersInfo =
|
const providersInfo =
|
||||||
(<string[]>providers.reduce(
|
(<string[]>providers.reduce(
|
||||||
@ -900,19 +812,9 @@ export class CompileMetadataResolver {
|
|||||||
return compileProviders;
|
return compileProviders;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _validateProvider(provider: any): void {
|
|
||||||
if (provider.hasOwnProperty('useClass') && provider.useClass == null) {
|
|
||||||
this._reportError(new SyntaxError(
|
|
||||||
`Invalid provider for ${stringifyType(provider.provide)}. useClass cannot be ${provider.useClass}.
|
|
||||||
Usually it happens when:
|
|
||||||
1. There's a circular dependency (might be caused by using index.ts (barrel) files).
|
|
||||||
2. Class was used before it was declared. Use forwardRef in this case.`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta, type?: any):
|
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta, type?: any):
|
||||||
cpl.CompileEntryComponentMetadata[] {
|
cpl.CompileIdentifierMetadata[] {
|
||||||
const components: cpl.CompileEntryComponentMetadata[] = [];
|
const components: cpl.CompileIdentifierMetadata[] = [];
|
||||||
const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
|
const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
|
||||||
|
|
||||||
if (provider.useFactory || provider.useExisting || provider.useClass) {
|
if (provider.useFactory || provider.useExisting || provider.useClass) {
|
||||||
@ -930,27 +832,14 @@ export class CompileMetadataResolver {
|
|||||||
|
|
||||||
extractIdentifiers(provider.useValue, collectedIdentifiers);
|
extractIdentifiers(provider.useValue, collectedIdentifiers);
|
||||||
collectedIdentifiers.forEach((identifier) => {
|
collectedIdentifiers.forEach((identifier) => {
|
||||||
const entry = this._getEntryComponentMetadata(identifier.reference);
|
if (this._directiveResolver.isDirective(identifier.reference) ||
|
||||||
if (entry) {
|
this._loadSummary(identifier.reference, cpl.CompileSummaryKind.Directive)) {
|
||||||
components.push(entry);
|
components.push(identifier);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return components;
|
return components;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getEntryComponentMetadata(dirType: any): cpl.CompileEntryComponentMetadata {
|
|
||||||
const dirMeta = this.getNonNormalizedDirectiveMetadata(dirType);
|
|
||||||
if (dirMeta) {
|
|
||||||
return {componentType: dirType, componentFactory: dirMeta.metadata.componentFactory};
|
|
||||||
} else {
|
|
||||||
const dirSummary =
|
|
||||||
<cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive);
|
|
||||||
if (dirSummary) {
|
|
||||||
return {componentType: dirType, componentFactory: dirSummary.componentFactory};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getProviderMetadata(provider: cpl.ProviderMeta): cpl.CompileProviderMetadata {
|
getProviderMetadata(provider: cpl.ProviderMeta): cpl.CompileProviderMetadata {
|
||||||
let compileDeps: cpl.CompileDiDependencyMetadata[];
|
let compileDeps: cpl.CompileDiDependencyMetadata[];
|
||||||
let compileTypeMetadata: cpl.CompileTypeMetadata = null;
|
let compileTypeMetadata: cpl.CompileTypeMetadata = null;
|
||||||
@ -1098,4 +987,4 @@ function stringifyType(type: any): string {
|
|||||||
} else {
|
} else {
|
||||||
return stringify(type);
|
return stringify(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -283,7 +283,7 @@ class _TreeBuilder {
|
|||||||
const tagDef = this.getTagDefinition(el.name);
|
const tagDef = this.getTagDefinition(el.name);
|
||||||
const {parent, container} = this._getParentElementSkippingContainers();
|
const {parent, container} = this._getParentElementSkippingContainers();
|
||||||
|
|
||||||
if (isPresent(parent) && tagDef.requireExtraParent(parent.name)) {
|
if (parent && tagDef.requireExtraParent(parent.name)) {
|
||||||
const newParent = new html.Element(
|
const newParent = new html.Element(
|
||||||
tagDef.parentToAdd, [], [], el.sourceSpan, el.startSourceSpan, el.endSourceSpan);
|
tagDef.parentToAdd, [], [], el.sourceSpan, el.startSourceSpan, el.endSourceSpan);
|
||||||
this._insertBeforeContainer(parent, container, newParent);
|
this._insertBeforeContainer(parent, container, newParent);
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, tokenName, tokenReference} from './compile_metadata';
|
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, tokenName, tokenReference} from './compile_metadata';
|
||||||
import {createDiTokenExpression} from './compiler_util/identifier_util';
|
import {createDiTokenExpression} from './compiler_util/identifier_util';
|
||||||
import {isPresent} from './facade/lang';
|
import {isPresent} from './facade/lang';
|
||||||
import {Identifiers, createIdentifier, createIdentifierToken, resolveIdentifier} from './identifiers';
|
import {Identifiers, createIdentifier, resolveIdentifier} from './identifiers';
|
||||||
import {CompilerInjectable} from './injectable';
|
import {CompilerInjectable} from './injectable';
|
||||||
import {ClassBuilder, createClassStmt} from './output/class_builder';
|
import {ClassBuilder, createClassStmt} from './output/class_builder';
|
||||||
import * as o from './output/output_ast';
|
import * as o from './output/output_ast';
|
||||||
@ -19,12 +19,9 @@ import {LifecycleHooks} from './private_import_core';
|
|||||||
import {NgModuleProviderAnalyzer} from './provider_analyzer';
|
import {NgModuleProviderAnalyzer} from './provider_analyzer';
|
||||||
import {ProviderAst} from './template_parser/template_ast';
|
import {ProviderAst} from './template_parser/template_ast';
|
||||||
|
|
||||||
/**
|
|
||||||
* This is currently not read, but will probably be used in the future.
|
|
||||||
* We keep it as we already pass it through all the rigth places...
|
|
||||||
*/
|
|
||||||
export class ComponentFactoryDependency {
|
export class ComponentFactoryDependency {
|
||||||
constructor(public compType: any) {}
|
constructor(
|
||||||
|
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NgModuleCompileResult {
|
export class NgModuleCompileResult {
|
||||||
@ -49,12 +46,13 @@ export class NgModuleCompiler {
|
|||||||
const bootstrapComponentFactories: CompileIdentifierMetadata[] = [];
|
const bootstrapComponentFactories: CompileIdentifierMetadata[] = [];
|
||||||
const entryComponentFactories =
|
const entryComponentFactories =
|
||||||
ngModuleMeta.transitiveModule.entryComponents.map((entryComponent) => {
|
ngModuleMeta.transitiveModule.entryComponents.map((entryComponent) => {
|
||||||
|
const id: CompileIdentifierMetadata = {reference: null};
|
||||||
if (ngModuleMeta.bootstrapComponents.some(
|
if (ngModuleMeta.bootstrapComponents.some(
|
||||||
(id) => id.reference === entryComponent.componentType)) {
|
(id) => id.reference === entryComponent.reference)) {
|
||||||
bootstrapComponentFactories.push({reference: entryComponent.componentFactory});
|
bootstrapComponentFactories.push(id);
|
||||||
}
|
}
|
||||||
deps.push(new ComponentFactoryDependency(entryComponent.componentType));
|
deps.push(new ComponentFactoryDependency(entryComponent, id));
|
||||||
return {reference: entryComponent.componentFactory};
|
return id;
|
||||||
});
|
});
|
||||||
const builder = new _InjectorBuilder(
|
const builder = new _InjectorBuilder(
|
||||||
ngModuleMeta, entryComponentFactories, bootstrapComponentFactories, sourceSpan);
|
ngModuleMeta, entryComponentFactories, bootstrapComponentFactories, sourceSpan);
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {NgModule, Type} from '@angular/core';
|
import {NgModule, Type} from '@angular/core';
|
||||||
|
|
||||||
import {ListWrapper} from './facade/collection';
|
import {ListWrapper} from './facade/collection';
|
||||||
import {isPresent, stringify} from './facade/lang';
|
import {stringify} from './facade/lang';
|
||||||
import {CompilerInjectable} from './injectable';
|
import {CompilerInjectable} from './injectable';
|
||||||
import {ReflectorReader, reflector} from './private_import_core';
|
import {ReflectorReader, reflector} from './private_import_core';
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ export class NgModuleResolver {
|
|||||||
const ngModuleMeta: NgModule =
|
const ngModuleMeta: NgModule =
|
||||||
ListWrapper.findLast(this._reflector.annotations(type), _isNgModuleMetadata);
|
ListWrapper.findLast(this._reflector.annotations(type), _isNgModuleMetadata);
|
||||||
|
|
||||||
if (isPresent(ngModuleMeta)) {
|
if (ngModuleMeta) {
|
||||||
return ngModuleMeta;
|
return ngModuleMeta;
|
||||||
} else {
|
} else {
|
||||||
if (throwIfNotFound) {
|
if (throwIfNotFound) {
|
||||||
|
@ -7,8 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {StaticSymbol} from '../aot/static_symbol';
|
import {identifierModuleUrl, identifierName} from '../compile_metadata';
|
||||||
import {CompileIdentifierMetadata} from '../compile_metadata';
|
|
||||||
import {isBlank, isPresent} from '../facade/lang';
|
import {isBlank, isPresent} from '../facade/lang';
|
||||||
|
|
||||||
import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
|
import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
|
||||||
@ -17,17 +16,17 @@ import * as o from './output_ast';
|
|||||||
import {ImportResolver} from './path_util';
|
import {ImportResolver} from './path_util';
|
||||||
|
|
||||||
export class JavaScriptEmitter implements OutputEmitter {
|
export class JavaScriptEmitter implements OutputEmitter {
|
||||||
constructor(private _importResolver: ImportResolver) {}
|
constructor(private _importGenerator: ImportResolver) {}
|
||||||
emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string {
|
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
|
||||||
const converter = new JsEmitterVisitor(genFilePath, this._importResolver);
|
const converter = new JsEmitterVisitor(moduleUrl);
|
||||||
const ctx = EmitterVisitorContext.createRoot(exportedVars);
|
const ctx = EmitterVisitorContext.createRoot(exportedVars);
|
||||||
converter.visitAllStatements(stmts, ctx);
|
converter.visitAllStatements(stmts, ctx);
|
||||||
const srcParts: string[] = [];
|
const srcParts: string[] = [];
|
||||||
converter.importsWithPrefixes.forEach((prefix, importedFilePath) => {
|
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
|
||||||
// Note: can't write the real word for import as it screws up system.js auto detection...
|
// Note: can't write the real word for import as it screws up system.js auto detection...
|
||||||
srcParts.push(
|
srcParts.push(
|
||||||
`var ${prefix} = req` +
|
`var ${prefix} = req` +
|
||||||
`uire('${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}');`);
|
`uire('${this._importGenerator.fileNameToModuleName(importedModuleUrl, moduleUrl)}');`);
|
||||||
});
|
});
|
||||||
srcParts.push(ctx.toSource());
|
srcParts.push(ctx.toSource());
|
||||||
return srcParts.join('\n');
|
return srcParts.join('\n');
|
||||||
@ -37,23 +36,20 @@ export class JavaScriptEmitter implements OutputEmitter {
|
|||||||
class JsEmitterVisitor extends AbstractJsEmitterVisitor {
|
class JsEmitterVisitor extends AbstractJsEmitterVisitor {
|
||||||
importsWithPrefixes = new Map<string, string>();
|
importsWithPrefixes = new Map<string, string>();
|
||||||
|
|
||||||
constructor(private _genFilePath: string, private _importResolver: ImportResolver) { super(); }
|
constructor(private _moduleUrl: string) { super(); }
|
||||||
|
|
||||||
private _resolveStaticSymbol(value: CompileIdentifierMetadata): StaticSymbol {
|
|
||||||
const reference = value.reference;
|
|
||||||
if (!(reference instanceof StaticSymbol)) {
|
|
||||||
throw new Error(`Internal error: unknown identifier ${JSON.stringify(value)}`);
|
|
||||||
}
|
|
||||||
return this._importResolver.getImportAs(reference) || reference;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
|
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
|
||||||
const {name, filePath} = this._resolveStaticSymbol(ast.value);
|
const name = identifierName(ast.value);
|
||||||
if (filePath != this._genFilePath) {
|
const moduleUrl = identifierModuleUrl(ast.value);
|
||||||
let prefix = this.importsWithPrefixes.get(filePath);
|
if (isBlank(name)) {
|
||||||
|
console.error('>>>', ast.value);
|
||||||
|
throw new Error(`Internal error: unknown identifier ${ast.value}`);
|
||||||
|
}
|
||||||
|
if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) {
|
||||||
|
let prefix = this.importsWithPrefixes.get(moduleUrl);
|
||||||
if (isBlank(prefix)) {
|
if (isBlank(prefix)) {
|
||||||
prefix = `import${this.importsWithPrefixes.size}`;
|
prefix = `import${this.importsWithPrefixes.size}`;
|
||||||
this.importsWithPrefixes.set(filePath, prefix);
|
this.importsWithPrefixes.set(moduleUrl, prefix);
|
||||||
}
|
}
|
||||||
ctx.print(`${prefix}.`);
|
ctx.print(`${prefix}.`);
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {StaticSymbol} from '../aot/static_symbol';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface that defines how import statements should be generated.
|
* Interface that defines how import statements should be generated.
|
||||||
*/
|
*/
|
||||||
@ -18,10 +16,4 @@ export abstract class ImportResolver {
|
|||||||
*/
|
*/
|
||||||
abstract fileNameToModuleName(importedFilePath: string, containingFilePath: string): string
|
abstract fileNameToModuleName(importedFilePath: string, containingFilePath: string): string
|
||||||
/*|null*/;
|
/*|null*/;
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the given StaticSymbol into another StaticSymbol that should be used
|
|
||||||
* to generate the import from.
|
|
||||||
*/
|
|
||||||
abstract getImportAs(symbol: StaticSymbol): StaticSymbol /*|null*/;
|
|
||||||
}
|
}
|
||||||
|
@ -7,22 +7,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {StaticSymbol} from '../aot/static_symbol';
|
import {CompileIdentifierMetadata, identifierModuleUrl, identifierName} from '../compile_metadata';
|
||||||
import {CompileIdentifierMetadata} from '../compile_metadata';
|
|
||||||
import {isBlank, isPresent} from '../facade/lang';
|
import {isBlank, isPresent} from '../facade/lang';
|
||||||
|
|
||||||
import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
|
import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
|
||||||
import * as o from './output_ast';
|
import * as o from './output_ast';
|
||||||
import {ImportResolver} from './path_util';
|
import {ImportResolver} from './path_util';
|
||||||
|
|
||||||
const _debugFilePath = '/debug/lib';
|
const _debugModuleUrl = '/debug/lib';
|
||||||
|
|
||||||
export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.Type | any[]):
|
export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.Type | any[]):
|
||||||
string {
|
string {
|
||||||
const converter = new _TsEmitterVisitor(_debugFilePath, {
|
const converter = new _TsEmitterVisitor(_debugModuleUrl);
|
||||||
fileNameToModuleName(filePath: string, containingFilePath: string) { return filePath; },
|
|
||||||
getImportAs(symbol: StaticSymbol) { return null; }
|
|
||||||
});
|
|
||||||
const ctx = EmitterVisitorContext.createRoot([]);
|
const ctx = EmitterVisitorContext.createRoot([]);
|
||||||
const asts: any[] = Array.isArray(ast) ? ast : [ast];
|
const asts: any[] = Array.isArray(ast) ? ast : [ast];
|
||||||
|
|
||||||
@ -41,23 +37,17 @@ export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class TypeScriptEmitter implements OutputEmitter {
|
export class TypeScriptEmitter implements OutputEmitter {
|
||||||
constructor(private _importResolver: ImportResolver) {}
|
constructor(private _importGenerator: ImportResolver) {}
|
||||||
emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string {
|
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
|
||||||
const converter = new _TsEmitterVisitor(genFilePath, this._importResolver);
|
const converter = new _TsEmitterVisitor(moduleUrl);
|
||||||
const ctx = EmitterVisitorContext.createRoot(exportedVars);
|
const ctx = EmitterVisitorContext.createRoot(exportedVars);
|
||||||
converter.visitAllStatements(stmts, ctx);
|
converter.visitAllStatements(stmts, ctx);
|
||||||
const srcParts: string[] = [];
|
const srcParts: string[] = [];
|
||||||
converter.reexports.forEach((reexports, exportedFilePath) => {
|
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
|
||||||
const reexportsCode =
|
|
||||||
reexports.map(reexport => `${reexport.name} as ${reexport.as}`).join(',');
|
|
||||||
srcParts.push(
|
|
||||||
`export {${reexportsCode}} from '${this._importResolver.fileNameToModuleName(exportedFilePath, genFilePath)}';`);
|
|
||||||
});
|
|
||||||
converter.importsWithPrefixes.forEach((prefix, importedFilePath) => {
|
|
||||||
// Note: can't write the real word for import as it screws up system.js auto detection...
|
// Note: can't write the real word for import as it screws up system.js auto detection...
|
||||||
srcParts.push(
|
srcParts.push(
|
||||||
`imp` +
|
`imp` +
|
||||||
`ort * as ${prefix} from '${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}';`);
|
`ort * as ${prefix} from '${this._importGenerator.fileNameToModuleName(importedModuleUrl, moduleUrl)}';`);
|
||||||
});
|
});
|
||||||
srcParts.push(ctx.toSource());
|
srcParts.push(ctx.toSource());
|
||||||
return srcParts.join('\n');
|
return srcParts.join('\n');
|
||||||
@ -65,12 +55,9 @@ export class TypeScriptEmitter implements OutputEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor {
|
class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor {
|
||||||
constructor(private _genFilePath: string, private _importResolver: ImportResolver) {
|
constructor(private _moduleUrl: string) { super(false); }
|
||||||
super(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
importsWithPrefixes = new Map<string, string>();
|
importsWithPrefixes = new Map<string, string>();
|
||||||
reexports = new Map<string, {name: string, as: string}[]>();
|
|
||||||
|
|
||||||
visitType(t: o.Type, ctx: EmitterVisitorContext, defaultType: string = 'any') {
|
visitType(t: o.Type, ctx: EmitterVisitorContext, defaultType: string = 'any') {
|
||||||
if (isPresent(t)) {
|
if (isPresent(t)) {
|
||||||
@ -111,19 +98,6 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
|
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
|
||||||
if (ctx.isExportedVar(stmt.name) && stmt.value instanceof o.ExternalExpr && !stmt.type) {
|
|
||||||
// check for a reexport
|
|
||||||
const {name, filePath, members} = this._resolveStaticSymbol(stmt.value.value);
|
|
||||||
if (members.length === 0 && filePath !== this._genFilePath) {
|
|
||||||
let reexports = this.reexports.get(filePath);
|
|
||||||
if (!reexports) {
|
|
||||||
reexports = [];
|
|
||||||
this.reexports.set(filePath, reexports);
|
|
||||||
}
|
|
||||||
reexports.push({name, as: stmt.name});
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ctx.isExportedVar(stmt.name)) {
|
if (ctx.isExportedVar(stmt.name)) {
|
||||||
ctx.print(`export `);
|
ctx.print(`export `);
|
||||||
}
|
}
|
||||||
@ -346,29 +320,25 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
|||||||
}, params, ctx, ',');
|
}, params, ctx, ',');
|
||||||
}
|
}
|
||||||
|
|
||||||
private _resolveStaticSymbol(value: CompileIdentifierMetadata): StaticSymbol {
|
|
||||||
const reference = value.reference;
|
|
||||||
if (!(reference instanceof StaticSymbol)) {
|
|
||||||
throw new Error(`Internal error: unknown identifier ${JSON.stringify(value)}`);
|
|
||||||
}
|
|
||||||
return this._importResolver.getImportAs(reference) || reference;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _visitIdentifier(
|
private _visitIdentifier(
|
||||||
value: CompileIdentifierMetadata, typeParams: o.Type[], ctx: EmitterVisitorContext): void {
|
value: CompileIdentifierMetadata, typeParams: o.Type[], ctx: EmitterVisitorContext): void {
|
||||||
const {name, filePath, members} = this._resolveStaticSymbol(value);
|
const name = identifierName(value);
|
||||||
if (filePath != this._genFilePath) {
|
const moduleUrl = identifierModuleUrl(value);
|
||||||
let prefix = this.importsWithPrefixes.get(filePath);
|
if (isBlank(name)) {
|
||||||
|
throw new Error(`Internal error: unknown identifier ${value}`);
|
||||||
|
}
|
||||||
|
if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) {
|
||||||
|
let prefix = this.importsWithPrefixes.get(moduleUrl);
|
||||||
if (isBlank(prefix)) {
|
if (isBlank(prefix)) {
|
||||||
prefix = `import${this.importsWithPrefixes.size}`;
|
prefix = `import${this.importsWithPrefixes.size}`;
|
||||||
this.importsWithPrefixes.set(filePath, prefix);
|
this.importsWithPrefixes.set(moduleUrl, prefix);
|
||||||
}
|
}
|
||||||
ctx.print(`${prefix}.`);
|
ctx.print(`${prefix}.`);
|
||||||
}
|
}
|
||||||
if (members.length) {
|
if (value.reference && value.reference.members && value.reference.members.length) {
|
||||||
ctx.print(name);
|
ctx.print(value.reference.name);
|
||||||
ctx.print('.');
|
ctx.print('.');
|
||||||
ctx.print(members.join('.'));
|
ctx.print(value.reference.members.join('.'));
|
||||||
} else {
|
} else {
|
||||||
ctx.print(name);
|
ctx.print(name);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {CompileIdentifierMetadata} from '../compile_metadata';
|
|
||||||
import {ValueTransformer, visitValue} from '../util';
|
import {ValueTransformer, visitValue} from '../util';
|
||||||
|
|
||||||
import * as o from './output_ast';
|
import * as o from './output_ast';
|
||||||
|
@ -48,6 +48,51 @@ export class ParseLocation {
|
|||||||
}
|
}
|
||||||
return new ParseLocation(this.file, offset, line, col);
|
return new ParseLocation(this.file, offset, line, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the source around the location
|
||||||
|
// Up to `maxChars` or `maxLines` on each side of the location
|
||||||
|
getContext(maxChars: number, maxLines: number): {before: string, after: string} {
|
||||||
|
const content = this.file.content;
|
||||||
|
let startOffset = this.offset;
|
||||||
|
|
||||||
|
if (isPresent(startOffset)) {
|
||||||
|
if (startOffset > content.length - 1) {
|
||||||
|
startOffset = content.length - 1;
|
||||||
|
}
|
||||||
|
let endOffset = startOffset;
|
||||||
|
let ctxChars = 0;
|
||||||
|
let ctxLines = 0;
|
||||||
|
|
||||||
|
while (ctxChars < maxChars && startOffset > 0) {
|
||||||
|
startOffset--;
|
||||||
|
ctxChars++;
|
||||||
|
if (content[startOffset] == '\n') {
|
||||||
|
if (++ctxLines == maxLines) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxChars = 0;
|
||||||
|
ctxLines = 0;
|
||||||
|
while (ctxChars < maxChars && endOffset < content.length - 1) {
|
||||||
|
endOffset++;
|
||||||
|
ctxChars++;
|
||||||
|
if (content[endOffset] == '\n') {
|
||||||
|
if (++ctxLines == maxLines) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
before: content.substring(startOffset, this.offset),
|
||||||
|
after: content.substring(this.offset, endOffset + 1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ParseSourceFile {
|
export class ParseSourceFile {
|
||||||
@ -74,47 +119,9 @@ export class ParseError {
|
|||||||
public level: ParseErrorLevel = ParseErrorLevel.FATAL) {}
|
public level: ParseErrorLevel = ParseErrorLevel.FATAL) {}
|
||||||
|
|
||||||
toString(): string {
|
toString(): string {
|
||||||
const source = this.span.start.file.content;
|
const ctx = this.span.start.getContext(100, 3);
|
||||||
let ctxStart = this.span.start.offset;
|
const contextStr = ctx ? ` ("${ctx.before}[ERROR ->]${ctx.after}")` : '';
|
||||||
let contextStr = '';
|
const details = this.span.details ? `, ${this.span.details}` : '';
|
||||||
let details = '';
|
|
||||||
if (isPresent(ctxStart)) {
|
|
||||||
if (ctxStart > source.length - 1) {
|
|
||||||
ctxStart = source.length - 1;
|
|
||||||
}
|
|
||||||
let ctxEnd = ctxStart;
|
|
||||||
let ctxLen = 0;
|
|
||||||
let ctxLines = 0;
|
|
||||||
|
|
||||||
while (ctxLen < 100 && ctxStart > 0) {
|
|
||||||
ctxStart--;
|
|
||||||
ctxLen++;
|
|
||||||
if (source[ctxStart] == '\n') {
|
|
||||||
if (++ctxLines == 3) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctxLen = 0;
|
|
||||||
ctxLines = 0;
|
|
||||||
while (ctxLen < 100 && ctxEnd < source.length - 1) {
|
|
||||||
ctxEnd++;
|
|
||||||
ctxLen++;
|
|
||||||
if (source[ctxEnd] == '\n') {
|
|
||||||
if (++ctxLines == 3) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const context = source.substring(ctxStart, this.span.start.offset) + '[ERROR ->]' +
|
|
||||||
source.substring(this.span.start.offset, ctxEnd + 1);
|
|
||||||
contextStr = ` ("${context}")`;
|
|
||||||
}
|
|
||||||
if (this.span.details) {
|
|
||||||
details = `, ${this.span.details}`;
|
|
||||||
}
|
|
||||||
return `${this.msg}${contextStr}: ${this.span.start}${details}`;
|
return `${this.msg}${contextStr}: ${this.span.start}${details}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {Pipe, Type, resolveForwardRef} from '@angular/core';
|
import {Pipe, Type, resolveForwardRef} from '@angular/core';
|
||||||
|
|
||||||
import {ListWrapper} from './facade/collection';
|
import {ListWrapper} from './facade/collection';
|
||||||
import {isPresent, stringify} from './facade/lang';
|
import {stringify} from './facade/lang';
|
||||||
import {CompilerInjectable} from './injectable';
|
import {CompilerInjectable} from './injectable';
|
||||||
import {ReflectorReader, reflector} from './private_import_core';
|
import {ReflectorReader, reflector} from './private_import_core';
|
||||||
|
|
||||||
@ -38,9 +38,9 @@ export class PipeResolver {
|
|||||||
*/
|
*/
|
||||||
resolve(type: Type<any>, throwIfNotFound = true): Pipe {
|
resolve(type: Type<any>, throwIfNotFound = true): Pipe {
|
||||||
const metas = this._reflector.annotations(resolveForwardRef(type));
|
const metas = this._reflector.annotations(resolveForwardRef(type));
|
||||||
if (isPresent(metas)) {
|
if (metas) {
|
||||||
const annotation = ListWrapper.findLast(metas, _isPipeMetadata);
|
const annotation = ListWrapper.findLast(metas, _isPipeMetadata);
|
||||||
if (isPresent(annotation)) {
|
if (annotation) {
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ export const view_utils: typeof r.view_utils = r.view_utils;
|
|||||||
export const DebugContext: typeof r.DebugContext = r.DebugContext;
|
export const DebugContext: typeof r.DebugContext = r.DebugContext;
|
||||||
export const StaticNodeDebugInfo: typeof r.StaticNodeDebugInfo = r.StaticNodeDebugInfo;
|
export const StaticNodeDebugInfo: typeof r.StaticNodeDebugInfo = r.StaticNodeDebugInfo;
|
||||||
export const devModeEqual: typeof r.devModeEqual = r.devModeEqual;
|
export const devModeEqual: typeof r.devModeEqual = r.devModeEqual;
|
||||||
|
export const UNINITIALIZED: typeof r.UNINITIALIZED = r.UNINITIALIZED;
|
||||||
export const ValueUnwrapper: typeof r.ValueUnwrapper = r.ValueUnwrapper;
|
export const ValueUnwrapper: typeof r.ValueUnwrapper = r.ValueUnwrapper;
|
||||||
export const TemplateRef_: typeof r.TemplateRef_ = r.TemplateRef_;
|
export const TemplateRef_: typeof r.TemplateRef_ = r.TemplateRef_;
|
||||||
export type RenderDebugInfo = typeof r._RenderDebugInfo;
|
export type RenderDebugInfo = typeof r._RenderDebugInfo;
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata, tokenName, tokenReference} from './compile_metadata';
|
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata, tokenName, tokenReference} from './compile_metadata';
|
||||||
import {isBlank, isPresent} from './facade/lang';
|
import {isBlank, isPresent} from './facade/lang';
|
||||||
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
|
import {Identifiers, resolveIdentifier} from './identifiers';
|
||||||
import {ParseError, ParseSourceSpan} from './parse_util';
|
import {ParseError, ParseSourceSpan} from './parse_util';
|
||||||
import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst} from './template_parser/template_ast';
|
import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst} from './template_parser/template_ast';
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ export class ProviderElementContext {
|
|||||||
let queries: CompileQueryMetadata[];
|
let queries: CompileQueryMetadata[];
|
||||||
while (currentEl !== null) {
|
while (currentEl !== null) {
|
||||||
queries = currentEl._contentQueries.get(tokenReference(token));
|
queries = currentEl._contentQueries.get(tokenReference(token));
|
||||||
if (isPresent(queries)) {
|
if (queries) {
|
||||||
result.push(...queries.filter((query) => query.descendants || distance <= 1));
|
result.push(...queries.filter((query) => query.descendants || distance <= 1));
|
||||||
}
|
}
|
||||||
if (currentEl._directiveAsts.length > 0) {
|
if (currentEl._directiveAsts.length > 0) {
|
||||||
@ -123,7 +123,7 @@ export class ProviderElementContext {
|
|||||||
currentEl = currentEl._parent;
|
currentEl = currentEl._parent;
|
||||||
}
|
}
|
||||||
queries = this.viewContext.viewQueries.get(tokenReference(token));
|
queries = this.viewContext.viewQueries.get(tokenReference(token));
|
||||||
if (isPresent(queries)) {
|
if (queries) {
|
||||||
result.push(...queries);
|
result.push(...queries);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -143,7 +143,7 @@ export class ProviderElementContext {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
|
let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
|
||||||
if (isPresent(transformedProviderAst)) {
|
if (transformedProviderAst) {
|
||||||
return transformedProviderAst;
|
return transformedProviderAst;
|
||||||
}
|
}
|
||||||
if (isPresent(this._seenProviders.get(tokenReference(token)))) {
|
if (isPresent(this._seenProviders.get(tokenReference(token)))) {
|
||||||
@ -165,11 +165,11 @@ export class ProviderElementContext {
|
|||||||
transformedUseExisting = null;
|
transformedUseExisting = null;
|
||||||
transformedUseValue = existingDiDep.value;
|
transformedUseValue = existingDiDep.value;
|
||||||
}
|
}
|
||||||
} else if (isPresent(provider.useFactory)) {
|
} else if (provider.useFactory) {
|
||||||
const deps = provider.deps || provider.useFactory.diDeps;
|
const deps = provider.deps || provider.useFactory.diDeps;
|
||||||
transformedDeps =
|
transformedDeps =
|
||||||
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
||||||
} else if (isPresent(provider.useClass)) {
|
} else if (provider.useClass) {
|
||||||
const deps = provider.deps || provider.useClass.diDeps;
|
const deps = provider.deps || provider.useClass.diDeps;
|
||||||
transformedDeps =
|
transformedDeps =
|
||||||
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
||||||
@ -235,7 +235,7 @@ export class ProviderElementContext {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// check parent elements
|
// check parent elements
|
||||||
while (!result && isPresent(currElement._parent)) {
|
while (!result && currElement._parent) {
|
||||||
const prevElement = currElement;
|
const prevElement = currElement;
|
||||||
currElement = currElement._parent;
|
currElement = currElement._parent;
|
||||||
if (prevElement._isViewRoot) {
|
if (prevElement._isViewRoot) {
|
||||||
@ -301,7 +301,7 @@ export class NgModuleProviderAnalyzer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
|
let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
|
||||||
if (isPresent(transformedProviderAst)) {
|
if (transformedProviderAst) {
|
||||||
return transformedProviderAst;
|
return transformedProviderAst;
|
||||||
}
|
}
|
||||||
if (isPresent(this._seenProviders.get(tokenReference(token)))) {
|
if (isPresent(this._seenProviders.get(tokenReference(token)))) {
|
||||||
@ -324,11 +324,11 @@ export class NgModuleProviderAnalyzer {
|
|||||||
transformedUseExisting = null;
|
transformedUseExisting = null;
|
||||||
transformedUseValue = existingDiDep.value;
|
transformedUseValue = existingDiDep.value;
|
||||||
}
|
}
|
||||||
} else if (isPresent(provider.useFactory)) {
|
} else if (provider.useFactory) {
|
||||||
const deps = provider.deps || provider.useFactory.diDeps;
|
const deps = provider.deps || provider.useFactory.diDeps;
|
||||||
transformedDeps =
|
transformedDeps =
|
||||||
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
|
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
|
||||||
} else if (isPresent(provider.useClass)) {
|
} else if (provider.useClass) {
|
||||||
const deps = provider.deps || provider.useClass.diDeps;
|
const deps = provider.deps || provider.useClass.diDeps;
|
||||||
transformedDeps =
|
transformedDeps =
|
||||||
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
|
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
|
||||||
@ -454,7 +454,7 @@ function _resolveProviders(
|
|||||||
|
|
||||||
function _getViewQueries(component: CompileDirectiveMetadata): Map<any, CompileQueryMetadata[]> {
|
function _getViewQueries(component: CompileDirectiveMetadata): Map<any, CompileQueryMetadata[]> {
|
||||||
const viewQueries = new Map<any, CompileQueryMetadata[]>();
|
const viewQueries = new Map<any, CompileQueryMetadata[]>();
|
||||||
if (isPresent(component.viewQueries)) {
|
if (component.viewQueries) {
|
||||||
component.viewQueries.forEach((query) => _addQueryToTokenMap(viewQueries, query));
|
component.viewQueries.forEach((query) => _addQueryToTokenMap(viewQueries, query));
|
||||||
}
|
}
|
||||||
return viewQueries;
|
return viewQueries;
|
||||||
@ -464,7 +464,7 @@ function _getContentQueries(directives: CompileDirectiveSummary[]):
|
|||||||
Map<any, CompileQueryMetadata[]> {
|
Map<any, CompileQueryMetadata[]> {
|
||||||
const contentQueries = new Map<any, CompileQueryMetadata[]>();
|
const contentQueries = new Map<any, CompileQueryMetadata[]>();
|
||||||
directives.forEach(directive => {
|
directives.forEach(directive => {
|
||||||
if (isPresent(directive.queries)) {
|
if (directive.queries) {
|
||||||
directive.queries.forEach((query) => _addQueryToTokenMap(contentQueries, query));
|
directive.queries.forEach((query) => _addQueryToTokenMap(contentQueries, query));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -9,10 +9,11 @@
|
|||||||
import {getHtmlTagDefinition} from './ml_parser/html_tags';
|
import {getHtmlTagDefinition} from './ml_parser/html_tags';
|
||||||
|
|
||||||
const _SELECTOR_REGEXP = new RegExp(
|
const _SELECTOR_REGEXP = new RegExp(
|
||||||
'(\\:not\\()|' + //":not("
|
'(\\:not\\()|' + //":not("
|
||||||
'([-\\w]+)|' + // "tag"
|
'([-\\w]+)|' + // "tag"
|
||||||
'(?:\\.([-\\w]+))|' + // ".class"
|
'(?:\\.([-\\w]+))|' + // ".class"
|
||||||
'(?:\\[([.-\\w*]+)(?:=([^\\]]*))?\\])|' + // "[name]", "[name=value]"
|
// "-" should appear first in the regexp below as FF31 parses "[.-\w]" as a range
|
||||||
|
'(?:\\[([-.\\w*]+)(?:=([^\\]]*))?\\])|' + // "[name]", "[name=value]"
|
||||||
'(\\))|' + // ")"
|
'(\\))|' + // ")"
|
||||||
'(\\s*,\\s*)', // ","
|
'(\\s*,\\s*)', // ","
|
||||||
'g');
|
'g');
|
||||||
|
@ -16,9 +16,6 @@ export interface Summary<T> {
|
|||||||
|
|
||||||
@CompilerInjectable()
|
@CompilerInjectable()
|
||||||
export class SummaryResolver<T> {
|
export class SummaryResolver<T> {
|
||||||
isLibraryFile(fileName: string): boolean { return false; };
|
|
||||||
getLibraryFileName(fileName: string): string { return null; }
|
|
||||||
resolveSummary(reference: T): Summary<T> { return null; };
|
resolveSummary(reference: T): Summary<T> { return null; };
|
||||||
getSymbolsOf(filePath: string): T[] { return []; }
|
getSymbolsOf(filePath: string): T[] { return []; }
|
||||||
getImportAs(reference: T): T { return reference; }
|
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import {SecurityContext} from '@angular/core';
|
|||||||
import {CompileDirectiveSummary, CompilePipeSummary} from '../compile_metadata';
|
import {CompileDirectiveSummary, CompilePipeSummary} from '../compile_metadata';
|
||||||
import {ASTWithSource, BindingPipe, EmptyExpr, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast';
|
import {ASTWithSource, BindingPipe, EmptyExpr, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast';
|
||||||
import {Parser} from '../expression_parser/parser';
|
import {Parser} from '../expression_parser/parser';
|
||||||
import {isPresent} from '../facade/lang';
|
|
||||||
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||||
import {mergeNsAndName} from '../ml_parser/tags';
|
import {mergeNsAndName} from '../ml_parser/tags';
|
||||||
import {ParseError, ParseErrorLevel, ParseSourceSpan} from '../parse_util';
|
import {ParseError, ParseErrorLevel, ParseSourceSpan} from '../parse_util';
|
||||||
@ -111,14 +110,14 @@ export class BindingParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseInlineTemplateBinding(
|
parseInlineTemplateBinding(
|
||||||
name: string, prefixToken: string, value: string, sourceSpan: ParseSourceSpan,
|
prefixToken: string, value: string, sourceSpan: ParseSourceSpan,
|
||||||
targetMatchableAttrs: string[][], targetProps: BoundProperty[], targetVars: VariableAst[]) {
|
targetMatchableAttrs: string[][], targetProps: BoundProperty[], targetVars: VariableAst[]) {
|
||||||
const bindings = this._parseTemplateBindings(prefixToken, value, sourceSpan);
|
const bindings = this._parseTemplateBindings(prefixToken, value, sourceSpan);
|
||||||
for (let i = 0; i < bindings.length; i++) {
|
for (let i = 0; i < bindings.length; i++) {
|
||||||
const binding = bindings[i];
|
const binding = bindings[i];
|
||||||
if (binding.keyIsVar) {
|
if (binding.keyIsVar) {
|
||||||
targetVars.push(new VariableAst(binding.key, binding.name, sourceSpan));
|
targetVars.push(new VariableAst(binding.key, binding.name, sourceSpan));
|
||||||
} else if (isPresent(binding.expression)) {
|
} else if (binding.expression) {
|
||||||
this._parsePropertyAst(
|
this._parsePropertyAst(
|
||||||
binding.key, binding.expression, sourceSpan, targetMatchableAttrs, targetProps);
|
binding.key, binding.expression, sourceSpan, targetMatchableAttrs, targetProps);
|
||||||
} else {
|
} else {
|
||||||
@ -136,7 +135,7 @@ export class BindingParser {
|
|||||||
const bindingsResult = this._exprParser.parseTemplateBindings(prefixToken, value, sourceInfo);
|
const bindingsResult = this._exprParser.parseTemplateBindings(prefixToken, value, sourceInfo);
|
||||||
this._reportExpressionParserErrors(bindingsResult.errors, sourceSpan);
|
this._reportExpressionParserErrors(bindingsResult.errors, sourceSpan);
|
||||||
bindingsResult.templateBindings.forEach((binding) => {
|
bindingsResult.templateBindings.forEach((binding) => {
|
||||||
if (isPresent(binding.expression)) {
|
if (binding.expression) {
|
||||||
this._checkPipes(binding.expression, sourceSpan);
|
this._checkPipes(binding.expression, sourceSpan);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -193,7 +192,7 @@ export class BindingParser {
|
|||||||
name: string, value: string, sourceSpan: ParseSourceSpan, targetMatchableAttrs: string[][],
|
name: string, value: string, sourceSpan: ParseSourceSpan, targetMatchableAttrs: string[][],
|
||||||
targetProps: BoundProperty[]): boolean {
|
targetProps: BoundProperty[]): boolean {
|
||||||
const expr = this.parseInterpolation(value, sourceSpan);
|
const expr = this.parseInterpolation(value, sourceSpan);
|
||||||
if (isPresent(expr)) {
|
if (expr) {
|
||||||
this._parsePropertyAst(name, expr, sourceSpan, targetMatchableAttrs, targetProps);
|
this._parsePropertyAst(name, expr, sourceSpan, targetMatchableAttrs, targetProps);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -374,7 +373,7 @@ export class BindingParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _checkPipes(ast: ASTWithSource, sourceSpan: ParseSourceSpan) {
|
private _checkPipes(ast: ASTWithSource, sourceSpan: ParseSourceSpan) {
|
||||||
if (isPresent(ast)) {
|
if (ast) {
|
||||||
const collector = new PipeCollector();
|
const collector = new PipeCollector();
|
||||||
ast.visit(collector);
|
ast.visit(collector);
|
||||||
collector.pipes.forEach((ast, pipeName) => {
|
collector.pipes.forEach((ast, pipeName) => {
|
||||||
|
@ -276,7 +276,7 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
templateBindingsSource = attr.value;
|
templateBindingsSource = attr.value;
|
||||||
} else if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX)) {
|
} else if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX)) {
|
||||||
templateBindingsSource = attr.value;
|
templateBindingsSource = attr.value;
|
||||||
prefixToken = normalizedName.substring(TEMPLATE_ATTR_PREFIX.length);
|
prefixToken = normalizedName.substring(TEMPLATE_ATTR_PREFIX.length) + ':';
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasTemplateBinding = isPresent(templateBindingsSource);
|
const hasTemplateBinding = isPresent(templateBindingsSource);
|
||||||
@ -288,7 +288,7 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
}
|
}
|
||||||
hasInlineTemplates = true;
|
hasInlineTemplates = true;
|
||||||
this._bindingParser.parseInlineTemplateBinding(
|
this._bindingParser.parseInlineTemplateBinding(
|
||||||
attr.name, prefixToken, templateBindingsSource, attr.sourceSpan, templateMatchableAttrs,
|
prefixToken, templateBindingsSource, attr.sourceSpan, templateMatchableAttrs,
|
||||||
templateElementOrDirectiveProps, templateElementVars);
|
templateElementOrDirectiveProps, templateElementVars);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,9 +309,11 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
const elementProps: BoundElementPropertyAst[] =
|
const elementProps: BoundElementPropertyAst[] =
|
||||||
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts);
|
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts);
|
||||||
const isViewRoot = parent.isTemplateElement || hasInlineTemplates;
|
const isViewRoot = parent.isTemplateElement || hasInlineTemplates;
|
||||||
|
|
||||||
const providerContext = new ProviderElementContext(
|
const providerContext = new ProviderElementContext(
|
||||||
this.providerViewContext, parent.providerContext, isViewRoot, directiveAsts, attrs,
|
this.providerViewContext, parent.providerContext, isViewRoot, directiveAsts, attrs,
|
||||||
references, element.sourceSpan);
|
references, element.sourceSpan);
|
||||||
|
|
||||||
const children = html.visitAll(
|
const children = html.visitAll(
|
||||||
preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children,
|
preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children,
|
||||||
ElementContext.create(
|
ElementContext.create(
|
||||||
|
@ -19,7 +19,7 @@ import {ProviderAst, ProviderAstType, ReferenceAst, TemplateAst} from '../templa
|
|||||||
import {CompileMethod} from './compile_method';
|
import {CompileMethod} from './compile_method';
|
||||||
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
|
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
|
||||||
import {CompileView, CompileViewRootNode} from './compile_view';
|
import {CompileView, CompileViewRootNode} from './compile_view';
|
||||||
import {InjectMethodVars, ViewProperties} from './constants';
|
import {InjectMethodVars} from './constants';
|
||||||
import {ComponentFactoryDependency, DirectiveWrapperDependency} from './deps';
|
import {ComponentFactoryDependency, DirectiveWrapperDependency} from './deps';
|
||||||
import {getPropertyInView, injectFromViewParentInjector} from './util';
|
import {getPropertyInView, injectFromViewParentInjector} from './util';
|
||||||
|
|
||||||
@ -97,11 +97,12 @@ export class CompileElement extends CompileNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _createComponentFactoryResolver() {
|
private _createComponentFactoryResolver() {
|
||||||
const entryComponents = this.component.entryComponents.map((entryComponent) => {
|
const entryComponents =
|
||||||
this.view.targetDependencies.push(
|
this.component.entryComponents.map((entryComponent: CompileIdentifierMetadata) => {
|
||||||
new ComponentFactoryDependency(entryComponent.componentType));
|
const id: CompileIdentifierMetadata = {reference: null};
|
||||||
return {reference: entryComponent.componentFactory};
|
this.view.targetDependencies.push(new ComponentFactoryDependency(entryComponent, id));
|
||||||
});
|
return id;
|
||||||
|
});
|
||||||
if (!entryComponents || entryComponents.length === 0) {
|
if (!entryComponents || entryComponents.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -178,11 +179,11 @@ export class CompileElement extends CompileNode {
|
|||||||
const depsExpr =
|
const depsExpr =
|
||||||
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
||||||
if (isDirectiveWrapper) {
|
if (isDirectiveWrapper) {
|
||||||
const dirMeta =
|
const directiveWrapperIdentifier: CompileIdentifierMetadata = {reference: null};
|
||||||
this._directives.find(dir => dir.type.reference === provider.useClass.reference);
|
this.view.targetDependencies.push(new DirectiveWrapperDependency(
|
||||||
this.view.targetDependencies.push(
|
provider.useClass, DirectiveWrapperCompiler.dirWrapperClassName(provider.useClass),
|
||||||
new DirectiveWrapperDependency(dirMeta.type.reference));
|
directiveWrapperIdentifier));
|
||||||
return DirectiveWrapperExpressions.create({reference: dirMeta.wrapperType}, depsExpr);
|
return DirectiveWrapperExpressions.create(directiveWrapperIdentifier, depsExpr);
|
||||||
} else {
|
} else {
|
||||||
return o.importExpr(provider.useClass)
|
return o.importExpr(provider.useClass)
|
||||||
.instantiate(depsExpr, o.importType(provider.useClass));
|
.instantiate(depsExpr, o.importType(provider.useClass));
|
||||||
@ -194,7 +195,7 @@ export class CompileElement extends CompileNode {
|
|||||||
const propName =
|
const propName =
|
||||||
`_${tokenName(resolvedProvider.token)}_${this.nodeIndex}_${this.instances.size}`;
|
`_${tokenName(resolvedProvider.token)}_${this.nodeIndex}_${this.instances.size}`;
|
||||||
const instance = createProviderProperty(
|
const instance = createProviderProperty(
|
||||||
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
|
propName, providerValueExpressions, resolvedProvider.multiProvider,
|
||||||
resolvedProvider.eager, this);
|
resolvedProvider.eager, this);
|
||||||
if (isDirectiveWrapper) {
|
if (isDirectiveWrapper) {
|
||||||
this.directiveWrapperInstance.set(tokenReference(resolvedProvider.token), instance);
|
this.directiveWrapperInstance.set(tokenReference(resolvedProvider.token), instance);
|
||||||
@ -288,7 +289,7 @@ export class CompileElement extends CompileNode {
|
|||||||
CompileQuery {
|
CompileQuery {
|
||||||
const propName =
|
const propName =
|
||||||
`_query_${tokenName(queryMeta.selectors[0])}_${this.nodeIndex}_${this._queryCount++}`;
|
`_query_${tokenName(queryMeta.selectors[0])}_${this.nodeIndex}_${this._queryCount++}`;
|
||||||
const queryList = createQueryList(queryMeta, directiveInstance, propName, this.view);
|
const queryList = createQueryList(propName, this.view);
|
||||||
const query = new CompileQuery(queryMeta, queryList, directiveInstance, this.view);
|
const query = new CompileQuery(queryMeta, queryList, directiveInstance, this.view);
|
||||||
addQueryToTokenMap(this._queries, query);
|
addQueryToTokenMap(this._queries, query);
|
||||||
return query;
|
return query;
|
||||||
@ -368,8 +369,8 @@ function createInjectInternalCondition(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createProviderProperty(
|
function createProviderProperty(
|
||||||
propName: string, provider: ProviderAst, providerValueExpressions: o.Expression[],
|
propName: string, providerValueExpressions: o.Expression[], isMulti: boolean, isEager: boolean,
|
||||||
isMulti: boolean, isEager: boolean, compileElement: CompileElement): o.Expression {
|
compileElement: CompileElement): o.Expression {
|
||||||
const view = compileElement.view;
|
const view = compileElement.view;
|
||||||
let resolvedProviderValueExpr: o.Expression;
|
let resolvedProviderValueExpr: o.Expression;
|
||||||
let type: o.Type;
|
let type: o.Type;
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {isPresent} from '../facade/lang';
|
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {TemplateAst} from '../template_parser/template_ast';
|
import {TemplateAst} from '../template_parser/template_ast';
|
||||||
|
|
||||||
@ -34,7 +33,7 @@ export class CompileMethod {
|
|||||||
if (this._newState.nodeIndex !== this._currState.nodeIndex ||
|
if (this._newState.nodeIndex !== this._currState.nodeIndex ||
|
||||||
this._newState.sourceAst !== this._currState.sourceAst) {
|
this._newState.sourceAst !== this._currState.sourceAst) {
|
||||||
const expr = this._updateDebugContext(this._newState);
|
const expr = this._updateDebugContext(this._newState);
|
||||||
if (isPresent(expr)) {
|
if (expr) {
|
||||||
this._bodyStatements.push(expr.toStmt());
|
this._bodyStatements.push(expr.toStmt());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,13 +42,12 @@ export class CompileMethod {
|
|||||||
private _updateDebugContext(newState: _DebugState): o.Expression {
|
private _updateDebugContext(newState: _DebugState): o.Expression {
|
||||||
this._currState = this._newState = newState;
|
this._currState = this._newState = newState;
|
||||||
if (this._debugEnabled) {
|
if (this._debugEnabled) {
|
||||||
const sourceLocation =
|
const sourceLocation = newState.sourceAst ? newState.sourceAst.sourceSpan.start : null;
|
||||||
isPresent(newState.sourceAst) ? newState.sourceAst.sourceSpan.start : null;
|
|
||||||
|
|
||||||
return o.THIS_EXPR.callMethod('debug', [
|
return o.THIS_EXPR.callMethod('debug', [
|
||||||
o.literal(newState.nodeIndex),
|
o.literal(newState.nodeIndex),
|
||||||
isPresent(sourceLocation) ? o.literal(sourceLocation.line) : o.NULL_EXPR,
|
sourceLocation ? o.literal(sourceLocation.line) : o.NULL_EXPR,
|
||||||
isPresent(sourceLocation) ? o.literal(sourceLocation.col) : o.NULL_EXPR
|
sourceLocation ? o.literal(sourceLocation.col) : o.NULL_EXPR
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
import {CompileQueryMetadata, tokenReference} from '../compile_metadata';
|
import {CompileQueryMetadata, tokenReference} from '../compile_metadata';
|
||||||
import {ListWrapper} from '../facade/collection';
|
import {ListWrapper} from '../facade/collection';
|
||||||
import {isPresent} from '../facade/lang';
|
|
||||||
import {Identifiers, createIdentifier} from '../identifiers';
|
import {Identifiers, createIdentifier} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
|
|
||||||
@ -33,7 +32,7 @@ export class CompileQuery {
|
|||||||
addValue(value: o.Expression, view: CompileView) {
|
addValue(value: o.Expression, view: CompileView) {
|
||||||
let currentView = view;
|
let currentView = view;
|
||||||
const elPath: CompileElement[] = [];
|
const elPath: CompileElement[] = [];
|
||||||
while (isPresent(currentView) && currentView !== this.view) {
|
while (currentView && currentView !== this.view) {
|
||||||
const parentEl = currentView.declarationElement;
|
const parentEl = currentView.declarationElement;
|
||||||
elPath.unshift(parentEl);
|
elPath.unshift(parentEl);
|
||||||
currentView = parentEl.view;
|
currentView = parentEl.view;
|
||||||
@ -67,7 +66,7 @@ export class CompileQuery {
|
|||||||
generateStatements(targetStaticMethod: CompileMethod, targetDynamicMethod: CompileMethod) {
|
generateStatements(targetStaticMethod: CompileMethod, targetDynamicMethod: CompileMethod) {
|
||||||
const values = createQueryValues(this._values);
|
const values = createQueryValues(this._values);
|
||||||
const updateStmts = [this.queryList.callMethod('reset', [o.literalArr(values)]).toStmt()];
|
const updateStmts = [this.queryList.callMethod('reset', [o.literalArr(values)]).toStmt()];
|
||||||
if (isPresent(this.ownerDirectiveExpression)) {
|
if (this.ownerDirectiveExpression) {
|
||||||
const valueExpr = this.meta.first ? this.queryList.prop('first') : this.queryList;
|
const valueExpr = this.meta.first ? this.queryList.prop('first') : this.queryList;
|
||||||
updateStmts.push(
|
updateStmts.push(
|
||||||
this.ownerDirectiveExpression.prop(this.meta.propertyName).set(valueExpr).toStmt());
|
this.ownerDirectiveExpression.prop(this.meta.propertyName).set(valueExpr).toStmt());
|
||||||
@ -110,9 +109,7 @@ function mapNestedViews(
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createQueryList(
|
export function createQueryList(propertyName: string, compileView: CompileView): o.Expression {
|
||||||
query: CompileQueryMetadata, directiveInstance: o.Expression, propertyName: string,
|
|
||||||
compileView: CompileView): o.Expression {
|
|
||||||
compileView.fields.push(new o.ClassField(
|
compileView.fields.push(new o.ClassField(
|
||||||
propertyName, o.importType(createIdentifier(Identifiers.QueryList), [o.DYNAMIC_TYPE])));
|
propertyName, o.importType(createIdentifier(Identifiers.QueryList), [o.DYNAMIC_TYPE])));
|
||||||
const expr = o.THIS_EXPR.prop(propertyName);
|
const expr = o.THIS_EXPR.prop(propertyName);
|
||||||
|
@ -7,11 +7,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
|
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeSummary, tokenName, viewClassName} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeSummary, tokenName} from '../compile_metadata';
|
||||||
import {EventHandlerVars, NameResolver} from '../compiler_util/expression_converter';
|
import {EventHandlerVars, NameResolver} from '../compiler_util/expression_converter';
|
||||||
|
import {createPureProxy} from '../compiler_util/identifier_util';
|
||||||
import {CompilerConfig} from '../config';
|
import {CompilerConfig} from '../config';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {Identifiers, createIdentifier} from '../identifiers';
|
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {ViewType} from '../private_import_core';
|
import {ViewType} from '../private_import_core';
|
||||||
|
|
||||||
@ -19,8 +19,8 @@ import {CompileElement, CompileNode} from './compile_element';
|
|||||||
import {CompileMethod} from './compile_method';
|
import {CompileMethod} from './compile_method';
|
||||||
import {CompilePipe} from './compile_pipe';
|
import {CompilePipe} from './compile_pipe';
|
||||||
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
|
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
|
||||||
import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency} from './deps';
|
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency} from './deps';
|
||||||
import {getPropertyInView} from './util';
|
import {getPropertyInView, getViewClassName} from './util';
|
||||||
|
|
||||||
export enum CompileViewRootNodeType {
|
export enum CompileViewRootNodeType {
|
||||||
Node,
|
Node,
|
||||||
@ -86,7 +86,7 @@ export class CompileView implements NameResolver {
|
|||||||
public animations: AnimationEntryCompileResult[], public viewIndex: number,
|
public animations: AnimationEntryCompileResult[], public viewIndex: number,
|
||||||
public declarationElement: CompileElement, public templateVariableBindings: string[][],
|
public declarationElement: CompileElement, public templateVariableBindings: string[][],
|
||||||
public targetDependencies:
|
public targetDependencies:
|
||||||
Array<ComponentViewDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {
|
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {
|
||||||
this.createMethod = new CompileMethod(this);
|
this.createMethod = new CompileMethod(this);
|
||||||
this.animationBindingsMethod = new CompileMethod(this);
|
this.animationBindingsMethod = new CompileMethod(this);
|
||||||
this.injectorGetMethod = new CompileMethod(this);
|
this.injectorGetMethod = new CompileMethod(this);
|
||||||
@ -102,7 +102,7 @@ export class CompileView implements NameResolver {
|
|||||||
this.detachMethod = new CompileMethod(this);
|
this.detachMethod = new CompileMethod(this);
|
||||||
|
|
||||||
this.viewType = getViewType(component, viewIndex);
|
this.viewType = getViewType(component, viewIndex);
|
||||||
this.className = viewClassName(component.type.reference, viewIndex);
|
this.className = getViewClassName(component, viewIndex);
|
||||||
this.classType = o.expressionType(o.variable(this.className));
|
this.classType = o.expressionType(o.variable(this.className));
|
||||||
this.classExpr = o.variable(this.className);
|
this.classExpr = o.variable(this.className);
|
||||||
if (this.viewType === ViewType.COMPONENT || this.viewType === ViewType.HOST) {
|
if (this.viewType === ViewType.COMPONENT || this.viewType === ViewType.HOST) {
|
||||||
@ -118,7 +118,7 @@ export class CompileView implements NameResolver {
|
|||||||
const directiveInstance = o.THIS_EXPR.prop('context');
|
const directiveInstance = o.THIS_EXPR.prop('context');
|
||||||
this.component.viewQueries.forEach((queryMeta, queryIndex) => {
|
this.component.viewQueries.forEach((queryMeta, queryIndex) => {
|
||||||
const propName = `_viewQuery_${tokenName(queryMeta.selectors[0])}_${queryIndex}`;
|
const propName = `_viewQuery_${tokenName(queryMeta.selectors[0])}_${queryIndex}`;
|
||||||
const queryList = createQueryList(queryMeta, directiveInstance, propName, this);
|
const queryList = createQueryList(propName, this);
|
||||||
const query = new CompileQuery(queryMeta, queryList, directiveInstance, this);
|
const query = new CompileQuery(queryMeta, queryList, directiveInstance, this);
|
||||||
addQueryToTokenMap(viewQueries, query);
|
addQueryToTokenMap(viewQueries, query);
|
||||||
});
|
});
|
||||||
|
@ -11,7 +11,7 @@ import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
|
|||||||
import {createEnumExpression} from '../compiler_util/identifier_util';
|
import {createEnumExpression} from '../compiler_util/identifier_util';
|
||||||
import {Identifiers} from '../identifiers';
|
import {Identifiers} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {ChangeDetectorStatus, ViewType} from '../private_import_core';
|
import {ViewType} from '../private_import_core';
|
||||||
|
|
||||||
export class ViewTypeEnum {
|
export class ViewTypeEnum {
|
||||||
static fromValue(value: ViewType): o.Expression {
|
static fromValue(value: ViewType): o.Expression {
|
||||||
@ -25,12 +25,6 @@ export class ViewEncapsulationEnum {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChangeDetectionStrategyEnum {
|
|
||||||
static fromValue(value: ChangeDetectionStrategy): o.Expression {
|
|
||||||
return createEnumExpression(Identifiers.ChangeDetectionStrategy, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ChangeDetectorStatusEnum {
|
export class ChangeDetectorStatusEnum {
|
||||||
static fromValue(value: ChangeDetectorStatusEnum): o.Expression {
|
static fromValue(value: ChangeDetectorStatusEnum): o.Expression {
|
||||||
return createEnumExpression(Identifiers.ChangeDetectorStatus, value);
|
return createEnumExpression(Identifiers.ChangeDetectorStatus, value);
|
||||||
@ -47,7 +41,6 @@ export class ViewConstructorVars {
|
|||||||
export class ViewProperties {
|
export class ViewProperties {
|
||||||
static renderer = o.THIS_EXPR.prop('renderer');
|
static renderer = o.THIS_EXPR.prop('renderer');
|
||||||
static viewUtils = o.THIS_EXPR.prop('viewUtils');
|
static viewUtils = o.THIS_EXPR.prop('viewUtils');
|
||||||
static throwOnChange = o.THIS_EXPR.prop('throwOnChange');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InjectMethodVars {
|
export class InjectMethodVars {
|
||||||
@ -55,3 +48,9 @@ export class InjectMethodVars {
|
|||||||
static requestNodeIndex = o.variable('requestNodeIndex');
|
static requestNodeIndex = o.variable('requestNodeIndex');
|
||||||
static notFoundResult = o.variable('notFoundResult');
|
static notFoundResult = o.variable('notFoundResult');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class DetectChangesVars {
|
||||||
|
static throwOnChange = o.variable(`throwOnChange`);
|
||||||
|
static changes = o.variable(`changes`);
|
||||||
|
static changed = o.variable(`changed`);
|
||||||
|
}
|
||||||
|
@ -8,26 +8,19 @@
|
|||||||
|
|
||||||
import {CompileIdentifierMetadata} from '../compile_metadata';
|
import {CompileIdentifierMetadata} from '../compile_metadata';
|
||||||
|
|
||||||
/**
|
export class ViewClassDependency {
|
||||||
* This is currently not read, but will probably be used in the future.
|
constructor(
|
||||||
* We keep it as we already pass it through all the rigth places...
|
public comp: CompileIdentifierMetadata, public name: string,
|
||||||
*/
|
public placeholder: CompileIdentifierMetadata) {}
|
||||||
export class ComponentViewDependency {
|
|
||||||
constructor(public compType: any) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This is currently not read, but will probably be used in the future.
|
|
||||||
* We keep it as we already pass it through all the rigth places...
|
|
||||||
*/
|
|
||||||
export class ComponentFactoryDependency {
|
export class ComponentFactoryDependency {
|
||||||
constructor(public compType: any) {}
|
constructor(
|
||||||
|
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This is currently not read, but will probably be used in the future.
|
|
||||||
* We keep it as we already pass it through all the rigth places...
|
|
||||||
*/
|
|
||||||
export class DirectiveWrapperDependency {
|
export class DirectiveWrapperDependency {
|
||||||
constructor(public dirType: any) {}
|
constructor(
|
||||||
|
public dir: CompileIdentifierMetadata, public name: string,
|
||||||
|
public placeholder: CompileIdentifierMetadata) {}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
|
|||||||
|
|
||||||
import {CompileElement} from './compile_element';
|
import {CompileElement} from './compile_element';
|
||||||
import {CompileMethod} from './compile_method';
|
import {CompileMethod} from './compile_method';
|
||||||
import {ViewProperties} from './constants';
|
|
||||||
import {getHandleEventMethodName} from './util';
|
import {getHandleEventMethodName} from './util';
|
||||||
|
|
||||||
export function bindOutputs(
|
export function bindOutputs(
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompileDirectiveSummary, CompilePipeSummary} from '../compile_metadata';
|
import {CompileDirectiveSummary, CompilePipeSummary} from '../compile_metadata';
|
||||||
import {isFirstViewCheck} from '../compiler_util/binding_util';
|
|
||||||
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {LifecycleHooks} from '../private_import_core';
|
import {LifecycleHooks} from '../private_import_core';
|
||||||
@ -15,6 +14,10 @@ import {DirectiveAst, ProviderAst, ProviderAstType} from '../template_parser/tem
|
|||||||
|
|
||||||
import {CompileElement} from './compile_element';
|
import {CompileElement} from './compile_element';
|
||||||
import {CompileView} from './compile_view';
|
import {CompileView} from './compile_view';
|
||||||
|
import {DetectChangesVars} from './constants';
|
||||||
|
|
||||||
|
const STATE_IS_NEVER_CHECKED = o.THIS_EXPR.prop('numberOfChecks').identical(new o.LiteralExpr(0));
|
||||||
|
const NOT_THROW_ON_CHANGES = o.not(DetectChangesVars.throwOnChange);
|
||||||
|
|
||||||
export function bindDirectiveAfterContentLifecycleCallbacks(
|
export function bindDirectiveAfterContentLifecycleCallbacks(
|
||||||
directiveMeta: CompileDirectiveSummary, directiveInstance: o.Expression,
|
directiveMeta: CompileDirectiveSummary, directiveInstance: o.Expression,
|
||||||
@ -26,8 +29,7 @@ export function bindDirectiveAfterContentLifecycleCallbacks(
|
|||||||
compileElement.nodeIndex, compileElement.sourceAst);
|
compileElement.nodeIndex, compileElement.sourceAst);
|
||||||
if (lifecycleHooks.indexOf(LifecycleHooks.AfterContentInit) !== -1) {
|
if (lifecycleHooks.indexOf(LifecycleHooks.AfterContentInit) !== -1) {
|
||||||
afterContentLifecycleCallbacksMethod.addStmt(new o.IfStmt(
|
afterContentLifecycleCallbacksMethod.addStmt(new o.IfStmt(
|
||||||
isFirstViewCheck(o.THIS_EXPR),
|
STATE_IS_NEVER_CHECKED, [directiveInstance.callMethod('ngAfterContentInit', []).toStmt()]));
|
||||||
[directiveInstance.callMethod('ngAfterContentInit', []).toStmt()]));
|
|
||||||
}
|
}
|
||||||
if (lifecycleHooks.indexOf(LifecycleHooks.AfterContentChecked) !== -1) {
|
if (lifecycleHooks.indexOf(LifecycleHooks.AfterContentChecked) !== -1) {
|
||||||
afterContentLifecycleCallbacksMethod.addStmt(
|
afterContentLifecycleCallbacksMethod.addStmt(
|
||||||
@ -45,8 +47,7 @@ export function bindDirectiveAfterViewLifecycleCallbacks(
|
|||||||
compileElement.nodeIndex, compileElement.sourceAst);
|
compileElement.nodeIndex, compileElement.sourceAst);
|
||||||
if (lifecycleHooks.indexOf(LifecycleHooks.AfterViewInit) !== -1) {
|
if (lifecycleHooks.indexOf(LifecycleHooks.AfterViewInit) !== -1) {
|
||||||
afterViewLifecycleCallbacksMethod.addStmt(new o.IfStmt(
|
afterViewLifecycleCallbacksMethod.addStmt(new o.IfStmt(
|
||||||
isFirstViewCheck(o.THIS_EXPR),
|
STATE_IS_NEVER_CHECKED, [directiveInstance.callMethod('ngAfterViewInit', []).toStmt()]));
|
||||||
[directiveInstance.callMethod('ngAfterViewInit', []).toStmt()]));
|
|
||||||
}
|
}
|
||||||
if (lifecycleHooks.indexOf(LifecycleHooks.AfterViewChecked) !== -1) {
|
if (lifecycleHooks.indexOf(LifecycleHooks.AfterViewChecked) !== -1) {
|
||||||
afterViewLifecycleCallbacksMethod.addStmt(
|
afterViewLifecycleCallbacksMethod.addStmt(
|
||||||
|
@ -8,19 +8,19 @@
|
|||||||
|
|
||||||
import {SecurityContext} from '@angular/core';
|
import {SecurityContext} from '@angular/core';
|
||||||
|
|
||||||
import {createCheckBindingField} from '../compiler_util/binding_util';
|
import {createCheckBindingField, createCheckBindingStmt} from '../compiler_util/binding_util';
|
||||||
import {ConvertPropertyBindingResult, convertPropertyBinding} from '../compiler_util/expression_converter';
|
import {ConvertPropertyBindingResult, convertPropertyBinding} from '../compiler_util/expression_converter';
|
||||||
import {createEnumExpression} from '../compiler_util/identifier_util';
|
import {createEnumExpression} from '../compiler_util/identifier_util';
|
||||||
import {createCheckAnimationBindingStmts, createCheckRenderBindingStmt} from '../compiler_util/render_util';
|
import {triggerAnimation, writeToRenderer} from '../compiler_util/render_util';
|
||||||
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||||
import {Identifiers, createIdentifier} from '../identifiers';
|
import {Identifiers, createIdentifier} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {isDefaultChangeDetectionStrategy} from '../private_import_core';
|
import {isDefaultChangeDetectionStrategy} from '../private_import_core';
|
||||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||||
import {BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, PropertyBindingType} from '../template_parser/template_ast';
|
import {BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, PropertyBindingType} from '../template_parser/template_ast';
|
||||||
|
|
||||||
import {CompileElement, CompileNode} from './compile_element';
|
import {CompileElement, CompileNode} from './compile_element';
|
||||||
import {CompileView} from './compile_view';
|
import {CompileView} from './compile_view';
|
||||||
|
import {DetectChangesVars} from './constants';
|
||||||
import {getHandleEventMethodName} from './util';
|
import {getHandleEventMethodName} from './util';
|
||||||
|
|
||||||
export function bindRenderText(
|
export function bindRenderText(
|
||||||
@ -33,15 +33,11 @@ export function bindRenderText(
|
|||||||
}
|
}
|
||||||
|
|
||||||
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText);
|
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText);
|
||||||
view.detectChangesRenderPropertiesMethod.addStmts(evalResult.stmts);
|
view.detectChangesRenderPropertiesMethod.addStmts(createCheckBindingStmt(
|
||||||
view.detectChangesRenderPropertiesMethod.addStmt(
|
evalResult, valueField.expression, DetectChangesVars.throwOnChange,
|
||||||
o.importExpr(createIdentifier(Identifiers.checkRenderText))
|
[o.THIS_EXPR.prop('renderer')
|
||||||
.callFn([
|
.callMethod('setText', [compileNode.renderNode, evalResult.currValExpr])
|
||||||
o.THIS_EXPR, compileNode.renderNode, valueField.expression,
|
.toStmt()]));
|
||||||
valueField.expression.set(evalResult.currValExpr),
|
|
||||||
evalResult.forceUpdate || o.literal(false)
|
|
||||||
])
|
|
||||||
.toStmt());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bindRenderInputs(
|
export function bindRenderInputs(
|
||||||
@ -58,27 +54,31 @@ export function bindRenderInputs(
|
|||||||
if (!evalResult) {
|
if (!evalResult) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const checkBindingStmts: o.Statement[] = [];
|
||||||
let compileMethod = view.detectChangesRenderPropertiesMethod;
|
let compileMethod = view.detectChangesRenderPropertiesMethod;
|
||||||
switch (boundProp.type) {
|
switch (boundProp.type) {
|
||||||
case PropertyBindingType.Property:
|
case PropertyBindingType.Property:
|
||||||
case PropertyBindingType.Attribute:
|
case PropertyBindingType.Attribute:
|
||||||
case PropertyBindingType.Class:
|
case PropertyBindingType.Class:
|
||||||
case PropertyBindingType.Style:
|
case PropertyBindingType.Style:
|
||||||
compileMethod.addStmts(createCheckRenderBindingStmt(
|
checkBindingStmts.push(...writeToRenderer(
|
||||||
o.THIS_EXPR, renderNode, boundProp, bindingField.expression, evalResult));
|
o.THIS_EXPR, boundProp, renderNode, evalResult.currValExpr,
|
||||||
|
view.genConfig.logBindingUpdate));
|
||||||
break;
|
break;
|
||||||
case PropertyBindingType.Animation:
|
case PropertyBindingType.Animation:
|
||||||
compileMethod = view.animationBindingsMethod;
|
compileMethod = view.animationBindingsMethod;
|
||||||
const {checkUpdateStmts, checkDetachStmts} = createCheckAnimationBindingStmts(
|
const {updateStmts, detachStmts} = triggerAnimation(
|
||||||
o.THIS_EXPR, o.THIS_EXPR, boundProp, boundOutputs,
|
o.THIS_EXPR, o.THIS_EXPR, boundProp, boundOutputs,
|
||||||
(hasEvents ? o.THIS_EXPR.prop(getHandleEventMethodName(compileElement.nodeIndex)) :
|
(hasEvents ? o.THIS_EXPR.prop(getHandleEventMethodName(compileElement.nodeIndex)) :
|
||||||
o.importExpr(createIdentifier(Identifiers.noop)))
|
o.importExpr(createIdentifier(Identifiers.noop)))
|
||||||
.callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR]),
|
.callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR]),
|
||||||
compileElement.renderNode, bindingField.expression, evalResult);
|
compileElement.renderNode, evalResult.currValExpr, bindingField.expression);
|
||||||
view.detachMethod.addStmts(checkDetachStmts);
|
checkBindingStmts.push(...updateStmts);
|
||||||
compileMethod.addStmts(checkUpdateStmts);
|
view.detachMethod.addStmts(detachStmts);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
compileMethod.addStmts(createCheckBindingStmt(
|
||||||
|
evalResult, bindingField.expression, DetectChangesVars.throwOnChange, checkBindingStmts));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ export function bindDirectiveHostProps(
|
|||||||
DirectiveWrapperExpressions.checkHost(
|
DirectiveWrapperExpressions.checkHost(
|
||||||
directiveAst.hostProperties, directiveWrapperInstance, o.THIS_EXPR,
|
directiveAst.hostProperties, directiveWrapperInstance, o.THIS_EXPR,
|
||||||
compileElement.compViewExpr || o.THIS_EXPR, compileElement.renderNode,
|
compileElement.compViewExpr || o.THIS_EXPR, compileElement.renderNode,
|
||||||
runtimeSecurityCtxExprs));
|
DetectChangesVars.throwOnChange, runtimeSecurityCtxExprs));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bindDirectiveInputs(
|
export function bindDirectiveInputs(
|
||||||
@ -132,13 +132,17 @@ export function bindDirectiveInputs(
|
|||||||
directiveWrapperInstance
|
directiveWrapperInstance
|
||||||
.callMethod(
|
.callMethod(
|
||||||
`check_${input.directiveName}`,
|
`check_${input.directiveName}`,
|
||||||
[o.THIS_EXPR, evalResult.currValExpr, evalResult.forceUpdate || o.literal(false)])
|
[
|
||||||
|
evalResult.currValExpr, DetectChangesVars.throwOnChange,
|
||||||
|
evalResult.forceUpdate || o.literal(false)
|
||||||
|
])
|
||||||
.toStmt());
|
.toStmt());
|
||||||
});
|
});
|
||||||
const isOnPushComp = directiveAst.directive.isComponent &&
|
const isOnPushComp = directiveAst.directive.isComponent &&
|
||||||
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
|
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
|
||||||
const directiveDetectChangesExpr = DirectiveWrapperExpressions.ngDoCheck(
|
const directiveDetectChangesExpr = DirectiveWrapperExpressions.ngDoCheck(
|
||||||
directiveWrapperInstance, o.THIS_EXPR, compileElement.renderNode);
|
directiveWrapperInstance, o.THIS_EXPR, compileElement.renderNode,
|
||||||
|
DetectChangesVars.throwOnChange);
|
||||||
const directiveDetectChangesStmt = isOnPushComp ?
|
const directiveDetectChangesStmt = isOnPushComp ?
|
||||||
new o.IfStmt(
|
new o.IfStmt(
|
||||||
directiveDetectChangesExpr,
|
directiveDetectChangesExpr,
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompileQueryMetadata, CompileTokenMetadata, tokenReference} from '../compile_metadata';
|
import {CompileTokenMetadata, tokenReference} from '../compile_metadata';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
|
|
||||||
import {CompileElement} from './compile_element';
|
import {CompileElement} from './compile_element';
|
||||||
@ -17,16 +17,18 @@ import {CompileQuery} from './compile_query';
|
|||||||
// as we create embedded views before the <template> elements themselves.
|
// as we create embedded views before the <template> elements themselves.
|
||||||
export function bindQueryValues(ce: CompileElement) {
|
export function bindQueryValues(ce: CompileElement) {
|
||||||
const queriesWithReads: _QueryWithRead[] = [];
|
const queriesWithReads: _QueryWithRead[] = [];
|
||||||
|
|
||||||
ce.getProviderTokens().forEach((token) => {
|
ce.getProviderTokens().forEach((token) => {
|
||||||
const queriesForProvider = ce.getQueriesFor(token);
|
const queriesForProvider = ce.getQueriesFor(token);
|
||||||
queriesWithReads.push(...queriesForProvider.map(query => new _QueryWithRead(query, token)));
|
queriesWithReads.push(...queriesForProvider.map(query => new _QueryWithRead(query, token)));
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.keys(ce.referenceTokens).forEach(varName => {
|
Object.keys(ce.referenceTokens).forEach(varName => {
|
||||||
const token = ce.referenceTokens[varName];
|
|
||||||
const varToken = {value: varName};
|
const varToken = {value: varName};
|
||||||
queriesWithReads.push(
|
queriesWithReads.push(
|
||||||
...ce.getQueriesFor(varToken).map(query => new _QueryWithRead(query, varToken)));
|
...ce.getQueriesFor(varToken).map(query => new _QueryWithRead(query, varToken)));
|
||||||
});
|
});
|
||||||
|
|
||||||
queriesWithReads.forEach((queryWithRead) => {
|
queriesWithReads.forEach((queryWithRead) => {
|
||||||
let value: o.Expression;
|
let value: o.Expression;
|
||||||
if (queryWithRead.read.identifier) {
|
if (queryWithRead.read.identifier) {
|
||||||
|
@ -9,8 +9,6 @@
|
|||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileTokenMetadata, identifierName} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileTokenMetadata, identifierName} from '../compile_metadata';
|
||||||
import {createDiTokenExpression} from '../compiler_util/identifier_util';
|
import {createDiTokenExpression} from '../compiler_util/identifier_util';
|
||||||
import {isPresent} from '../facade/lang';
|
|
||||||
import {Identifiers, createIdentifier} from '../identifiers';
|
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {ViewType} from '../private_import_core';
|
import {ViewType} from '../private_import_core';
|
||||||
|
|
||||||
@ -23,7 +21,7 @@ export function getPropertyInView(
|
|||||||
} else {
|
} else {
|
||||||
let viewProp: o.Expression = o.THIS_EXPR;
|
let viewProp: o.Expression = o.THIS_EXPR;
|
||||||
let currView: CompileView = callingView;
|
let currView: CompileView = callingView;
|
||||||
while (currView !== definedView && isPresent(currView.declarationElement.view)) {
|
while (currView !== definedView && currView.declarationElement.view) {
|
||||||
currView = currView.declarationElement.view;
|
currView = currView.declarationElement.view;
|
||||||
viewProp = viewProp.prop('parentView');
|
viewProp = viewProp.prop('parentView');
|
||||||
}
|
}
|
||||||
@ -71,6 +69,12 @@ export function injectFromViewParentInjector(
|
|||||||
return viewExpr.callMethod('injectorGet', args);
|
return viewExpr.callMethod('injectorGet', args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getViewClassName(
|
||||||
|
component: CompileDirectiveSummary | CompileDirectiveMetadata,
|
||||||
|
embeddedTemplateIndex: number): string {
|
||||||
|
return `View_${identifierName(component.type)}${embeddedTemplateIndex}`;
|
||||||
|
}
|
||||||
|
|
||||||
export function getHandleEventMethodName(elementIndex: number): string {
|
export function getHandleEventMethodName(elementIndex: number): string {
|
||||||
return `handleEvent_${elementIndex}`;
|
return `handleEvent_${elementIndex}`;
|
||||||
}
|
}
|
||||||
|
@ -8,21 +8,21 @@
|
|||||||
|
|
||||||
import {ViewEncapsulation} from '@angular/core';
|
import {ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, viewClassName} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName} from '../compile_metadata';
|
||||||
import {createSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter';
|
import {createSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter';
|
||||||
import {createDiTokenExpression, createInlineArray} from '../compiler_util/identifier_util';
|
import {createDiTokenExpression, createInlineArray} from '../compiler_util/identifier_util';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {Identifiers, createIdentifier, identifierToken} from '../identifiers';
|
import {Identifiers, createIdentifier, identifierToken} from '../identifiers';
|
||||||
import {createClassStmt} from '../output/class_builder';
|
import {createClassStmt} from '../output/class_builder';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {ParseSourceSpan} from '../parse_util';
|
|
||||||
import {ChangeDetectorStatus, ViewType, isDefaultChangeDetectionStrategy} from '../private_import_core';
|
import {ChangeDetectorStatus, ViewType, isDefaultChangeDetectionStrategy} from '../private_import_core';
|
||||||
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
|
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
|
||||||
|
|
||||||
import {CompileElement, CompileNode} from './compile_element';
|
import {CompileElement, CompileNode} from './compile_element';
|
||||||
import {CompileView, CompileViewRootNode, CompileViewRootNodeType} from './compile_view';
|
import {CompileView, CompileViewRootNode, CompileViewRootNodeType} from './compile_view';
|
||||||
import {ChangeDetectorStatusEnum, InjectMethodVars, ViewConstructorVars, ViewEncapsulationEnum, ViewProperties, ViewTypeEnum} from './constants';
|
import {ChangeDetectorStatusEnum, DetectChangesVars, InjectMethodVars, ViewConstructorVars, ViewEncapsulationEnum, ViewProperties, ViewTypeEnum} from './constants';
|
||||||
import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency} from './deps';
|
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency} from './deps';
|
||||||
|
import {getViewClassName} from './util';
|
||||||
|
|
||||||
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
||||||
const CLASS_ATTR = 'class';
|
const CLASS_ATTR = 'class';
|
||||||
@ -35,8 +35,7 @@ const rootSelectorVar = o.variable('rootSelector');
|
|||||||
export function buildView(
|
export function buildView(
|
||||||
view: CompileView, template: TemplateAst[],
|
view: CompileView, template: TemplateAst[],
|
||||||
targetDependencies:
|
targetDependencies:
|
||||||
Array<ComponentViewDependency|ComponentFactoryDependency|DirectiveWrapperDependency>):
|
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>): number {
|
||||||
number {
|
|
||||||
const builderVisitor = new ViewBuilderVisitor(view, targetDependencies);
|
const builderVisitor = new ViewBuilderVisitor(view, targetDependencies);
|
||||||
const parentEl =
|
const parentEl =
|
||||||
view.declarationElement.isNull() ? view.declarationElement : view.declarationElement.parent;
|
view.declarationElement.isNull() ? view.declarationElement : view.declarationElement.parent;
|
||||||
@ -66,7 +65,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
|||||||
constructor(
|
constructor(
|
||||||
public view: CompileView,
|
public view: CompileView,
|
||||||
public targetDependencies:
|
public targetDependencies:
|
||||||
Array<ComponentViewDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {}
|
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {}
|
||||||
|
|
||||||
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
|
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
|
||||||
|
|
||||||
@ -217,7 +216,9 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
|||||||
this.view.nodes.push(compileElement);
|
this.view.nodes.push(compileElement);
|
||||||
let compViewExpr: o.ReadPropExpr = null;
|
let compViewExpr: o.ReadPropExpr = null;
|
||||||
if (isPresent(component)) {
|
if (isPresent(component)) {
|
||||||
this.targetDependencies.push(new ComponentViewDependency(component.type.reference));
|
const nestedComponentIdentifier: CompileIdentifierMetadata = {reference: null};
|
||||||
|
this.targetDependencies.push(new ViewClassDependency(
|
||||||
|
component.type, getViewClassName(component, 0), nestedComponentIdentifier));
|
||||||
|
|
||||||
compViewExpr = o.THIS_EXPR.prop(`compView_${nodeIndex}`); // fix highlighting: `
|
compViewExpr = o.THIS_EXPR.prop(`compView_${nodeIndex}`); // fix highlighting: `
|
||||||
this.view.fields.push(new o.ClassField(
|
this.view.fields.push(new o.ClassField(
|
||||||
@ -227,7 +228,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
|||||||
compileElement.setComponentView(compViewExpr);
|
compileElement.setComponentView(compViewExpr);
|
||||||
this.view.createMethod.addStmt(
|
this.view.createMethod.addStmt(
|
||||||
compViewExpr
|
compViewExpr
|
||||||
.set(o.importExpr({reference: component.componentViewType}).instantiate([
|
.set(o.importExpr(nestedComponentIdentifier).instantiate([
|
||||||
ViewProperties.viewUtils, o.THIS_EXPR, o.literal(nodeIndex), renderNode
|
ViewProperties.viewUtils, o.THIS_EXPR, o.literal(nodeIndex), renderNode
|
||||||
]))
|
]))
|
||||||
.toStmt());
|
.toStmt());
|
||||||
@ -403,8 +404,9 @@ function createViewTopLevelStmts(view: CompileView, targetStatements: o.Statemen
|
|||||||
o.literal(view.component.template.ngContentSelectors.length),
|
o.literal(view.component.template.ngContentSelectors.length),
|
||||||
ViewEncapsulationEnum.fromValue(view.component.template.encapsulation),
|
ViewEncapsulationEnum.fromValue(view.component.template.encapsulation),
|
||||||
view.styles,
|
view.styles,
|
||||||
o.literalMap(view.animations.map(
|
o.literalMap(
|
||||||
(entry): [string, o.Expression] => [entry.name, entry.fnExp])),
|
view.animations.map((entry): [string, o.Expression] => [entry.name, entry.fnExp]),
|
||||||
|
null, true),
|
||||||
]))
|
]))
|
||||||
.toDeclStmt(o.importType(createIdentifier(Identifiers.RenderComponentType))));
|
.toDeclStmt(o.importType(createIdentifier(Identifiers.RenderComponentType))));
|
||||||
}
|
}
|
||||||
@ -483,7 +485,9 @@ function createViewClass(
|
|||||||
],
|
],
|
||||||
addReturnValuefNotEmpty(view.injectorGetMethod.finish(), InjectMethodVars.notFoundResult),
|
addReturnValuefNotEmpty(view.injectorGetMethod.finish(), InjectMethodVars.notFoundResult),
|
||||||
o.DYNAMIC_TYPE),
|
o.DYNAMIC_TYPE),
|
||||||
new o.ClassMethod('detectChangesInternal', [], generateDetectChangesMethod(view)),
|
new o.ClassMethod(
|
||||||
|
'detectChangesInternal', [new o.FnParam(DetectChangesVars.throwOnChange.name, o.BOOL_TYPE)],
|
||||||
|
generateDetectChangesMethod(view)),
|
||||||
new o.ClassMethod('dirtyParentQueriesInternal', [], view.dirtyParentQueriesMethod.finish()),
|
new o.ClassMethod('dirtyParentQueriesInternal', [], view.dirtyParentQueriesMethod.finish()),
|
||||||
new o.ClassMethod('destroyInternal', [], generateDestroyMethod(view)),
|
new o.ClassMethod('destroyInternal', [], generateDestroyMethod(view)),
|
||||||
new o.ClassMethod('detachInternal', [], view.detachMethod.finish()),
|
new o.ClassMethod('detachInternal', [], view.detachMethod.finish()),
|
||||||
@ -567,26 +571,36 @@ function generateDetectChangesMethod(view: CompileView): o.Statement[] {
|
|||||||
stmts.push(...view.detectChangesInInputsMethod.finish());
|
stmts.push(...view.detectChangesInInputsMethod.finish());
|
||||||
view.viewContainers.forEach((viewContainer) => {
|
view.viewContainers.forEach((viewContainer) => {
|
||||||
stmts.push(
|
stmts.push(
|
||||||
viewContainer.callMethod('detectChangesInNestedViews', [ViewProperties.throwOnChange])
|
viewContainer.callMethod('detectChangesInNestedViews', [DetectChangesVars.throwOnChange])
|
||||||
.toStmt());
|
.toStmt());
|
||||||
});
|
});
|
||||||
const afterContentStmts = view.updateContentQueriesMethod.finish().concat(
|
const afterContentStmts = view.updateContentQueriesMethod.finish().concat(
|
||||||
view.afterContentLifecycleCallbacksMethod.finish());
|
view.afterContentLifecycleCallbacksMethod.finish());
|
||||||
if (afterContentStmts.length > 0) {
|
if (afterContentStmts.length > 0) {
|
||||||
stmts.push(new o.IfStmt(o.not(ViewProperties.throwOnChange), afterContentStmts));
|
stmts.push(new o.IfStmt(o.not(DetectChangesVars.throwOnChange), afterContentStmts));
|
||||||
}
|
}
|
||||||
stmts.push(...view.detectChangesRenderPropertiesMethod.finish());
|
stmts.push(...view.detectChangesRenderPropertiesMethod.finish());
|
||||||
view.viewChildren.forEach((viewChild) => {
|
view.viewChildren.forEach((viewChild) => {
|
||||||
stmts.push(
|
stmts.push(
|
||||||
viewChild.callMethod('internalDetectChanges', [ViewProperties.throwOnChange]).toStmt());
|
viewChild.callMethod('internalDetectChanges', [DetectChangesVars.throwOnChange]).toStmt());
|
||||||
});
|
});
|
||||||
const afterViewStmts =
|
const afterViewStmts =
|
||||||
view.updateViewQueriesMethod.finish().concat(view.afterViewLifecycleCallbacksMethod.finish());
|
view.updateViewQueriesMethod.finish().concat(view.afterViewLifecycleCallbacksMethod.finish());
|
||||||
if (afterViewStmts.length > 0) {
|
if (afterViewStmts.length > 0) {
|
||||||
stmts.push(new o.IfStmt(o.not(ViewProperties.throwOnChange), afterViewStmts));
|
stmts.push(new o.IfStmt(o.not(DetectChangesVars.throwOnChange), afterViewStmts));
|
||||||
}
|
}
|
||||||
|
|
||||||
const varStmts = createSharedBindingVariablesIfNeeded(stmts);
|
const varStmts: any[] = [];
|
||||||
|
const readVars = o.findReadVarNames(stmts);
|
||||||
|
if (readVars.has(DetectChangesVars.changed.name)) {
|
||||||
|
varStmts.push(DetectChangesVars.changed.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE));
|
||||||
|
}
|
||||||
|
if (readVars.has(DetectChangesVars.changes.name)) {
|
||||||
|
varStmts.push(
|
||||||
|
DetectChangesVars.changes.set(o.NULL_EXPR)
|
||||||
|
.toDeclStmt(new o.MapType(o.importType(createIdentifier(Identifiers.SimpleChange)))));
|
||||||
|
}
|
||||||
|
varStmts.push(...createSharedBindingVariablesIfNeeded(stmts));
|
||||||
return varStmts.concat(stmts);
|
return varStmts.concat(stmts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -681,7 +695,6 @@ function generateCreateEmbeddedViewsMethod(view: CompileView): o.ClassMethod {
|
|||||||
view.nodes.forEach((node) => {
|
view.nodes.forEach((node) => {
|
||||||
if (node instanceof CompileElement) {
|
if (node instanceof CompileElement) {
|
||||||
if (node.embeddedView) {
|
if (node.embeddedView) {
|
||||||
const parentNodeIndex = node.isRootElement() ? null : node.parent.nodeIndex;
|
|
||||||
stmts.push(new o.IfStmt(
|
stmts.push(new o.IfStmt(
|
||||||
nodeIndexVar.equals(o.literal(node.nodeIndex)),
|
nodeIndexVar.equals(o.literal(node.nodeIndex)),
|
||||||
[new o.ReturnStatement(node.embeddedView.classExpr.instantiate([
|
[new o.ReturnStatement(node.embeddedView.classExpr.instantiate([
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user