Compare commits
235 Commits
2.4.8
...
4.0.0-beta
Author | SHA1 | Date | |
---|---|---|---|
c4e7c083e2 | |||
28bdc5af47 | |||
b88714bcdf | |||
d2859cdd71 | |||
4931a615bf | |||
a733444d0e | |||
6152eb24bc | |||
b2f9d56577 | |||
1c24271daf | |||
c3e5ddbe20 | |||
d02eab498f | |||
fc550185fc | |||
0c7726dd74 | |||
83361d811d | |||
1f54040ef4 | |||
65417374f1 | |||
0adb97bffb | |||
f20d1a8af5 | |||
e21e9c5fb7 | |||
d3a3a8e1fc | |||
dff6ee3272 | |||
ba52b2e08c | |||
0589f93e41 | |||
2f87eb52fe | |||
9d8c467cb0 | |||
67dc0912c5 | |||
b049217437 | |||
2191f44025 | |||
4b854be29e | |||
0a724208b9 | |||
1200cf25f4 | |||
635bf02b02 | |||
2d7b3a86cc | |||
523fd84d22 | |||
e8ea741039 | |||
1a92e3d406 | |||
be6c95ad03 | |||
f816319e41 | |||
5047d9780d | |||
b1e3dda5cb | |||
d169c2434e | |||
6d1f1a43bb | |||
e19bf70b47 | |||
a6f8e9fc90 | |||
d6382bfa0b | |||
4dea347101 | |||
5237b1c98c | |||
f364557629 | |||
c2aa981dd6 | |||
dc63cef10a | |||
2c294d5dff | |||
e1af25d93e | |||
123943a6e0 | |||
3a4b54daa4 | |||
95cbca20a5 | |||
aeed7373af | |||
2e3ac70e0a | |||
9aeb8c5357 | |||
424e6c4cb9 | |||
5cb2008e6c | |||
78f42c7aa1 | |||
d4d3782d45 | |||
46cb04d575 | |||
8c7e93bebe | |||
5d9cbd7d6f | |||
d061adc02d | |||
6d29faefea | |||
99aa49ab6c | |||
e5c6bb4286 | |||
d9a22dae4f | |||
fb6c4582a1 | |||
8578682dcf | |||
c0178de0e2 | |||
31322e73b7 | |||
9211a22039 | |||
3f67ab074a | |||
4bae4b3bb5 | |||
02dd90faed | |||
1c85e99588 | |||
ccb65893bf | |||
3e90ffd293 | |||
8063b0d9a2 | |||
21030e9a1c | |||
889b48d85f | |||
1bd04e95de | |||
f88cd2f22e | |||
f822f9599c | |||
9898d8f6d9 | |||
2dd6280ab8 | |||
35f9a1c2cb | |||
465516b905 | |||
db49d422f2 | |||
8ed92d75b0 | |||
50e5cb15dd | |||
c5c53f3666 | |||
bb0d23f82b | |||
1e6440e81b | |||
6b02b80a03 | |||
2c0c86e3ce | |||
5b4bea24de | |||
7690d02133 | |||
b2ae7b607e | |||
7c210645a3 | |||
07e0fce8fc | |||
0ac8e102de | |||
e74d8aaf92 | |||
881eb894bc | |||
0eca960494 | |||
eed83443b8 | |||
e5c4e5801f | |||
69fa3bbc03 | |||
445ed43b9a | |||
174334dec3 | |||
9c697030e6 | |||
697690349f | |||
0448e80704 | |||
e85232afd2 | |||
e7ece6c8ce | |||
67380d4b28 | |||
f114e40212 | |||
952471e25d | |||
c65e428778 | |||
842f52e841 | |||
eb2ceff4ba | |||
f49ab56160 | |||
c0f750af4e | |||
bcd37f52fb | |||
e69c1fb36c | |||
9da4c259a5 | |||
fcd116fdc0 | |||
383adc9ad9 | |||
9b8488f007 | |||
1817ddb57b | |||
1ee574c51e | |||
171a9bdc85 | |||
896916af29 | |||
e49c7fae22 | |||
6b65fc1286 | |||
0e3981afc1 | |||
e78508507d | |||
a23fa94ca8 | |||
4568d5ddac | |||
c6e893953f | |||
55dfa1b69d | |||
0fe3cd9a4c | |||
0c19898694 | |||
5b6e8ea3ec | |||
732f446ad2 | |||
f0e092515c | |||
14e785f5b7 | |||
01d1624884 | |||
33910ddfc9 | |||
01ca2db6ae | |||
6cefccb314 | |||
fa9e21e83c | |||
b6078f5887 | |||
c65b4fa9dc | |||
169ed82900 | |||
fd8e15b15d | |||
aa40366a92 | |||
40d8d9c3e3 | |||
ee2ac025ef | |||
aa3769ba69 | |||
d4ddb6004e | |||
84400bcc86 | |||
42d9998cbb | |||
c18d2fe5e3 | |||
d91a86aac6 | |||
d6e5e9283c | |||
eab7e490c9 | |||
3e90605db9 | |||
79671a6f12 | |||
a659259962 | |||
b56474d067 | |||
8395f0e138 | |||
dd0519abad | |||
f238c8ac7a | |||
8c27c62fab | |||
5031adc7a3 | |||
821b8f09d6 | |||
2bf1bbc071 | |||
7b0a86718c | |||
3edca4d37e | |||
a0a05041ac | |||
7256d0ede5 | |||
d62d89319e | |||
f5f1d5f65c | |||
a8d237581d | |||
d036165a19 | |||
d17e690eb4 | |||
714f2af0dd | |||
2b90cd532f | |||
3a64ad895a | |||
9ec0a4e105 | |||
4b3d135193 | |||
1d0ed6f75f | |||
6f330a5fc9 | |||
e23076f767 | |||
7295a5e7f2 | |||
20bed46737 | |||
2a5012d515 | |||
fb38fba8f9 | |||
4c35be3e07 | |||
e9f307f948 | |||
2e500cc85b | |||
56dce0e26d | |||
8a8c53250e | |||
08ff2e5249 | |||
a006c1418a | |||
90c223591f | |||
aaf6e05f56 | |||
3bee521aa4 | |||
95f48292b1 | |||
04cfa1ebdf | |||
4022173d1e | |||
c8baf51f4f | |||
b4db73d0bf | |||
e15a3f273f | |||
213c713409 | |||
9a8423da36 | |||
f0b0762f4a | |||
b5c4bf1c59 | |||
56c361ff6a | |||
562f7a2f8b | |||
6dd5201765 | |||
72361fb68f | |||
5c6ec20c7e | |||
440ef02f29 | |||
4e3d58a792 | |||
61d7c1e0b3 | |||
bf93389615 | |||
4398056146 | |||
1b547886d0 | |||
9591a08dfb | |||
65965c27a8 |
236
.pullapprove.yml
236
.pullapprove.yml
@ -1,24 +1,6 @@
|
|||||||
# Configuration for pullapprove.com
|
# Configuration for pullapprove.com
|
||||||
#
|
# See ownership spreadsheet:
|
||||||
# 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
|
# 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
|
version: 2
|
||||||
|
|
||||||
@ -27,222 +9,24 @@ group_defaults:
|
|||||||
reset_on_reopened:
|
reset_on_reopened:
|
||||||
enabled: true
|
enabled: true
|
||||||
approve_by_comment:
|
approve_by_comment:
|
||||||
enabled: false
|
enabled: true
|
||||||
|
approve_regex: '^(Approved|:\+1:|LGTM)'
|
||||||
|
|
||||||
groups:
|
groups:
|
||||||
root:
|
config:
|
||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
include:
|
|
||||||
- "*"
|
|
||||||
exclude:
|
|
||||||
- "angular.io/*"
|
|
||||||
- "integration/*"
|
|
||||||
- "modules/*"
|
|
||||||
- "tools/*"
|
|
||||||
users:
|
|
||||||
- IgorMinar
|
|
||||||
- mhevery
|
|
||||||
|
|
||||||
public-api:
|
|
||||||
conditions:
|
|
||||||
files:
|
|
||||||
include:
|
|
||||||
- "tools/public_api_guard/*"
|
|
||||||
users:
|
|
||||||
- IgorMinar
|
|
||||||
- mhevery
|
|
||||||
|
|
||||||
build-and-ci:
|
|
||||||
conditions:
|
|
||||||
files:
|
|
||||||
include:
|
|
||||||
- "*.yml"
|
- "*.yml"
|
||||||
- "*.json"
|
- "*.json"
|
||||||
- "*.lock"
|
teams:
|
||||||
- "tools/*"
|
- repoowners
|
||||||
exclude:
|
|
||||||
- "tools/@angular/tsc-wrapped/*"
|
|
||||||
- "tools/public_api_guard/*"
|
|
||||||
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:
|
compiler:
|
||||||
conditions:
|
|
||||||
files:
|
|
||||||
- "modules/@angular/compiler/*"
|
|
||||||
users:
|
|
||||||
- tbosch #primary
|
|
||||||
- vicb
|
|
||||||
- chuckjaz
|
|
||||||
- mhevery
|
|
||||||
- IgorMinar #fallback
|
|
||||||
|
|
||||||
compiler-cli:
|
|
||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
- "tools/@angular/tsc-wrapped/*"
|
- "tools/@angular/tsc-wrapped/*"
|
||||||
|
- "modules/@angular/compiler/*"
|
||||||
- "modules/@angular/compiler-cli/*"
|
- "modules/@angular/compiler-cli/*"
|
||||||
users:
|
teams:
|
||||||
- alexeagle
|
- compiler-owners
|
||||||
- chuckjaz
|
- repoowners
|
||||||
- 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 #secondary
|
|
||||||
- IgorMinar #fallback
|
|
||||||
- mhevery #fallback
|
|
||||||
|
|
||||||
platform-server:
|
|
||||||
conditions:
|
|
||||||
files:
|
|
||||||
- "modules/@angular/platform-server/*"
|
|
||||||
users:
|
|
||||||
- vikerman #primary
|
|
||||||
- alxhub
|
|
||||||
- vicb
|
|
||||||
- tbosch
|
|
||||||
- IgorMinar #fallback
|
|
||||||
- mhevery #fallback
|
|
||||||
|
|
||||||
platform-webworker:
|
|
||||||
conditions:
|
|
||||||
files:
|
|
||||||
- "modules/@angular/platform-webworker/*"
|
|
||||||
users:
|
|
||||||
- vicb #primary
|
|
||||||
- tbosch #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
|
|
||||||
- petebacondarwin
|
|
||||||
- mhevery #fallback
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
sudo: false
|
sudo: false
|
||||||
node_js:
|
node_js:
|
||||||
- '6.9.5'
|
- '6.6.0'
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
# firefox: "38.0"
|
# firefox: "38.0"
|
||||||
@ -28,8 +28,8 @@ env:
|
|||||||
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
||||||
matrix:
|
matrix:
|
||||||
# Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
# Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
||||||
|
- CI_MODE=e2e EXPERIMENTAL_ES2015_DISTRO=1
|
||||||
- CI_MODE=js
|
- CI_MODE=js
|
||||||
- CI_MODE=e2e
|
|
||||||
- CI_MODE=saucelabs_required
|
- CI_MODE=saucelabs_required
|
||||||
- CI_MODE=browserstack_required
|
- CI_MODE=browserstack_required
|
||||||
- CI_MODE=saucelabs_optional
|
- CI_MODE=saucelabs_optional
|
||||||
|
249
CHANGELOG.md
249
CHANGELOG.md
@ -1,53 +1,64 @@
|
|||||||
<a name="2.4.8"></a>
|
<a name="4.0.0-beta.5"></a>
|
||||||
## [2.4.8](https://github.com/angular/angular/compare/2.4.7...2.4.8) (2017-02-18)
|
# [4.0.0-beta.5](https://github.com/angular/angular/compare/4.0.0-beta.3...4.0.0-beta.5) (2017-01-25)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **forms:** getRawValue should correctly work with nested FormGroups/Arrays ([#12964](https://github.com/angular/angular/issues/12964)) ([ea7737e](https://github.com/angular/angular/commit/ea7737e)), closes [#12963](https://github.com/angular/angular/issues/12963)
|
* **animations:** fix internal jscompiler issue and AOT quoting ([#13798](https://github.com/angular/angular/issues/13798)) ([c2aa981](https://github.com/angular/angular/commit/c2aa981))
|
||||||
* **http:** REVERT: remove dots from jsonp callback name ([#13219](https://github.com/angular/angular/issues/13219)) ([9ceb5d1](https://github.com/angular/angular/commit/9ceb5d1))
|
* **common:** support numeric value as discrete cases for NgPlural ([#13876](https://github.com/angular/angular/issues/13876)) ([f364557](https://github.com/angular/angular/commit/f364557))
|
||||||
* **platform-browser:** should only add styles with native encapsulation in shadow DOM ([#14313](https://github.com/angular/angular/issues/14313)) ([fadaf1e](https://github.com/angular/angular/commit/fadaf1e)), closes [#7887](https://github.com/angular/angular/issues/7887)
|
* **compiler:** [i18n] XMB/XTB placeholder names can contain only A-Z, 0-9, _n ([d02eab4](https://github.com/angular/angular/commit/d02eab4))
|
||||||
* **router:** do not finish bootstrap until all the routes are resolved ([#14327](https://github.com/angular/angular/issues/14327)) ([541de26](https://github.com/angular/angular/commit/541de26)), closes [#12162](https://github.com/angular/angular/issues/12162)
|
* **compiler:** fix regexp to support firefox 31 ([#14082](https://github.com/angular/angular/issues/14082)) ([b2f9d56](https://github.com/angular/angular/commit/b2f9d56)), closes [#14029](https://github.com/angular/angular/issues/14029) [#13900](https://github.com/angular/angular/issues/13900)
|
||||||
* **upgrade:** correctly project content on downgraded components with structural directives ([#14274](https://github.com/angular/angular/issues/14274)) ([74cb575](https://github.com/angular/angular/commit/74cb575)), closes [#14260](https://github.com/angular/angular/issues/14260)
|
* **core:** export animation classes required for Renderer impl ([#14002](https://github.com/angular/angular/issues/14002)) ([83361d8](https://github.com/angular/angular/commit/83361d8)), closes [#14001](https://github.com/angular/angular/issues/14001)
|
||||||
* **upgrade:** pass correct values to `ngOnChanges` for interpolation bindings ([#14400](https://github.com/angular/angular/issues/14400)) ([7c87c52](https://github.com/angular/angular/commit/7c87c52))
|
* **core:** fix not declared variable in view engine ([#14045](https://github.com/angular/angular/issues/14045)) ([d3a3a8e](https://github.com/angular/angular/commit/d3a3a8e))
|
||||||
|
* **http:** don't create a blob out of ArrayBuffer when type is application/octet-stream ([#13992](https://github.com/angular/angular/issues/13992)) ([1200cf2](https://github.com/angular/angular/commit/1200cf2)), closes [#13973](https://github.com/angular/angular/issues/13973)
|
||||||
|
* **router:** enable loadChildren with function in aot ([#13909](https://github.com/angular/angular/issues/13909)) ([635bf02](https://github.com/angular/angular/commit/635bf02)), 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)) ([e8ea741](https://github.com/angular/angular/commit/e8ea741)), closes [#13270](https://github.com/angular/angular/issues/13270)
|
||||||
|
* **upgrade:** detect async downgrade component changes ([#13812](https://github.com/angular/angular/issues/13812)) ([d6382bf](https://github.com/angular/angular/commit/d6382bf)), 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)
|
||||||
|
* **upgrade/static:** ensure upgraded injector is initialized early enough ([#14065](https://github.com/angular/angular/issues/14065)) ([6152eb2](https://github.com/angular/angular/commit/6152eb2)), closes [#13811](https://github.com/angular/angular/issues/13811)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
<a name="2.4.7"></a>
|
* **build:** optionally build an ES2015 distro ([#13471](https://github.com/angular/angular/issues/13471)) ([be6c95a](https://github.com/angular/angular/commit/be6c95a))
|
||||||
## [2.4.7](https://github.com/angular/angular/compare/2.4.6...2.4.7) (2017-02-09)
|
* **core:** add event support to view engine ([0adb97b](https://github.com/angular/angular/commit/0adb97b))
|
||||||
|
* **core:** add initial view engine ([#14014](https://github.com/angular/angular/issues/14014)) ([2f87eb5](https://github.com/angular/angular/commit/2f87eb5))
|
||||||
|
* **core:** add pure expression support to view engine ([6541737](https://github.com/angular/angular/commit/6541737))
|
||||||
|
* **core:** Add type information to injector.get() ([#13785](https://github.com/angular/angular/issues/13785)) ([d169c24](https://github.com/angular/angular/commit/d169c24))
|
||||||
|
* **security:** allow calc and gradient functions. ([#13943](https://github.com/angular/angular/issues/13943)) ([e19bf70](https://github.com/angular/angular/commit/e19bf70))
|
||||||
|
* **tsc-wrapped:** Support of vinyl like config file was added ([#13987](https://github.com/angular/angular/issues/13987)) ([0c7726d](https://github.com/angular/angular/commit/0c7726d))
|
||||||
|
* **upgrade:** Support ng-model in downgraded components ([#10578](https://github.com/angular/angular/issues/10578)) ([e21e9c5](https://github.com/angular/angular/commit/e21e9c5))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### BREAKING CHANGES
|
||||||
|
|
||||||
* **upgrade:** allow non-element selectors for downgraded components ([#14291](https://github.com/angular/angular/issues/14291)) ([5bb47db](https://github.com/angular/angular/commit/5bb47db))
|
* core: - Because `injector.get()` is now parameterize it is possible that code
|
||||||
|
which used to work no longer type checks. Example would be if one
|
||||||
|
injects `Foo` but configures it as `{provide: Foo, useClass: MockFoo}`.
|
||||||
|
The injection instance will be that of `MockFoo` but the type will be
|
||||||
|
`Foo` instead of `any` as in the past. This means that it was possible
|
||||||
|
to call a method on `MockFoo` in the past which now will fail type
|
||||||
|
check. See this example:
|
||||||
|
|
||||||
|
```
|
||||||
|
class Foo {}
|
||||||
|
class MockFoo extends Foo {
|
||||||
|
setupMock();
|
||||||
|
}
|
||||||
|
|
||||||
|
var PROVIDERS = [
|
||||||
|
{provide: Foo, useClass: MockFoo}
|
||||||
|
];
|
||||||
|
|
||||||
<a name="2.4.6"></a>
|
...
|
||||||
## [2.4.6](https://github.com/angular/angular/compare/2.4.5...2.4.6) (2017-02-03)
|
|
||||||
|
|
||||||
|
function myTest(injector: Injector) {
|
||||||
### Bug Fixes
|
var foo = injector.get(Foo);
|
||||||
|
// This line used to work since `foo` used to be `any` before this
|
||||||
* **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)
|
// change, it will now be `Foo`, and `Foo` does not have `setUpMock()`.
|
||||||
* **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)
|
// The fix is to downcast: `injector.get(Foo) as MockFoo`.
|
||||||
* **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)
|
foo.setUpMock();
|
||||||
* **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:** 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)
|
```
|
||||||
* **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)
|
|
||||||
* **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))
|
|
||||||
* **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)
|
|
||||||
* **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))
|
|
||||||
* 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))
|
|
||||||
* **http:** remove dots from jsonp callback name ([#13219](https://github.com/angular/angular/issues/13219)) ([1eece50](https://github.com/angular/angular/commit/1eece50))
|
|
||||||
* **i18n:** parse ICU messages while normalizing templates ([#14153](https://github.com/angular/angular/issues/14153)) ([8d4aa82](https://github.com/angular/angular/commit/8d4aa82))
|
|
||||||
* **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)
|
|
||||||
* **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)
|
|
||||||
* **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:** 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:** 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)
|
|
||||||
* **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))
|
|
||||||
* **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)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -64,12 +75,63 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.4.4"></a>
|
<a name="4.0.0-beta.4"></a>
|
||||||
## [2.4.4](https://github.com/angular/angular/compare/2.4.3...2.4.4) (2017-01-19)
|
# [4.0.0-beta.4](https://github.com/angular/angular/compare/4.0.0-beta.3...4.0.0-beta.4) (2017-01-19)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **animations:** fix internal jscompiler issue and AOT quoting ([#13798](https://github.com/angular/angular/issues/13798)) ([c2aa981](https://github.com/angular/angular/commit/c2aa981))
|
||||||
|
* **common:** support numeric value as discrete cases for NgPlural ([#13876](https://github.com/angular/angular/issues/13876)) ([f364557](https://github.com/angular/angular/commit/f364557))
|
||||||
|
* **http:** don't create a blob out of ArrayBuffer when type is application/octet-stream ([#13992](https://github.com/angular/angular/issues/13992)) ([1200cf2](https://github.com/angular/angular/commit/1200cf2)), closes [#13973](https://github.com/angular/angular/issues/13973)
|
||||||
|
* **router:** enable loadChildren with function in aot ([#13909](https://github.com/angular/angular/issues/13909)) ([635bf02](https://github.com/angular/angular/commit/635bf02)), 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)) ([e8ea741](https://github.com/angular/angular/commit/e8ea741)), closes [#13270](https://github.com/angular/angular/issues/13270)
|
||||||
|
* **security:** allow calc and gradient functions. ([#13943](https://github.com/angular/angular/issues/13943)) ([e19bf70](https://github.com/angular/angular/commit/e19bf70))
|
||||||
|
* **upgrade:** detect async downgrade component changes ([#13812](https://github.com/angular/angular/issues/13812)) ([d6382bf](https://github.com/angular/angular/commit/d6382bf)), 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)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **build:** optionally build an ES2015 distro ([#13471](https://github.com/angular/angular/issues/13471)) ([be6c95a](https://github.com/angular/angular/commit/be6c95a))
|
||||||
|
* **core:** Add type information to injector.get() ([#13785](https://github.com/angular/angular/issues/13785)) ([d169c24](https://github.com/angular/angular/commit/d169c24))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* core: - Because `injector.get()` is now parameterize it is possible that code
|
||||||
|
which used to work no longer type checks. Example would be if one
|
||||||
|
injects `Foo` but configures it as `{provide: Foo, useClass: MockFoo}`.
|
||||||
|
The injection instance will be that of `MockFoo` but the type will be
|
||||||
|
`Foo` instead of `any` as in the past. This means that it was possible
|
||||||
|
to call a method on `MockFoo` in the past which now will fail type
|
||||||
|
check. See this example:
|
||||||
|
|
||||||
|
```
|
||||||
|
class Foo {}
|
||||||
|
class MockFoo extends Foo {
|
||||||
|
setupMock();
|
||||||
|
}
|
||||||
|
|
||||||
|
var PROVIDERS = [
|
||||||
|
{provide: Foo, useClass: MockFoo}
|
||||||
|
];
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
function myTest(injector: Injector) {
|
||||||
|
var foo = injector.get(Foo);
|
||||||
|
// This line used to work since `foo` used to be `any` before this
|
||||||
|
// change, it will now be `Foo`, and `Foo` does not have `setUpMock()`.
|
||||||
|
// The fix is to downcast: `injector.get(Foo) as MockFoo`.
|
||||||
|
foo.setUpMock();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.4.4"></a>
|
||||||
|
## [2.4.4](https://github.com/angular/angular/compare/2.4.3...2.4.4) (2017-01-19)
|
||||||
|
|
||||||
* **animations:** fix internal jscompiler issue and AOT quoting ([#13798](https://github.com/angular/angular/issues/13798)) ([261fd16](https://github.com/angular/angular/commit/261fd16))
|
* **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))
|
* **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)
|
* **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)
|
||||||
@ -80,6 +142,58 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="4.0.0-beta.3"></a>
|
||||||
|
# [4.0.0-beta.3](https://github.com/angular/angular/compare/4.0.0-beta.2...4.0.0-beta.3) (2017-01-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** avoid evaluating arguments to unknown decorators ([d061adc](https://github.com/angular/angular/commit/d061adc)), closes [#13605](https://github.com/angular/angular/issues/13605)
|
||||||
|
* **compiler:** fix template binding parsing (`*directive="-..."`) ([e5c6bb4](https://github.com/angular/angular/commit/e5c6bb4)), closes [#13800](https://github.com/angular/angular/issues/13800)
|
||||||
|
* **compiler-cli:** add support for more than 2 levels of nested lazy routes ([5d9cbd7](https://github.com/angular/angular/commit/5d9cbd7)), 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 ([aeed737](https://github.com/angular/angular/commit/aeed737)), closes [angular/angular-cli#3204](https://github.com/angular/angular-cli/issues/3204)
|
||||||
|
* **core:** Add type information to differs ([8c7e93b](https://github.com/angular/angular/commit/8c7e93b)), closes [#13382](https://github.com/angular/angular/issues/13382)
|
||||||
|
* **i18n:** translate attributes inside elements marked for translation ([424e6c4](https://github.com/angular/angular/commit/424e6c4)), closes [#13796](https://github.com/angular/angular/issues/13796) [#13814](https://github.com/angular/angular/issues/13814)
|
||||||
|
* **router:** RouterLink mirrors input `target` as attribute ([d9a22da](https://github.com/angular/angular/commit/d9a22da)), closes [#13837](https://github.com/angular/angular/issues/13837)
|
||||||
|
* **router:** throw an error when navigate to null/undefined path ([46cb04d](https://github.com/angular/angular/commit/46cb04d)), closes [#10560](https://github.com/angular/angular/issues/10560) [#13384](https://github.com/angular/angular/issues/13384)
|
||||||
|
* **router:** fix checking for object intersection ([6d29fae](https://github.com/angular/angular/commit/6d29fae))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **animations:** expose the `element` value within transition events ([4bae4b3](https://github.com/angular/angular/commit/4bae4b3))
|
||||||
|
* **animations:** expose the `triggerName` within the transition event ([3f67ab0](https://github.com/angular/angular/commit/3f67ab0)), closes [#13600](https://github.com/angular/angular/issues/13600)
|
||||||
|
* **animations:** support function types in transitions ([9211a22](https://github.com/angular/angular/commit/9211a22)), closes [#13538](https://github.com/angular/angular/issues/13538) [#13537](https://github.com/angular/angular/issues/13537)
|
||||||
|
* **language-service:** support TS2.2 plugin model ([99aa49a](https://github.com/angular/angular/commit/99aa49a))
|
||||||
|
* **NgComponentOutlet:** add NgComponentOutlet directive ([8578682](https://github.com/angular/angular/commit/8578682)), closes [#11168](https://github.com/angular/angular/issues/11168)
|
||||||
|
* **NgTemplateOutlet:** Make NgTemplateOutlet compatible with * syntax ([c0178de](https://github.com/angular/angular/commit/c0178de))
|
||||||
|
* **router:** call resolver when upstream params change ([#12942](https://github.com/angular/angular/issues/12942)) ([d4d3782](https://github.com/angular/angular/commit/d4d3782))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* core: - `IterableChangeRecord` is now an interface and parameterized on `<V>`.
|
||||||
|
This should not be an issue unless your code does
|
||||||
|
`new IterableChangeRecord` which it should not have a reason to do.
|
||||||
|
- `KeyValueChangeRecord` is now an interface and parameterized on `<V>`.
|
||||||
|
This should not be an issue unless your code does
|
||||||
|
`new KeyValueChangeRecord` which it should not have a reason to do.
|
||||||
|
|
||||||
|
### DEPRECATION
|
||||||
|
|
||||||
|
* Deprecate `ngOutletContext`. Use `ngTemplateOutletContext` instead.
|
||||||
|
* `CollectionChangeRecord` is renamed to `IterableChangeRecord`.
|
||||||
|
`CollectionChangeRecord` is aliased to `IterableChangeRecord` and is
|
||||||
|
marked as `@deprecated`. It will be removed in `v6.x.x`.
|
||||||
|
* Deprecate `DefaultIterableDiffer` as it is private class which
|
||||||
|
was erroneously exposed.
|
||||||
|
* Deprecate `KeyValueDiffers#factories` as it is private field which
|
||||||
|
was erroneously exposed.
|
||||||
|
* Deprecate `IterableDiffers#factories` as it is private field which
|
||||||
|
was erroneously exposed.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.4.3"></a>
|
<a name="2.4.3"></a>
|
||||||
## [2.4.3](https://github.com/angular/angular/compare/2.4.2...2.4.3) (2017-01-11)
|
## [2.4.3](https://github.com/angular/angular/compare/2.4.2...2.4.3) (2017-01-11)
|
||||||
|
|
||||||
@ -122,6 +236,45 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="4.0.0-beta.2"></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)
|
||||||
|
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
* **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:** 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)
|
||||||
|
* **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)
|
||||||
|
* **i18n:** parse ICU messages while normalizing templates ([e74d8aa](https://github.com/angular/angular/commit/e74d8aa))
|
||||||
|
|
||||||
|
Note: 4.0.0-beta.2 release also contains all the changes present in the 2.4.2 release.
|
||||||
|
|
||||||
|
|
||||||
|
### 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>
|
||||||
|
# [4.0.0-beta.1](https://github.com/angular/angular/compare/2.4.0-marker...4.0.0-beta.1) (2016-12-22)
|
||||||
|
|
||||||
|
### 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))
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
@ -154,6 +307,25 @@
|
|||||||
* 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)
|
||||||
@ -208,7 +380,6 @@ The >=2.3.1 compiler will issue is the following error if it encounters componen
|
|||||||
|
|
||||||
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,16 +6,29 @@ 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.
|
||||||
|
|
||||||
# Change approvals
|
## The `PR: merge` label and `presubmit-*` branches
|
||||||
|
|
||||||
Change approvals in our monorepo are managed via [pullapprove.com](https://about.pullapprove.com/) and are configured via the `.pullapprove.yaml` file.
|
We have automated the process for merging pull requests into master. Our goal is to minimize the disruption for
|
||||||
|
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.
|
||||||
|
|
||||||
# Merging
|
After this review, the caretaker adds `zomg_admin: do_merge` which is restricted to admins only.
|
||||||
|
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}`.
|
||||||
|
|
||||||
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.
|
(Note: if the automation fails, committers can instead push the commits to a branch following this naming scheme.)
|
||||||
This signals to the caretaker that the PR should be merged.
|
|
||||||
|
|
||||||
# Who is the Caretaker?
|
When a Travis build succeeds for a presubmit branch named following the convention,
|
||||||
|
Travis will re-base the commits, merge to master, and close the PR automatically.
|
||||||
|
|
||||||
See [this explanation](https://twitter.com/IgorMinar/status/799365744806854656).
|
Finally, after merge `mary-poppins` removes the presubmit branch.
|
||||||
|
|
||||||
|
## 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)
|
||||||
|
@ -191,21 +191,46 @@ If the commit reverts a previous commit, it should begin with `revert: `, follow
|
|||||||
### Type
|
### Type
|
||||||
Must be one of the following:
|
Must be one of the following:
|
||||||
|
|
||||||
* **feat**: A new feature
|
|
||||||
* **fix**: A bug fix
|
|
||||||
* **docs**: Documentation only changes
|
|
||||||
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
|
|
||||||
semi-colons, etc)
|
|
||||||
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
|
||||||
* **perf**: A code change that improves performance
|
|
||||||
* **test**: Adding missing tests or correcting existing tests
|
|
||||||
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
||||||
* **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
|
* **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
|
||||||
* **chore**: Other changes that don't modify `src` or `test` files
|
* **docs**: Documentation only changes
|
||||||
|
* **feat**: A new feature
|
||||||
|
* **fix**: A bug fix
|
||||||
|
* **perf**: A code change that improves performance
|
||||||
|
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||||
|
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
|
||||||
|
semi-colons, etc)
|
||||||
|
* **test**: Adding missing tests or correcting existing tests
|
||||||
|
|
||||||
### Scope
|
### Scope
|
||||||
The scope could be anything specifying place of the commit change. For example
|
The scope should be the name of the npm package affected (as perceived by person reading changelog generated from commit messages.
|
||||||
`Compiler`, `ElementInjector`, etc.
|
|
||||||
|
The following is the list of supported scopes:
|
||||||
|
|
||||||
|
* **common**
|
||||||
|
* **compiler**
|
||||||
|
* **compiler-cli**
|
||||||
|
* **core**
|
||||||
|
* **forms**
|
||||||
|
* **http**
|
||||||
|
* **language-service**
|
||||||
|
* **platform-browser**
|
||||||
|
* **platform-browser-dynamic**
|
||||||
|
* **platform-server**
|
||||||
|
* **platform-webworker**
|
||||||
|
* **platform-webworker-dynamic**
|
||||||
|
* **router**
|
||||||
|
* **upgrade**
|
||||||
|
* **tsc-wrapped**
|
||||||
|
|
||||||
|
There is currently few exception to the "use package name" rule:
|
||||||
|
|
||||||
|
* **packaging**: used for changes that change the npm package layout in all of our packages, e.g. public path changes, package.json changes done to all packages, d.ts file/format changes, changes to bundles, etc.
|
||||||
|
* **changelog**: used for updating the release notes in CHANGELOG.md
|
||||||
|
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all packages (e.g. `style: add missing semicolons`)
|
||||||
|
|
||||||
|
|
||||||
|
Packaging
|
||||||
|
|
||||||
### Subject
|
### Subject
|
||||||
The subject contains succinct description of the change:
|
The subject contains succinct description of the change:
|
||||||
|
16
DEVELOPER.md
16
DEVELOPER.md
@ -71,13 +71,7 @@ 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*: globally installing the package `npm-run` by running `npm install -g npm-run`
|
*Option 2*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
|
||||||
(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
|
||||||
@ -161,7 +155,13 @@ You can check that your code is properly formatted and adheres to coding style b
|
|||||||
$ gulp lint
|
$ gulp lint
|
||||||
```
|
```
|
||||||
|
|
||||||
## Publishing your own personal snapshot build
|
## Publishing snapshot builds
|
||||||
|
|
||||||
|
When the `master` branch successfully builds on Travis, it automatically publishes build artifacts
|
||||||
|
to repositories in the Angular org, eg. the `@angular/core` package is published to
|
||||||
|
http://github.com/angular/core-builds.
|
||||||
|
The ES2015 version of Angular is published to a different branch in these repos, for example
|
||||||
|
http://github.com/angular/core-builds#master-es2015
|
||||||
|
|
||||||
You may find that your un-merged change needs some validation from external participants.
|
You may find that your un-merged change needs some validation from external participants.
|
||||||
Rather than requiring them to pull your Pull Request and build Angular locally, you can
|
Rather than requiring them to pull your Pull Request and build Angular locally, you can
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
The MIT License
|
The MIT License
|
||||||
|
|
||||||
Copyright (c) 2014-2017 Google, Inc. http://angular.io
|
Copyright (c) 2014-2016 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
|
||||||
|
12
build.sh
12
build.sh
@ -112,6 +112,7 @@ do
|
|||||||
PWD=`pwd`
|
PWD=`pwd`
|
||||||
SRCDIR=${PWD}/modules/@angular/${PACKAGE}
|
SRCDIR=${PWD}/modules/@angular/${PACKAGE}
|
||||||
DESTDIR=${PWD}/dist/packages-dist/${PACKAGE}
|
DESTDIR=${PWD}/dist/packages-dist/${PACKAGE}
|
||||||
|
ES2015_DESTDIR=${PWD}/dist/packages-dist-es2015/${PACKAGE}
|
||||||
UMD_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.js
|
UMD_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.js
|
||||||
UMD_TESTING_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-testing.umd.js
|
UMD_TESTING_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-testing.umd.js
|
||||||
UMD_STATIC_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-static.umd.js
|
UMD_STATIC_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-static.umd.js
|
||||||
@ -131,6 +132,13 @@ do
|
|||||||
|
|
||||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-build.json ====="
|
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-build.json ====="
|
||||||
$TSC -p ${SRCDIR}/tsconfig-build.json
|
$TSC -p ${SRCDIR}/tsconfig-build.json
|
||||||
|
# ES2015 distro is not ready yet; don't slow down all builds for it
|
||||||
|
# TODO(alexeagle,igorminar): figure out ES2015 story and enable
|
||||||
|
if [[ -n "${EXPERIMENTAL_ES2015_DISTRO}" ]]; then
|
||||||
|
$TSC -p ${SRCDIR}/tsconfig-build.json --target es2015 --outDir ${ES2015_DESTDIR}
|
||||||
|
cp ${SRCDIR}/package.json ${ES2015_DESTDIR}/
|
||||||
|
cp ${PWD}/modules/@angular/README.md ${ES2015_DESTDIR}/
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -e ${SRCDIR}/tsconfig-upgrade.json ]]; then
|
if [[ -e ${SRCDIR}/tsconfig-upgrade.json ]]; then
|
||||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-upgrade.json ====="
|
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-upgrade.json ====="
|
||||||
@ -214,6 +222,10 @@ do
|
|||||||
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_UPGRADE_ES5_MIN_PATH} ${UMD_UPGRADE_ES5_PATH}
|
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_UPGRADE_ES5_MIN_PATH} ${UMD_UPGRADE_ES5_PATH}
|
||||||
fi
|
fi
|
||||||
) 2>&1 | grep -v "as external dependency"
|
) 2>&1 | grep -v "as external dependency"
|
||||||
|
|
||||||
|
if [[ -n "${EXPERIMENTAL_ES2015_DISTRO}" ]]; then
|
||||||
|
cp -prv ${DESTDIR}/bundles ${ES2015_DESTDIR}
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
(
|
(
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
machine:
|
machine:
|
||||||
node:
|
node:
|
||||||
version: 6.9.5
|
version: 6.6.0
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
pre:
|
pre:
|
||||||
- npm install -g npm@3.10.7
|
- 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, 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, benchpress, 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.
|
||||||
|
82
docs/RELEASE_SCHEDULE.md
Normal file
82
docs/RELEASE_SCHEDULE.md
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# Angular Release Schedule
|
||||||
|
|
||||||
|
This document contains historic record of past Angular releases and future release schedule.
|
||||||
|
|
||||||
|
The purpose of this document is to assist coordination among the Angular team, Angular contributors, Angular application teams, and Angular community projects.
|
||||||
|
|
||||||
|
We'll keep this doc up to date when unplanned releases or other schedule changes occur.
|
||||||
|
|
||||||
|
|
||||||
|
## Schedule Caveats and Exceptions
|
||||||
|
|
||||||
|
The dates listed here are approximate – last minute issues, team or community events, etc. can cause us to release a few days sooner or later.
|
||||||
|
|
||||||
|
This page contains only planned and past unplanned releases.
|
||||||
|
Due to serious regressions or other important reasons we reserve the right to release an unplanned patch or minor release.
|
||||||
|
In such case, we'll update this document accordingly.
|
||||||
|
|
||||||
|
The dates past March 2017 are just a guidance and might be adjusted slightly if necessary.
|
||||||
|
|
||||||
|
|
||||||
|
## Tentative Schedule Until March 2017
|
||||||
|
|
||||||
|
<!--
|
||||||
|
The table below is formatted so that it's easy to read and edit in both markdown and rendered html form.
|
||||||
|
|
||||||
|
In order to deal with undesirable line breaks, two special characters are occasionally used:
|
||||||
|
|
||||||
|
- non-breaking hyphen: "‑" http://www.fileformat.info/info/unicode/char/2011/index.htm
|
||||||
|
- non-breaking space: " " http://www.fileformat.info/info/unicode/char/00a0/index.htm
|
||||||
|
|
||||||
|
If you see undesirable wrapping issues in the rendered form, please copy&paste the quoted characters and use them in the table below where needed.
|
||||||
|
-->
|
||||||
|
|
||||||
|
Week Of | Stable Release<br>(@latest npm tag) | Beta/RC Release<br>(@next npm tag) | Note
|
||||||
|
------------- | ----------------------------------- | ---------------------------------- | ---------------------
|
||||||
|
2016-09-14 | 2.0.0 | - | Major Version Release
|
||||||
|
2016-09-21 | 2.0.1 | 2.1.0-beta.0 |
|
||||||
|
2016‑09‑28 | - | - | Angular Connect
|
||||||
|
2016-10-05 | 2.0.2 | 2.1.0-rc.0 |
|
||||||
|
2016-10-12 | 2.1.0 | - | Minor Version Release
|
||||||
|
2016-10-19 | 2.1.1 | 2.2.0-beta.0 |
|
||||||
|
2016-10-26 | 2.1.2 | 2.2.0-beta.1 |
|
||||||
|
2016-11-02 | 2.1.3 | 2.2.0-rc.0 |
|
||||||
|
2016-11-09 | 2.2.0 | - | Minor Version Release
|
||||||
|
2016-11-16 | 2.2.1 | 2.3.0-beta.0 |
|
||||||
|
2016-11-23 | 2.2.2 | 2.3.0-beta.1 |
|
||||||
|
*2016-11-23* | *2.2.3* | - | *Unplanned Patch Release to fix regressions*
|
||||||
|
2016-11-30 | 2.2.4 | 2.3.0-rc.0 |
|
||||||
|
2016-12-07 | 2.3.0 | - | Minor Version Release
|
||||||
|
2016-12-14 | 2.3.1 | 4.0.0-beta.0 |
|
||||||
|
*2016-12-21* | *2.4.0* | - | *Unplanned Minor Release due to release of RxJS 5.0.0*
|
||||||
|
2016-12-21 | 2.4.1 | 4.0.0-beta.1 |
|
||||||
|
2016-12-28 | - | - | Holiday Break
|
||||||
|
2017-01-04 | 2.4.2 | 4.0.0-beta.2 |
|
||||||
|
2017-01-11 | 2.4.3 | 4.0.0-beta.3 |
|
||||||
|
2017-01-18 | 2.4.4 | 4.0.0-beta.4 |
|
||||||
|
2017-01-25 | 2.4.5 | 4.0.0-beta.5 |
|
||||||
|
2017-02-01 | 2.4.6 | 4.0.0-beta.6 |
|
||||||
|
2017-02-08 | 2.4.7 | 4.0.0-rc.0 |
|
||||||
|
2017-02-15 | 2.4.8 | 4.0.0-rc.1 |
|
||||||
|
2017-02-22 | 2.4.9 | 4.0.0-rc.2 |
|
||||||
|
2017-03-01 | 2.4.10 | 4.0.0-rc.3 |
|
||||||
|
2017-03-08 | 4.0.0 + 2.4.11 | - | Major Version Release
|
||||||
|
|
||||||
|
|
||||||
|
## Tentative Schedule After March 2017
|
||||||
|
|
||||||
|
Date | Stable Release | Compatibility`*`
|
||||||
|
---------------------- | -------------- | ----------------
|
||||||
|
September/October 2017 | 5.0.0 | ^4.0.0
|
||||||
|
March 2018 | 6.0.0 | ^5.0.0
|
||||||
|
September/October 2018 | 7.0.0 | ^6.0.0
|
||||||
|
|
||||||
|
`*` The goal of the backwards compatibility promise, is to ensure that changes in the core framework and tooling don't break the existing ecosystem of components and applications and don't put undue upgrade/migration burden on Angular application and component authors.
|
||||||
|
|
||||||
|
|
||||||
|
## More Info & Resources
|
||||||
|
|
||||||
|
In [September 2016 we announced](http://angularjs.blogspot.com/2016/10/versioning-and-releasing-angular.html) that Angular is fully adopting [semantic versioning](http://semver.org/) and that we'll be releasing patch versions on a weekly basis (~50 per year), minor versions monthly for 3 months following a major version release, and every 6 months we'll release a major version that will be backwards compatible with the previous release for most developers, but might remove APIs that have been deprecated two major versions ago (6 or more months ago).
|
||||||
|
|
||||||
|
In [December 2016 we clarified this message](http://angularjs.blogspot.com/2016/12/ok-let-me-explain-its-going-to-be.html), and provided additional details about the plans to release Angular 4.0.0 in March 2017.
|
||||||
|
This document contains updates to the schedule that happened since then.
|
38
gulpfile.js
38
gulpfile.js
@ -11,8 +11,8 @@
|
|||||||
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
|
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
|
||||||
// This is to ensure that we catch env issues before we error while requiring other dependencies.
|
// This is to ensure that we catch env issues before we error while requiring other dependencies.
|
||||||
require('./tools/check-environment')({
|
require('./tools/check-environment')({
|
||||||
requiredNpmVersion: '>=3.10.7 <4.0.0',
|
requiredNpmVersion: '>=3.5.3 <4.0.0',
|
||||||
requiredNodeVersion: '>=6.9.5 <7.0.0',
|
requiredNodeVersion: '>=5.4.1 <7.0.0',
|
||||||
});
|
});
|
||||||
|
|
||||||
const gulp = require('gulp');
|
const gulp = require('gulp');
|
||||||
@ -155,6 +155,40 @@ gulp.task('lint', ['format:enforce', 'tools:build'], () => {
|
|||||||
.pipe(tslint.report({emitError: true}));
|
.pipe(tslint.report({emitError: true}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gulp.task('validate-commit-messages', () => {
|
||||||
|
const validateCommitMessage = require('./tools/validate-commit-message');
|
||||||
|
const childProcess = require('child_process');
|
||||||
|
|
||||||
|
// We need to fetch origin explicitly because it might be stale.
|
||||||
|
// I couldn't find a reliable way to do this without fetch.
|
||||||
|
childProcess.exec(
|
||||||
|
'git fetch origin master && git log --reverse --format=%s HEAD ^origin/master',
|
||||||
|
(error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
console.log(stderr);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let someCommitsInvalid = false;
|
||||||
|
let commitsByLine = stdout.trim().split(/\n/);
|
||||||
|
|
||||||
|
console.log(`Examining ${commitsByLine.length} commits between HEAD and master`);
|
||||||
|
|
||||||
|
if (commitsByLine.length == 0) {
|
||||||
|
console.log('There are zero new commits between this HEAD and master');
|
||||||
|
}
|
||||||
|
|
||||||
|
someCommitsInvalid = !commitsByLine.every(validateCommitMessage);
|
||||||
|
|
||||||
|
if (someCommitsInvalid) {
|
||||||
|
console.log('Please fix the failing commit messages before continuing...');
|
||||||
|
console.log(
|
||||||
|
'Commit message guidelines: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-guidelines');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task('tools:build', (done) => { tsc('tools/', done); });
|
gulp.task('tools:build', (done) => { tsc('tools/', done); });
|
||||||
|
|
||||||
// Check for circular dependency in the source code
|
// Check for circular dependency in the source code
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
// Must be imported first, because angular2 decorators throws on load.
|
// Must be imported first, because angular2 decorators throws on load.
|
||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
|
|
||||||
export {Injector, OpaqueToken, Provider, ReflectiveInjector} from '@angular/core';
|
export {InjectionToken, Injector, Provider, ReflectiveInjector} from '@angular/core';
|
||||||
export {Options} from './src/common_options';
|
export {Options} from './src/common_options';
|
||||||
export {MeasureValues} from './src/measure_values';
|
export {MeasureValues} from './src/measure_values';
|
||||||
export {Metric} from './src/metric';
|
export {Metric} from './src/metric';
|
||||||
|
@ -6,26 +6,26 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {OpaqueToken} from '@angular/core';
|
import {InjectionToken} from '@angular/core';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
|
||||||
export class Options {
|
export class Options {
|
||||||
static SAMPLE_ID = new OpaqueToken('Options.sampleId');
|
static SAMPLE_ID = new InjectionToken('Options.sampleId');
|
||||||
static DEFAULT_DESCRIPTION = new OpaqueToken('Options.defaultDescription');
|
static DEFAULT_DESCRIPTION = new InjectionToken('Options.defaultDescription');
|
||||||
static SAMPLE_DESCRIPTION = new OpaqueToken('Options.sampleDescription');
|
static SAMPLE_DESCRIPTION = new InjectionToken('Options.sampleDescription');
|
||||||
static FORCE_GC = new OpaqueToken('Options.forceGc');
|
static FORCE_GC = new InjectionToken('Options.forceGc');
|
||||||
static NO_PREPARE = () => true;
|
static NO_PREPARE = () => true;
|
||||||
static PREPARE = new OpaqueToken('Options.prepare');
|
static PREPARE = new InjectionToken('Options.prepare');
|
||||||
static EXECUTE = new OpaqueToken('Options.execute');
|
static EXECUTE = new InjectionToken('Options.execute');
|
||||||
static CAPABILITIES = new OpaqueToken('Options.capabilities');
|
static CAPABILITIES = new InjectionToken('Options.capabilities');
|
||||||
static USER_AGENT = new OpaqueToken('Options.userAgent');
|
static USER_AGENT = new InjectionToken('Options.userAgent');
|
||||||
static MICRO_METRICS = new OpaqueToken('Options.microMetrics');
|
static MICRO_METRICS = new InjectionToken('Options.microMetrics');
|
||||||
static USER_METRICS = new OpaqueToken('Options.userMetrics');
|
static USER_METRICS = new InjectionToken('Options.userMetrics');
|
||||||
static NOW = new OpaqueToken('Options.now');
|
static NOW = new InjectionToken('Options.now');
|
||||||
static WRITE_FILE = new OpaqueToken('Options.writeFile');
|
static WRITE_FILE = new InjectionToken('Options.writeFile');
|
||||||
static RECEIVED_DATA = new OpaqueToken('Options.receivedData');
|
static RECEIVED_DATA = new InjectionToken('Options.receivedData');
|
||||||
static REQUEST_COUNT = new OpaqueToken('Options.requestCount');
|
static REQUEST_COUNT = new InjectionToken('Options.requestCount');
|
||||||
static CAPTURE_FRAMES = new OpaqueToken('Options.frameCapture');
|
static CAPTURE_FRAMES = new InjectionToken('Options.frameCapture');
|
||||||
static DEFAULT_PROVIDERS = [
|
static DEFAULT_PROVIDERS = [
|
||||||
{provide: Options.DEFAULT_DESCRIPTION, useValue: {}},
|
{provide: Options.DEFAULT_DESCRIPTION, useValue: {}},
|
||||||
{provide: Options.SAMPLE_DESCRIPTION, useValue: {}},
|
{provide: Options.SAMPLE_DESCRIPTION, useValue: {}},
|
||||||
|
@ -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 {Injector, OpaqueToken} from '@angular/core';
|
import {InjectionToken, Injector} from '@angular/core';
|
||||||
|
|
||||||
import {Metric} from '../metric';
|
import {Metric} from '../metric';
|
||||||
|
|
||||||
@ -60,4 +60,4 @@ function mergeStringMaps(maps: {[key: string]: string}[]): {[key: string]: strin
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const _CHILDREN = new OpaqueToken('MultiMetric.children');
|
const _CHILDREN = new InjectionToken('MultiMetric.children');
|
||||||
|
@ -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 {Inject, Injectable, OpaqueToken} from '@angular/core';
|
import {Inject, Injectable, InjectionToken} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from '../common_options';
|
import {Options} from '../common_options';
|
||||||
import {Metric} from '../metric';
|
import {Metric} from '../metric';
|
||||||
@ -18,7 +18,7 @@ import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_e
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PerflogMetric extends Metric {
|
export class PerflogMetric extends Metric {
|
||||||
static SET_TIMEOUT = new OpaqueToken('PerflogMetric.setTimeout');
|
static SET_TIMEOUT = new InjectionToken('PerflogMetric.setTimeout');
|
||||||
static PROVIDERS = [
|
static PROVIDERS = [
|
||||||
PerflogMetric, {
|
PerflogMetric, {
|
||||||
provide: PerflogMetric.SET_TIMEOUT,
|
provide: PerflogMetric.SET_TIMEOUT,
|
||||||
|
@ -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 {Inject, Injectable, OpaqueToken} from '@angular/core';
|
import {Inject, Injectable, InjectionToken} from '@angular/core';
|
||||||
import {print} from '../facade/lang';
|
import {print} from '../facade/lang';
|
||||||
import {MeasureValues} from '../measure_values';
|
import {MeasureValues} from '../measure_values';
|
||||||
import {Reporter} from '../reporter';
|
import {Reporter} from '../reporter';
|
||||||
@ -20,8 +20,8 @@ import {formatNum, formatStats, sortedProps} from './util';
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ConsoleReporter extends Reporter {
|
export class ConsoleReporter extends Reporter {
|
||||||
static PRINT = new OpaqueToken('ConsoleReporter.print');
|
static PRINT = new InjectionToken('ConsoleReporter.print');
|
||||||
static COLUMN_WIDTH = new OpaqueToken('ConsoleReporter.columnWidth');
|
static COLUMN_WIDTH = new InjectionToken('ConsoleReporter.columnWidth');
|
||||||
static PROVIDERS = [
|
static PROVIDERS = [
|
||||||
ConsoleReporter, {provide: ConsoleReporter.COLUMN_WIDTH, useValue: 18},
|
ConsoleReporter, {provide: ConsoleReporter.COLUMN_WIDTH, useValue: 18},
|
||||||
{provide: ConsoleReporter.PRINT, useValue: print}
|
{provide: ConsoleReporter.PRINT, useValue: print}
|
||||||
|
@ -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 {Inject, Injectable, OpaqueToken} from '@angular/core';
|
import {Inject, Injectable, InjectionToken} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from '../common_options';
|
import {Options} from '../common_options';
|
||||||
import {MeasureValues} from '../measure_values';
|
import {MeasureValues} from '../measure_values';
|
||||||
@ -21,7 +21,7 @@ import {formatStats, sortedProps} from './util';
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class JsonFileReporter extends Reporter {
|
export class JsonFileReporter extends Reporter {
|
||||||
static PATH = new OpaqueToken('JsonFileReporter.path');
|
static PATH = new InjectionToken('JsonFileReporter.path');
|
||||||
static PROVIDERS = [JsonFileReporter, {provide: JsonFileReporter.PATH, useValue: '.'}];
|
static PROVIDERS = [JsonFileReporter, {provide: JsonFileReporter.PATH, useValue: '.'}];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -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 {Injector, OpaqueToken} from '@angular/core';
|
import {InjectionToken, Injector} from '@angular/core';
|
||||||
|
|
||||||
import {MeasureValues} from '../measure_values';
|
import {MeasureValues} from '../measure_values';
|
||||||
import {Reporter} from '../reporter';
|
import {Reporter} from '../reporter';
|
||||||
@ -39,4 +39,4 @@ export class MultiReporter extends Reporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const _CHILDREN = new OpaqueToken('MultiReporter.children');
|
const _CHILDREN = new InjectionToken('MultiReporter.children');
|
||||||
|
@ -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 {OpaqueToken} from '@angular/core';
|
import {InjectionToken} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from './common_options';
|
import {Options} from './common_options';
|
||||||
import {Metric} from './metric';
|
import {Metric} from './metric';
|
||||||
|
@ -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 {Inject, Injectable, OpaqueToken} from '@angular/core';
|
import {Inject, Injectable, InjectionToken} from '@angular/core';
|
||||||
|
|
||||||
import {MeasureValues} from '../measure_values';
|
import {MeasureValues} from '../measure_values';
|
||||||
import {Statistic} from '../statistic';
|
import {Statistic} from '../statistic';
|
||||||
@ -18,8 +18,8 @@ import {Validator} from '../validator';
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RegressionSlopeValidator extends Validator {
|
export class RegressionSlopeValidator extends Validator {
|
||||||
static SAMPLE_SIZE = new OpaqueToken('RegressionSlopeValidator.sampleSize');
|
static SAMPLE_SIZE = new InjectionToken('RegressionSlopeValidator.sampleSize');
|
||||||
static METRIC = new OpaqueToken('RegressionSlopeValidator.metric');
|
static METRIC = new InjectionToken('RegressionSlopeValidator.metric');
|
||||||
static PROVIDERS = [
|
static PROVIDERS = [
|
||||||
RegressionSlopeValidator, {provide: RegressionSlopeValidator.SAMPLE_SIZE, useValue: 10},
|
RegressionSlopeValidator, {provide: RegressionSlopeValidator.SAMPLE_SIZE, useValue: 10},
|
||||||
{provide: RegressionSlopeValidator.METRIC, useValue: 'scriptTime'}
|
{provide: RegressionSlopeValidator.METRIC, useValue: 'scriptTime'}
|
||||||
|
@ -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 {Inject, Injectable, OpaqueToken} from '@angular/core';
|
import {Inject, Injectable, InjectionToken} from '@angular/core';
|
||||||
|
|
||||||
import {MeasureValues} from '../measure_values';
|
import {MeasureValues} from '../measure_values';
|
||||||
import {Validator} from '../validator';
|
import {Validator} from '../validator';
|
||||||
@ -16,7 +16,7 @@ import {Validator} from '../validator';
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SizeValidator extends Validator {
|
export class SizeValidator extends Validator {
|
||||||
static SAMPLE_SIZE = new OpaqueToken('SizeValidator.sampleSize');
|
static SAMPLE_SIZE = new InjectionToken('SizeValidator.sampleSize');
|
||||||
static PROVIDERS = [SizeValidator, {provide: SizeValidator.SAMPLE_SIZE, useValue: 10}];
|
static PROVIDERS = [SizeValidator, {provide: SizeValidator.SAMPLE_SIZE, useValue: 10}];
|
||||||
|
|
||||||
constructor(@Inject(SizeValidator.SAMPLE_SIZE) private _sampleSize: number) { super(); }
|
constructor(@Inject(SizeValidator.SAMPLE_SIZE) private _sampleSize: number) { super(); }
|
||||||
|
@ -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 {Injector, OpaqueToken} from '@angular/core';
|
import {InjectionToken, Injector} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from './common_options';
|
import {Options} from './common_options';
|
||||||
|
|
||||||
@ -101,4 +101,4 @@ export class PerfLogFeatures {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const _CHILDREN = new OpaqueToken('WebDriverExtension.children');
|
const _CHILDREN = new InjectionToken('WebDriverExtension.children');
|
||||||
|
@ -12,9 +12,8 @@
|
|||||||
* 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 {NgLocalization} from './localization';
|
export {NgLocaleLocalization, 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, NgComponentOutlet} from './directives/index';
|
||||||
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe} from './pipes/index';
|
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe, TitleCasePipe} from './pipes/index';
|
||||||
export {VERSION} from './version';
|
export {VERSION} from './version';
|
||||||
export {Version} from '@angular/core';
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
import {Provider} from '@angular/core';
|
import {Provider} from '@angular/core';
|
||||||
|
|
||||||
import {NgClass} from './ng_class';
|
import {NgClass} from './ng_class';
|
||||||
|
import {NgComponentOutlet} from './ng_component_outlet';
|
||||||
import {NgFor} from './ng_for';
|
import {NgFor} from './ng_for';
|
||||||
import {NgIf} from './ng_if';
|
import {NgIf} from './ng_if';
|
||||||
import {NgPlural, NgPluralCase} from './ng_plural';
|
import {NgPlural, NgPluralCase} from './ng_plural';
|
||||||
@ -18,6 +19,7 @@ import {NgTemplateOutlet} from './ng_template_outlet';
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
NgClass,
|
NgClass,
|
||||||
|
NgComponentOutlet,
|
||||||
NgFor,
|
NgFor,
|
||||||
NgIf,
|
NgIf,
|
||||||
NgPlural,
|
NgPlural,
|
||||||
@ -29,12 +31,14 @@ export {
|
|||||||
NgTemplateOutlet
|
NgTemplateOutlet
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A collection of Angular directives that are likely to be used in each and every Angular
|
* A collection of Angular directives that are likely to be used in each and every Angular
|
||||||
* application.
|
* application.
|
||||||
*/
|
*/
|
||||||
export const COMMON_DIRECTIVES: Provider[] = [
|
export const COMMON_DIRECTIVES: Provider[] = [
|
||||||
NgClass,
|
NgClass,
|
||||||
|
NgComponentOutlet,
|
||||||
NgFor,
|
NgFor,
|
||||||
NgIf,
|
NgIf,
|
||||||
NgTemplateOutlet,
|
NgTemplateOutlet,
|
||||||
|
@ -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 {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
import {Directive, DoCheck, ElementRef, Input, IterableChanges, IterableDiffer, IterableDiffers, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||||
|
|
||||||
import {isListLikeIterable} from '../facade/collection';
|
import {isListLikeIterable} from '../facade/collection';
|
||||||
import {stringify} from '../facade/lang';
|
import {stringify} from '../facade/lang';
|
||||||
@ -41,8 +41,8 @@ import {stringify} from '../facade/lang';
|
|||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngClass]'})
|
@Directive({selector: '[ngClass]'})
|
||||||
export class NgClass implements DoCheck {
|
export class NgClass implements DoCheck {
|
||||||
private _iterableDiffer: IterableDiffer;
|
private _iterableDiffer: IterableDiffer<string>;
|
||||||
private _keyValueDiffer: KeyValueDiffer;
|
private _keyValueDiffer: KeyValueDiffer<string, any>;
|
||||||
private _initialClasses: string[] = [];
|
private _initialClasses: string[] = [];
|
||||||
private _rawClass: string[]|Set<string>|{[klass: string]: any};
|
private _rawClass: string[]|Set<string>|{[klass: string]: any};
|
||||||
|
|
||||||
@ -78,39 +78,35 @@ export class NgClass implements DoCheck {
|
|||||||
|
|
||||||
ngDoCheck(): void {
|
ngDoCheck(): void {
|
||||||
if (this._iterableDiffer) {
|
if (this._iterableDiffer) {
|
||||||
const changes = this._iterableDiffer.diff(this._rawClass);
|
const iterableChanges = this._iterableDiffer.diff(this._rawClass as string[]);
|
||||||
if (changes) {
|
if (iterableChanges) {
|
||||||
this._applyIterableChanges(changes);
|
this._applyIterableChanges(iterableChanges);
|
||||||
}
|
}
|
||||||
} else if (this._keyValueDiffer) {
|
} else if (this._keyValueDiffer) {
|
||||||
const changes = this._keyValueDiffer.diff(this._rawClass);
|
const keyValueChanges = this._keyValueDiffer.diff(this._rawClass as{[k: string]: any});
|
||||||
if (changes) {
|
if (keyValueChanges) {
|
||||||
this._applyKeyValueChanges(changes);
|
this._applyKeyValueChanges(keyValueChanges);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _cleanupClasses(rawClassVal: string[]|Set<string>|{[klass: string]: any}): void {
|
private _cleanupClasses(rawClassVal: string[]|{[klass: string]: any}): void {
|
||||||
this._applyClasses(rawClassVal, true);
|
this._applyClasses(rawClassVal, true);
|
||||||
this._applyInitialClasses(false);
|
this._applyInitialClasses(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyKeyValueChanges(changes: any): void {
|
private _applyKeyValueChanges(changes: KeyValueChanges<string, any>): void {
|
||||||
changes.forEachAddedItem(
|
changes.forEachAddedItem((record) => this._toggleClass(record.key, record.currentValue));
|
||||||
(record: KeyValueChangeRecord) => this._toggleClass(record.key, record.currentValue));
|
changes.forEachChangedItem((record) => this._toggleClass(record.key, record.currentValue));
|
||||||
|
changes.forEachRemovedItem((record) => {
|
||||||
changes.forEachChangedItem(
|
|
||||||
(record: KeyValueChangeRecord) => this._toggleClass(record.key, record.currentValue));
|
|
||||||
|
|
||||||
changes.forEachRemovedItem((record: KeyValueChangeRecord) => {
|
|
||||||
if (record.previousValue) {
|
if (record.previousValue) {
|
||||||
this._toggleClass(record.key, false);
|
this._toggleClass(record.key, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyIterableChanges(changes: any): void {
|
private _applyIterableChanges(changes: IterableChanges<string>): void {
|
||||||
changes.forEachAddedItem((record: CollectionChangeRecord) => {
|
changes.forEachAddedItem((record) => {
|
||||||
if (typeof record.item === 'string') {
|
if (typeof record.item === 'string') {
|
||||||
this._toggleClass(record.item, true);
|
this._toggleClass(record.item, true);
|
||||||
} else {
|
} else {
|
||||||
@ -119,8 +115,7 @@ export class NgClass implements DoCheck {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
changes.forEachRemovedItem(
|
changes.forEachRemovedItem((record) => this._toggleClass(record.item, false));
|
||||||
(record: CollectionChangeRecord) => this._toggleClass(record.item, false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyInitialClasses(isCleanup: boolean) {
|
private _applyInitialClasses(isCleanup: boolean) {
|
||||||
@ -128,7 +123,7 @@ export class NgClass implements DoCheck {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _applyClasses(
|
private _applyClasses(
|
||||||
rawClassVal: string[]|Set<string>|{[key: string]: any}, isCleanup: boolean) {
|
rawClassVal: string[]|Set<string>|{[klass: string]: any}, isCleanup: boolean) {
|
||||||
if (rawClassVal) {
|
if (rawClassVal) {
|
||||||
if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) {
|
if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) {
|
||||||
(<any>rawClassVal).forEach((klass: string) => this._toggleClass(klass, !isCleanup));
|
(<any>rawClassVal).forEach((klass: string) => this._toggleClass(klass, !isCleanup));
|
||||||
@ -140,11 +135,11 @@ export class NgClass implements DoCheck {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleClass(klass: string, enabled: boolean): void {
|
private _toggleClass(klass: string, enabled: any): void {
|
||||||
klass = klass.trim();
|
klass = klass.trim();
|
||||||
if (klass) {
|
if (klass) {
|
||||||
klass.split(/\s+/g).forEach(
|
klass.split(/\s+/g).forEach(
|
||||||
klass => { this._renderer.setElementClass(this._ngEl.nativeElement, klass, enabled); });
|
klass => { this._renderer.setElementClass(this._ngEl.nativeElement, klass, !!enabled); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* @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 {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, OnChanges, Provider, SimpleChanges, Type, ViewContainerRef} from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a single {@link Component} type and inserts its Host View into current View.
|
||||||
|
* `NgComponentOutlet` provides a declarative approach for dynamic component creation.
|
||||||
|
*
|
||||||
|
* `NgComponentOutlet` requires a component type, if a falsy value is set the view will clear and
|
||||||
|
* any existing component will get destroyed.
|
||||||
|
*
|
||||||
|
* ### Fine tune control
|
||||||
|
*
|
||||||
|
* You can control the component creation process by using the following optional attributes:
|
||||||
|
*
|
||||||
|
* * `ngOutletInjector`: Optional custom {@link Injector} that will be used as parent for the
|
||||||
|
* Component.
|
||||||
|
* Defaults to the injector of the current view container.
|
||||||
|
*
|
||||||
|
* * `ngOutletProviders`: Optional injectable objects ({@link Provider}) that are visible to the
|
||||||
|
* component.
|
||||||
|
*
|
||||||
|
* * `ngOutletContent`: Optional list of projectable nodes to insert into the content
|
||||||
|
* section of the component, if exists. ({@link NgContent}).
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ### Syntax
|
||||||
|
*
|
||||||
|
* Simple
|
||||||
|
* ```
|
||||||
|
* <ng-container *ngComponentOutlet="componentTypeExpression"></ng-container>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Customized
|
||||||
|
* ```
|
||||||
|
* <ng-container *ngComponentOutlet="componentTypeExpression;
|
||||||
|
* injector: injectorExpression;
|
||||||
|
* content: contentNodesExpression">
|
||||||
|
* </ng-container>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* # Example
|
||||||
|
*
|
||||||
|
* {@example common/ngComponentOutlet/ts/module.ts region='SimpleExample'}
|
||||||
|
*
|
||||||
|
* A more complete example with additional options:
|
||||||
|
*
|
||||||
|
* {@example common/ngComponentOutlet/ts/module.ts region='CompleteExample'}
|
||||||
|
*
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
@Directive({selector: '[ngComponentOutlet]'})
|
||||||
|
export class NgComponentOutlet implements OnChanges {
|
||||||
|
@Input() ngComponentOutlet: Type<any>;
|
||||||
|
@Input() ngComponentOutletInjector: Injector;
|
||||||
|
@Input() ngComponentOutletContent: any[][];
|
||||||
|
|
||||||
|
componentRef: ComponentRef<any>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private _cmpFactoryResolver: ComponentFactoryResolver,
|
||||||
|
private _viewContainerRef: ViewContainerRef) {}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
if (this.componentRef) {
|
||||||
|
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this.componentRef.hostView));
|
||||||
|
}
|
||||||
|
this._viewContainerRef.clear();
|
||||||
|
this.componentRef = null;
|
||||||
|
|
||||||
|
if (this.ngComponentOutlet) {
|
||||||
|
let injector = this.ngComponentOutletInjector || this._viewContainerRef.parentInjector;
|
||||||
|
|
||||||
|
this.componentRef = this._viewContainerRef.createComponent(
|
||||||
|
this._cmpFactoryResolver.resolveComponentFactory(this.ngComponentOutlet),
|
||||||
|
this._viewContainerRef.length, injector, this.ngComponentOutletContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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 {ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, Directive, DoCheck, EmbeddedViewRef, Input, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef, isDevMode} from '@angular/core';
|
import {ChangeDetectorRef, Directive, DoCheck, EmbeddedViewRef, Input, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef, isDevMode} from '@angular/core';
|
||||||
|
|
||||||
import {getTypeNameForDebugging} from '../facade/lang';
|
import {getTypeNameForDebugging} from '../facade/lang';
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ export class NgFor implements DoCheck, OnChanges {
|
|||||||
|
|
||||||
get ngForTrackBy(): TrackByFn { return this._trackByFn; }
|
get ngForTrackBy(): TrackByFn { return this._trackByFn; }
|
||||||
|
|
||||||
private _differ: IterableDiffer = null;
|
private _differ: IterableDiffer<any> = null;
|
||||||
private _trackByFn: TrackByFn;
|
private _trackByFn: TrackByFn;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -140,10 +140,10 @@ export class NgFor implements DoCheck, OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyChanges(changes: DefaultIterableDiffer) {
|
private _applyChanges(changes: IterableChanges<any>) {
|
||||||
const insertTuples: RecordViewTuple[] = [];
|
const insertTuples: RecordViewTuple[] = [];
|
||||||
changes.forEachOperation(
|
changes.forEachOperation(
|
||||||
(item: CollectionChangeRecord, adjustedPreviousIndex: number, currentIndex: number) => {
|
(item: IterableChangeRecord<any>, adjustedPreviousIndex: number, currentIndex: number) => {
|
||||||
if (item.previousIndex == null) {
|
if (item.previousIndex == null) {
|
||||||
const view = this._viewContainer.createEmbeddedView(
|
const view = this._viewContainer.createEmbeddedView(
|
||||||
this._template, new NgForRow(null, null, null), currentIndex);
|
this._template, new NgForRow(null, null, null), currentIndex);
|
||||||
@ -175,7 +175,7 @@ export class NgFor implements DoCheck, OnChanges {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _perViewChange(view: EmbeddedViewRef<NgForRow>, record: CollectionChangeRecord) {
|
private _perViewChange(view: EmbeddedViewRef<NgForRow>, record: IterableChangeRecord<any>) {
|
||||||
view.context.$implicit = record.item;
|
view.context.$implicit = record.item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,46 +6,152 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes or recreates a portion of the DOM tree based on an {expression}.
|
* Conditionally includes a template based on the value of an `expression`.
|
||||||
*
|
*
|
||||||
* If the expression assigned to `ngIf` evaluates to a falsy value then the element
|
* `ngIf` evaluates the `expression` and then renders the `then` or `else` template in its place
|
||||||
* is removed from the DOM, otherwise a clone of the element is reinserted into the DOM.
|
* when expression is truthy or falsy respectively. Typically the:
|
||||||
|
* - `then` template is the inline template of `ngIf` unless bound to a different value.
|
||||||
|
* - `else` template is blank unless it is bound.
|
||||||
*
|
*
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/fe0kgemFBtmQOY31b4tw?p=preview)):
|
* # Most common usage
|
||||||
|
*
|
||||||
|
* 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:
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* <div *ngIf="errorCount > 0" class="error">
|
* Hello {{ (userStream|async)?.last }}, {{ (userStream|async)?.first }}!
|
||||||
* <!-- 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 _hasView = false;
|
private _context: NgIfContext = new NgIfContext();
|
||||||
|
private _thenTemplateRef: TemplateRef<NgIfContext> = null;
|
||||||
|
private _elseTemplateRef: TemplateRef<NgIfContext> = null;
|
||||||
|
private _thenViewRef: EmbeddedViewRef<NgIfContext> = null;
|
||||||
|
private _elseViewRef: EmbeddedViewRef<NgIfContext> = null;
|
||||||
|
|
||||||
constructor(private _viewContainer: ViewContainerRef, private _template: TemplateRef<Object>) {}
|
constructor(private _viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext>) {
|
||||||
|
this._thenTemplateRef = templateRef;
|
||||||
|
}
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
set ngIf(condition: any) {
|
set ngIf(condition: any) {
|
||||||
if (condition && !this._hasView) {
|
this._context.$implicit = condition;
|
||||||
this._hasView = true;
|
this._updateView();
|
||||||
this._viewContainer.createEmbeddedView(this._template);
|
}
|
||||||
} else if (!condition && this._hasView) {
|
|
||||||
this._hasView = false;
|
@Input()
|
||||||
|
set ngIfThen(templateRef: TemplateRef<NgIfContext>) {
|
||||||
|
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._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; }
|
||||||
|
@ -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 {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
import {Directive, DoCheck, ElementRef, Input, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngModule CommonModule
|
* @ngModule CommonModule
|
||||||
@ -33,7 +33,7 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDif
|
|||||||
@Directive({selector: '[ngStyle]'})
|
@Directive({selector: '[ngStyle]'})
|
||||||
export class NgStyle implements DoCheck {
|
export class NgStyle implements DoCheck {
|
||||||
private _ngStyle: {[key: string]: string};
|
private _ngStyle: {[key: string]: string};
|
||||||
private _differ: KeyValueDiffer;
|
private _differ: KeyValueDiffer<string, string|number>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}
|
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||||
@ -55,20 +55,16 @@ export class NgStyle implements DoCheck {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyChanges(changes: any): void {
|
private _applyChanges(changes: KeyValueChanges<string, string|number>): void {
|
||||||
changes.forEachRemovedItem((record: KeyValueChangeRecord) => this._setStyle(record.key, null));
|
changes.forEachRemovedItem((record) => this._setStyle(record.key, null));
|
||||||
|
changes.forEachAddedItem((record) => this._setStyle(record.key, record.currentValue));
|
||||||
changes.forEachAddedItem(
|
changes.forEachChangedItem((record) => this._setStyle(record.key, record.currentValue));
|
||||||
(record: KeyValueChangeRecord) => this._setStyle(record.key, record.currentValue));
|
|
||||||
|
|
||||||
changes.forEachChangedItem(
|
|
||||||
(record: KeyValueChangeRecord) => this._setStyle(record.key, record.currentValue));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setStyle(nameAndUnit: string, value: string): void {
|
private _setStyle(nameAndUnit: string, value: string|number): void {
|
||||||
const [name, unit] = nameAndUnit.split('.');
|
const [name, unit] = nameAndUnit.split('.');
|
||||||
value = value && unit ? `${value}${unit}` : value;
|
value = value != null && unit ? `${value}${unit}` : value;
|
||||||
|
|
||||||
this._renderer.setElementStyle(this._ngEl.nativeElement, name, value);
|
this._renderer.setElementStyle(this._ngEl.nativeElement, name, value as string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,42 +15,47 @@ import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChanges, TemplateRef
|
|||||||
*
|
*
|
||||||
* @howToUse
|
* @howToUse
|
||||||
* ```
|
* ```
|
||||||
* <template [ngTemplateOutlet]="templateRefExpression"
|
* <ng-container *ngTemplateOutlet="templateRefExp; context: contextExp"></ng-container>
|
||||||
* [ngOutletContext]="objectExpression">
|
|
||||||
* </template>
|
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* You can attach a context object to the `EmbeddedViewRef` by setting `[ngOutletContext]`.
|
* You can attach a context object to the `EmbeddedViewRef` by setting `[ngTemplateOutletContext]`.
|
||||||
* `[ngOutletContext]` should be an object, the object's keys will be the local template variables
|
* `[ngTemplateOutletContext]` should be an object, the object's keys will be available for binding
|
||||||
* available within the `TemplateRef`.
|
* by the local template `let` declarations.
|
||||||
*
|
*
|
||||||
* Note: using the key `$implicit` in the context object will set it's value as default.
|
* Note: using the key `$implicit` in the context object will set it's value as default.
|
||||||
*
|
*
|
||||||
|
* # Example
|
||||||
|
*
|
||||||
|
* {@example common/ngTemplateOutlet/ts/module.ts region='NgTemplateOutlet'}
|
||||||
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngTemplateOutlet]'})
|
@Directive({selector: '[ngTemplateOutlet]'})
|
||||||
export class NgTemplateOutlet implements OnChanges {
|
export class NgTemplateOutlet implements OnChanges {
|
||||||
private _viewRef: EmbeddedViewRef<any>;
|
private _viewRef: EmbeddedViewRef<any>;
|
||||||
private _context: Object;
|
|
||||||
private _templateRef: TemplateRef<any>;
|
@Input() public ngTemplateOutletContext: Object;
|
||||||
|
|
||||||
|
@Input() public ngTemplateOutlet: TemplateRef<any>;
|
||||||
|
|
||||||
constructor(private _viewContainerRef: ViewContainerRef) {}
|
constructor(private _viewContainerRef: ViewContainerRef) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated v4.0.0 - Renamed to ngTemplateOutletContext.
|
||||||
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
set ngOutletContext(context: Object) { this._context = context; }
|
set ngOutletContext(context: Object) { this.ngTemplateOutletContext = context; }
|
||||||
|
|
||||||
@Input()
|
|
||||||
set ngTemplateOutlet(templateRef: TemplateRef<Object>) { this._templateRef = templateRef; }
|
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
if (this._viewRef) {
|
if (this._viewRef) {
|
||||||
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
|
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._templateRef) {
|
if (this.ngTemplateOutlet) {
|
||||||
this._viewRef = this._viewContainerRef.createEmbeddedView(this._templateRef, this._context);
|
this._viewRef = this._viewContainerRef.createEmbeddedView(
|
||||||
|
this.ngTemplateOutlet, this.ngTemplateOutletContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,10 +49,10 @@ export function getPluralCategory(
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class NgLocaleLocalization extends NgLocalization {
|
export class NgLocaleLocalization extends NgLocalization {
|
||||||
constructor(@Inject(LOCALE_ID) private _locale: string) { super(); }
|
constructor(@Inject(LOCALE_ID) protected 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:
|
||||||
|
@ -10,12 +10,6 @@ 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.
|
||||||
@ -128,7 +122,7 @@ export class Location {
|
|||||||
* Subscribe to the platform's `popState` events.
|
* Subscribe to the platform's `popState` events.
|
||||||
*/
|
*/
|
||||||
subscribe(
|
subscribe(
|
||||||
onNext: (value: PopStateEvent) => void, onThrow: (exception: any) => void = null,
|
onNext: (value: any) => 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,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {OpaqueToken} from '@angular/core';
|
import {InjectionToken} from '@angular/core';
|
||||||
import {LocationChangeListener} from './platform_location';
|
import {LocationChangeListener} from './platform_location';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,4 +61,4 @@ export abstract class LocationStrategy {
|
|||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export const APP_BASE_HREF: OpaqueToken = new OpaqueToken('appBaseHref');
|
export const APP_BASE_HREF = new InjectionToken<string>('appBaseHref');
|
||||||
|
@ -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 {OpaqueToken} from '@angular/core';
|
|
||||||
/**
|
/**
|
||||||
* This class should not be used directly by an application developer. Instead, use
|
* This class should not be used directly by an application developer. Instead, use
|
||||||
* {@link Location}.
|
* {@link Location}.
|
||||||
@ -51,12 +50,6 @@ export abstract class PlatformLocation {
|
|||||||
abstract back(): void;
|
abstract back(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @whatItDoes indicates when a location is initialized
|
|
||||||
* @experimental
|
|
||||||
*/
|
|
||||||
export const LOCATION_INITIALIZED = new OpaqueToken('Location Initialized');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A serializable version of the event from onPopState or onHashChange
|
* A serializable version of the event from onPopState or onHashChange
|
||||||
*
|
*
|
||||||
|
@ -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, PipeTransform, WrappedValue} from '@angular/core';
|
import {ChangeDetectorRef, OnDestroy, Pipe, WrappedValue} from '@angular/core';
|
||||||
import {EventEmitter, Observable} from '../facade/async';
|
import {EventEmitter, Observable} from '../facade/async';
|
||||||
import {isObservable, isPromise} from '../private_import_core';
|
import {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, PipeTransform {
|
export class AsyncPipe implements OnDestroy {
|
||||||
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, PipeTransform {
|
|||||||
return _promiseStrategy;
|
return _promiseStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isObservable(obj)) {
|
if ((<any>obj).subscribe) {
|
||||||
return _observableStrategy;
|
return _observableStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ export class AsyncPipe implements OnDestroy, PipeTransform {
|
|||||||
this._obj = null;
|
this._obj = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _updateLatestValue(async: any, value: Object): void {
|
private _updateLatestValue(async: any, value: Object) {
|
||||||
if (async === this._obj) {
|
if (async === this._obj) {
|
||||||
this._latestValue = value;
|
this._latestValue = value;
|
||||||
this._ref.markForCheck();
|
this._ref.markForCheck();
|
||||||
|
72
modules/@angular/common/src/pipes/case_conversion_pipes.ts
Normal file
72
modules/@angular/common/src/pipes/case_conversion_pipes.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* @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
|
||||||
@ -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) || value !== value) return null;
|
if (isBlank(value)) return null;
|
||||||
|
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
value = value.trim();
|
value = value.trim();
|
||||||
@ -130,13 +130,8 @@ export class DatePipe implements PipeTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isDate(date)) {
|
if (!isDate(date)) {
|
||||||
let match: RegExpMatchArray;
|
|
||||||
if ((typeof value === 'string') && (match = value.match(ISO8601_DATE_REGEX))) {
|
|
||||||
date = isoStringToDate(match);
|
|
||||||
} else {
|
|
||||||
throw new InvalidPipeArgumentError(DatePipe, value);
|
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);
|
||||||
}
|
}
|
||||||
@ -145,31 +140,3 @@ 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);
|
|
||||||
}
|
|
||||||
|
@ -12,14 +12,13 @@
|
|||||||
* 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,
|
||||||
@ -32,9 +31,11 @@ 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.
|
||||||
*/
|
*/
|
||||||
@ -46,6 +47,7 @@ export const COMMON_PIPES = [
|
|||||||
SlicePipe,
|
SlicePipe,
|
||||||
DecimalPipe,
|
DecimalPipe,
|
||||||
PercentPipe,
|
PercentPipe,
|
||||||
|
TitleCasePipe,
|
||||||
CurrencyPipe,
|
CurrencyPipe,
|
||||||
DatePipe,
|
DatePipe,
|
||||||
I18nPluralPipe,
|
I18nPluralPipe,
|
||||||
|
@ -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
|
|
||||||
*/
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +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 {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,4 +9,3 @@
|
|||||||
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;
|
|
||||||
|
@ -0,0 +1,184 @@
|
|||||||
|
/**
|
||||||
|
* @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 {CommonModule} from '@angular/common';
|
||||||
|
import {NgComponentOutlet} from '@angular/common/src/directives/ng_component_outlet';
|
||||||
|
import {Component, ComponentRef, Inject, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, Optional, Provider, QueryList, ReflectiveInjector, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
||||||
|
import {TestBed, async} from '@angular/core/testing';
|
||||||
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('insert/remove', () => {
|
||||||
|
|
||||||
|
beforeEach(() => { TestBed.configureTestingModule({imports: [TestModule]}); });
|
||||||
|
|
||||||
|
it('should do nothing if component is null', async(() => {
|
||||||
|
const template = `<template *ngComponentOutlet="currentComponent"></template>`;
|
||||||
|
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
||||||
|
let fixture = TestBed.createComponent(TestComponent);
|
||||||
|
|
||||||
|
fixture.componentInstance.currentComponent = null;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should insert content specified by a component', async(() => {
|
||||||
|
let fixture = TestBed.createComponent(TestComponent);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
|
|
||||||
|
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('foo');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should emit a ComponentRef once a component was created', async(() => {
|
||||||
|
let fixture = TestBed.createComponent(TestComponent);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
|
|
||||||
|
fixture.componentInstance.cmpRef = null;
|
||||||
|
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('foo');
|
||||||
|
expect(fixture.componentInstance.cmpRef).toBeAnInstanceOf(ComponentRef);
|
||||||
|
expect(fixture.componentInstance.cmpRef.instance).toBeAnInstanceOf(InjectedComponent);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should clear view if component becomes null', async(() => {
|
||||||
|
let fixture = TestBed.createComponent(TestComponent);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
|
|
||||||
|
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('foo');
|
||||||
|
|
||||||
|
fixture.componentInstance.currentComponent = null;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should swap content if component changes', async(() => {
|
||||||
|
let fixture = TestBed.createComponent(TestComponent);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
|
|
||||||
|
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('foo');
|
||||||
|
|
||||||
|
fixture.componentInstance.currentComponent = InjectedComponentAgain;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('bar');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should use the injector, if one supplied', async(() => {
|
||||||
|
let fixture = TestBed.createComponent(TestComponent);
|
||||||
|
|
||||||
|
const uniqueValue = {};
|
||||||
|
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||||
|
fixture.componentInstance.injector = ReflectiveInjector.resolveAndCreate(
|
||||||
|
[{provide: TEST_TOKEN, useValue: uniqueValue}], fixture.componentRef.injector);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef;
|
||||||
|
expect(cmpRef).toBeAnInstanceOf(ComponentRef);
|
||||||
|
expect(cmpRef.instance).toBeAnInstanceOf(InjectedComponent);
|
||||||
|
expect(cmpRef.instance.testToken).toBe(uniqueValue);
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should resolve a with injector', async(() => {
|
||||||
|
let fixture = TestBed.createComponent(TestComponent);
|
||||||
|
|
||||||
|
fixture.componentInstance.cmpRef = null;
|
||||||
|
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||||
|
fixture.detectChanges();
|
||||||
|
let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef;
|
||||||
|
expect(cmpRef).toBeAnInstanceOf(ComponentRef);
|
||||||
|
expect(cmpRef.instance).toBeAnInstanceOf(InjectedComponent);
|
||||||
|
expect(cmpRef.instance.testToken).toBeNull();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should render projectable nodes, if supplied', async(() => {
|
||||||
|
const template = `<template>projected foo</template>${TEST_CMP_TEMPLATE}`;
|
||||||
|
TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||||
|
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
|
||||||
|
|
||||||
|
TestBed
|
||||||
|
.overrideComponent(InjectedComponent, {set: {template: `<ng-content></ng-content>`}})
|
||||||
|
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
|
||||||
|
|
||||||
|
let fixture = TestBed.createComponent(TestComponent);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
|
|
||||||
|
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||||
|
fixture.componentInstance.projectables =
|
||||||
|
[fixture.componentInstance.vcRef
|
||||||
|
.createEmbeddedView(fixture.componentInstance.tplRefs.first)
|
||||||
|
.rootNodes];
|
||||||
|
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('projected foo');
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const TEST_TOKEN = new InjectionToken('TestToken');
|
||||||
|
@Component({selector: 'injected-component', template: 'foo'})
|
||||||
|
class InjectedComponent {
|
||||||
|
constructor(@Optional() @Inject(TEST_TOKEN) public testToken: any) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Component({selector: 'injected-component-again', template: 'bar'})
|
||||||
|
class InjectedComponentAgain {
|
||||||
|
}
|
||||||
|
|
||||||
|
const TEST_CMP_TEMPLATE =
|
||||||
|
`<template *ngComponentOutlet="currentComponent; injector: injector; content: projectables"></template>`;
|
||||||
|
@Component({selector: 'test-cmp', template: TEST_CMP_TEMPLATE})
|
||||||
|
class TestComponent {
|
||||||
|
currentComponent: Type<any>;
|
||||||
|
injector: Injector;
|
||||||
|
projectables: any[][];
|
||||||
|
|
||||||
|
get cmpRef(): ComponentRef<any> { return this.ngComponentOutlet.componentRef; }
|
||||||
|
set cmpRef(value: ComponentRef<any>) { this.ngComponentOutlet.componentRef = value; }
|
||||||
|
|
||||||
|
@ViewChildren(TemplateRef) tplRefs: QueryList<TemplateRef<any>>;
|
||||||
|
@ViewChild(NgComponentOutlet) ngComponentOutlet: NgComponentOutlet;
|
||||||
|
|
||||||
|
constructor(public vcRef: ViewContainerRef) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [TestComponent, InjectedComponent, InjectedComponentAgain],
|
||||||
|
exports: [TestComponent, InjectedComponent, InjectedComponentAgain],
|
||||||
|
entryComponents: [InjectedComponent, InjectedComponentAgain]
|
||||||
|
})
|
||||||
|
export class TestModule {
|
||||||
|
}
|
@ -137,6 +137,72 @@ export function main() {
|
|||||||
|
|
||||||
expect(fixture.nativeElement).toHaveText('hello');
|
expect(fixture.nativeElement).toHaveText('hello');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
describe('else', () => {
|
||||||
|
it('should support else', async(() => {
|
||||||
|
const template = '<span *ngIf="booleanCondition; else elseBlock">TRUE</span>' +
|
||||||
|
'<template #elseBlock>FALSE</template>';
|
||||||
|
|
||||||
|
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 =
|
||||||
|
'<span *ngIf="booleanCondition; then thenBlock; else elseBlock">IGNORE</span>' +
|
||||||
|
'<template #thenBlock>THEN</template>' +
|
||||||
|
'<template #elseBlock>ELSE</template>';
|
||||||
|
|
||||||
|
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 =
|
||||||
|
'<span *ngIf="booleanCondition; else nestedBooleanCondition ? b1 : b2">TRUE</span>' +
|
||||||
|
'<template #b1>FALSE1</template>' +
|
||||||
|
'<template #b2>FALSE2</template>';
|
||||||
|
|
||||||
|
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 = '<span *ngIf="booleanCondition; else elseBlock; let v">{{v}}</span>' +
|
||||||
|
'<template #elseBlock let-v>{{v}}</template>';
|
||||||
|
|
||||||
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('true');
|
||||||
|
|
||||||
|
getComponent().booleanCondition = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('false');
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,14 +79,14 @@ export function main() {
|
|||||||
|
|
||||||
it('should display template if context is `null`', async(() => {
|
it('should display template if context is `null`', async(() => {
|
||||||
const template = `<template #tpl>foo</template>` +
|
const template = `<template #tpl>foo</template>` +
|
||||||
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="null"></ng-container>`;
|
`<ng-container *ngTemplateOutlet="tpl; context: null"></ng-container>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
detectChangesAndExpectText('foo');
|
detectChangesAndExpectText('foo');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should reflect initial context and changes', async(() => {
|
it('should reflect initial context and changes', async(() => {
|
||||||
const template = `<template let-foo="foo" #tpl>{{foo}}</template>` +
|
const template = `<template let-foo="foo" #tpl>{{foo}}</template>` +
|
||||||
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="context"></ng-container>`;
|
`<ng-container *ngTemplateOutlet="tpl; context: context"></ng-container>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@ -98,7 +98,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should reflect user defined `$implicit` property in the context', async(() => {
|
it('should reflect user defined `$implicit` property in the context', async(() => {
|
||||||
const template = `<template let-ctx #tpl>{{ctx.foo}}</template>` +
|
const template = `<template let-ctx #tpl>{{ctx.foo}}</template>` +
|
||||||
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="context"></ng-container>`;
|
`<ng-container *ngTemplateOutlet="tpl; context: context"></ng-container>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
fixture.componentInstance.context = {$implicit: {foo: 'bra'}};
|
fixture.componentInstance.context = {$implicit: {foo: 'bra'}};
|
||||||
detectChangesAndExpectText('bra');
|
detectChangesAndExpectText('bra');
|
||||||
@ -106,7 +106,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should reflect context re-binding', async(() => {
|
it('should reflect context re-binding', async(() => {
|
||||||
const template = `<template let-shawshank="shawshank" #tpl>{{shawshank}}</template>` +
|
const template = `<template let-shawshank="shawshank" #tpl>{{shawshank}}</template>` +
|
||||||
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="context"></ng-container>`;
|
`<ng-container *ngTemplateOutlet="tpl; context: context"></ng-container>`;
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
fixture.componentInstance.context = {shawshank: 'brooks'};
|
fixture.componentInstance.context = {shawshank: 'brooks'};
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* @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,13 +53,11 @@ 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(/Invalid argument/));
|
() => { expect(() => pipe.transform({})).toThrowError(); });
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('transform', () => {
|
describe('transform', () => {
|
||||||
@ -190,14 +188,8 @@ 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); });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,42 +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} 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(); });
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,43 +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 {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(); });
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
@ -8,6 +8,10 @@
|
|||||||
|
|
||||||
import {AUTO_STYLE, Component, animate, state, style, transition, trigger} from '@angular/core';
|
import {AUTO_STYLE, Component, animate, state, style, transition, trigger} from '@angular/core';
|
||||||
|
|
||||||
|
export function anyToAny(stateA: string, stateB: string): boolean {
|
||||||
|
return Math.random() != Math.random();
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'animate-cmp',
|
selector: 'animate-cmp',
|
||||||
animations: [trigger(
|
animations: [trigger(
|
||||||
@ -16,7 +20,7 @@ import {AUTO_STYLE, Component, animate, state, style, transition, trigger} from
|
|||||||
state('*', style({height: AUTO_STYLE, color: 'black', borderColor: 'black'})),
|
state('*', style({height: AUTO_STYLE, color: 'black', borderColor: 'black'})),
|
||||||
state('closed, void', style({height: '0px', color: 'maroon', borderColor: 'maroon'})),
|
state('closed, void', style({height: '0px', color: 'maroon', borderColor: 'maroon'})),
|
||||||
state('open', style({height: AUTO_STYLE, borderColor: 'green', color: 'green'})),
|
state('open', style({height: AUTO_STYLE, borderColor: 'green', color: 'green'})),
|
||||||
transition('* => *', animate(500))
|
transition(anyToAny, animate('1s')), transition('* => *', animate(500))
|
||||||
])],
|
])],
|
||||||
template: `
|
template: `
|
||||||
<button (click)="setAsOpen()">Open</button>
|
<button (click)="setAsOpen()">Open</button>
|
||||||
|
@ -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 {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver, Inject, OpaqueToken} from '@angular/core';
|
import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver, Inject, InjectionToken} from '@angular/core';
|
||||||
|
|
||||||
import {BasicComp} from './basic';
|
import {BasicComp} from './basic';
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ export class CompWithEntryComponents {
|
|||||||
constructor(public cfr: ComponentFactoryResolver) {}
|
constructor(public cfr: ComponentFactoryResolver) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SOME_TOKEN = new OpaqueToken('someToken');
|
export const SOME_TOKEN = new InjectionToken('someToken');
|
||||||
|
|
||||||
export function provideValueWithEntryComponents(value: any) {
|
export function provideValueWithEntryComponents(value: any) {
|
||||||
return [
|
return [
|
||||||
|
@ -7,21 +7,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as common from '@angular/common';
|
import * as common from '@angular/common';
|
||||||
import {CUSTOM_ELEMENTS_SCHEMA, Component, Directive, EventEmitter, Inject, NgModule, OpaqueToken, Output} from '@angular/core';
|
import {CUSTOM_ELEMENTS_SCHEMA, Component, Directive, EventEmitter, Inject, InjectionToken, NgModule, Output} from '@angular/core';
|
||||||
import {Observable} from 'rxjs/Observable';
|
import {Observable} from 'rxjs/Observable';
|
||||||
|
|
||||||
import {wrapInArray} from './funcs';
|
import {wrapInArray} from './funcs';
|
||||||
|
|
||||||
export const SOME_OPAQUE_TOKEN = new OpaqueToken('opaqueToken');
|
export const SOME_INJECTON_TOKEN = new InjectionToken('injectionToken');
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'comp-providers',
|
selector: 'comp-providers',
|
||||||
template: '',
|
template: '',
|
||||||
providers: [
|
providers: [
|
||||||
{provide: 'strToken', useValue: 'strValue'},
|
{provide: 'strToken', useValue: 'strValue'},
|
||||||
{provide: SOME_OPAQUE_TOKEN, useValue: 10},
|
{provide: SOME_INJECTON_TOKEN, useValue: 10},
|
||||||
{provide: 'reference', useValue: common.NgIf},
|
{provide: 'reference', useValue: common.NgIf},
|
||||||
{provide: 'complexToken', useValue: {a: 1, b: ['test', SOME_OPAQUE_TOKEN]}},
|
{provide: 'complexToken', useValue: {a: 1, b: ['test', SOME_INJECTON_TOKEN]}},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CompWithProviders {
|
export class CompWithProviders {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {LowerCasePipe, NgIf} from '@angular/common';
|
import {LowerCasePipe, NgIf} from '@angular/common';
|
||||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver, Directive, Inject, Injectable, Input, ModuleWithProviders, NgModule, OpaqueToken, Pipe} from '@angular/core';
|
import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver, Directive, Inject, Injectable, InjectionToken, Input, ModuleWithProviders, NgModule, Pipe} from '@angular/core';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SomeService {
|
export class SomeService {
|
||||||
@ -48,7 +48,7 @@ export class CompUsingRootModuleDirectiveAndPipe {
|
|||||||
export class CompUsingLibModuleDirectiveAndPipe {
|
export class CompUsingLibModuleDirectiveAndPipe {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SOME_TOKEN = new OpaqueToken('someToken');
|
export const SOME_TOKEN = new InjectionToken('someToken');
|
||||||
|
|
||||||
export function provideValueWithEntryComponents(value: any) {
|
export function provideValueWithEntryComponents(value: any) {
|
||||||
return [
|
return [
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"ng-xi18n": "./src/extract_i18n.js"
|
"ng-xi18n": "./src/extract_i18n.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/tsc-wrapped": "0.5.2",
|
"@angular/tsc-wrapped": "4.0.0-beta.5",
|
||||||
"reflect-metadata": "^0.1.2",
|
"reflect-metadata": "^0.1.2",
|
||||||
"minimist": "^1.2.0"
|
"minimist": "^1.2.0"
|
||||||
},
|
},
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
* 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 {StaticSymbol} from '../aot/static_symbol';
|
||||||
|
|
||||||
export abstract class AnimationAst {
|
export abstract class AnimationAst {
|
||||||
public startTime: number = 0;
|
public startTime: number = 0;
|
||||||
@ -49,6 +50,10 @@ export class AnimationStateTransitionExpression {
|
|||||||
constructor(public fromState: string, public toState: string) {}
|
constructor(public fromState: string, public toState: string) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class AnimationStateTransitionFnExpression extends AnimationStateTransitionExpression {
|
||||||
|
constructor(public fn: Function|StaticSymbol) { super(null, null); }
|
||||||
|
}
|
||||||
|
|
||||||
export class AnimationStateTransitionAst extends AnimationStateAst {
|
export class AnimationStateTransitionAst extends AnimationStateAst {
|
||||||
constructor(
|
constructor(
|
||||||
public stateChanges: AnimationStateTransitionExpression[],
|
public stateChanges: AnimationStateTransitionExpression[],
|
||||||
|
@ -12,7 +12,7 @@ import {Identifiers, createIdentifier} from '../identifiers';
|
|||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {ANY_STATE, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core';
|
import {ANY_STATE, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core';
|
||||||
|
|
||||||
import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from './animation_ast';
|
import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionFnExpression, AnimationStepAst, AnimationStylesAst} from './animation_ast';
|
||||||
|
|
||||||
export class AnimationEntryCompileResult {
|
export class AnimationEntryCompileResult {
|
||||||
constructor(public name: string, public statements: o.Statement[], public fnExp: o.Expression) {}
|
constructor(public name: string, public statements: o.Statement[], public fnExp: o.Expression) {}
|
||||||
@ -162,6 +162,11 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
const stateChangePreconditions: o.Expression[] = [];
|
const stateChangePreconditions: o.Expression[] = [];
|
||||||
|
|
||||||
ast.stateChanges.forEach(stateChange => {
|
ast.stateChanges.forEach(stateChange => {
|
||||||
|
if (stateChange instanceof AnimationStateTransitionFnExpression) {
|
||||||
|
stateChangePreconditions.push(o.importExpr({reference: stateChange.fn}).callFn([
|
||||||
|
_ANIMATION_CURRENT_STATE_VAR, _ANIMATION_NEXT_STATE_VAR
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
stateChangePreconditions.push(
|
stateChangePreconditions.push(
|
||||||
_compareToAnimationStateExpr(_ANIMATION_CURRENT_STATE_VAR, stateChange.fromState)
|
_compareToAnimationStateExpr(_ANIMATION_CURRENT_STATE_VAR, stateChange.fromState)
|
||||||
.and(_compareToAnimationStateExpr(_ANIMATION_NEXT_STATE_VAR, stateChange.toState)));
|
.and(_compareToAnimationStateExpr(_ANIMATION_NEXT_STATE_VAR, stateChange.toState)));
|
||||||
@ -173,6 +178,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
if (stateChange.toState != ANY_STATE) {
|
if (stateChange.toState != ANY_STATE) {
|
||||||
context.stateMap.registerState(stateChange.toState);
|
context.stateMap.registerState(stateChange.toState);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const animationPlayerExpr = ast.animation.visit(this, context);
|
const animationPlayerExpr = ast.animation.visit(this, context);
|
||||||
@ -294,8 +300,8 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
|
|
||||||
statements.push(new o.ReturnStatement(
|
statements.push(new o.ReturnStatement(
|
||||||
o.importExpr(createIdentifier(Identifiers.AnimationTransition)).instantiate([
|
o.importExpr(createIdentifier(Identifiers.AnimationTransition)).instantiate([
|
||||||
_ANIMATION_PLAYER_VAR, _ANIMATION_CURRENT_STATE_VAR, _ANIMATION_NEXT_STATE_VAR,
|
_ANIMATION_PLAYER_VAR, _ANIMATION_FACTORY_ELEMENT_VAR, o.literal(this.animationName),
|
||||||
_ANIMATION_TIME_VAR
|
_ANIMATION_CURRENT_STATE_VAR, _ANIMATION_NEXT_STATE_VAR, _ANIMATION_TIME_VAR
|
||||||
])));
|
])));
|
||||||
|
|
||||||
return o.fn(
|
return o.fn(
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
* 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';
|
||||||
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata, identifierName} from '../compile_metadata';
|
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata, identifierName} from '../compile_metadata';
|
||||||
import {StringMapWrapper} from '../facade/collection';
|
import {StringMapWrapper} from '../facade/collection';
|
||||||
import {isBlank, isPresent} from '../facade/lang';
|
import {isBlank, isPresent} from '../facade/lang';
|
||||||
@ -14,7 +15,7 @@ import {ParseError} from '../parse_util';
|
|||||||
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
|
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
|
||||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||||
|
|
||||||
import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionExpression, AnimationStepAst, AnimationStylesAst, AnimationWithStepsAst} from './animation_ast';
|
import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionExpression, AnimationStateTransitionFnExpression, AnimationStepAst, AnimationStylesAst, AnimationWithStepsAst} from './animation_ast';
|
||||||
import {StylesCollection} from './styles_collection';
|
import {StylesCollection} from './styles_collection';
|
||||||
|
|
||||||
const _INITIAL_KEYFRAME = 0;
|
const _INITIAL_KEYFRAME = 0;
|
||||||
@ -110,9 +111,12 @@ function _parseAnimationStateTransition(
|
|||||||
errors: AnimationParseError[]): AnimationStateTransitionAst {
|
errors: AnimationParseError[]): AnimationStateTransitionAst {
|
||||||
const styles = new StylesCollection();
|
const styles = new StylesCollection();
|
||||||
const transitionExprs: AnimationStateTransitionExpression[] = [];
|
const transitionExprs: AnimationStateTransitionExpression[] = [];
|
||||||
const transitionStates = transitionStateMetadata.stateChangeExpr.split(/\s*,\s*/);
|
const stateChangeExpr = transitionStateMetadata.stateChangeExpr;
|
||||||
|
const transitionStates: Array<Function|StaticSymbol|string> = typeof stateChangeExpr == 'string' ?
|
||||||
|
(<string>stateChangeExpr).split(/\s*,\s*/) :
|
||||||
|
[<Function|StaticSymbol>stateChangeExpr];
|
||||||
transitionStates.forEach(
|
transitionStates.forEach(
|
||||||
expr => { transitionExprs.push(..._parseAnimationTransitionExpr(expr, errors)); });
|
expr => transitionExprs.push(..._parseAnimationTransitionExpr(expr, errors)));
|
||||||
const entry = _normalizeAnimationEntry(transitionStateMetadata.steps);
|
const entry = _normalizeAnimationEntry(transitionStateMetadata.steps);
|
||||||
const animation = _normalizeStyleSteps(entry, stateStyles, schema, errors);
|
const animation = _normalizeStyleSteps(entry, stateStyles, schema, errors);
|
||||||
const animationAst = _parseTransitionAnimation(animation, 0, styles, stateStyles, errors);
|
const animationAst = _parseTransitionAnimation(animation, 0, styles, stateStyles, errors);
|
||||||
@ -141,8 +145,11 @@ function _parseAnimationAlias(alias: string, errors: AnimationParseError[]): str
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _parseAnimationTransitionExpr(
|
function _parseAnimationTransitionExpr(
|
||||||
eventStr: string, errors: AnimationParseError[]): AnimationStateTransitionExpression[] {
|
transitionValue: string | Function | StaticSymbol,
|
||||||
|
errors: AnimationParseError[]): AnimationStateTransitionExpression[] {
|
||||||
const expressions: AnimationStateTransitionExpression[] = [];
|
const expressions: AnimationStateTransitionExpression[] = [];
|
||||||
|
if (typeof transitionValue == 'string') {
|
||||||
|
let eventStr = <string>transitionValue;
|
||||||
if (eventStr[0] == ':') {
|
if (eventStr[0] == ':') {
|
||||||
eventStr = _parseAnimationAlias(eventStr, errors);
|
eventStr = _parseAnimationAlias(eventStr, errors);
|
||||||
}
|
}
|
||||||
@ -161,6 +168,10 @@ function _parseAnimationTransitionExpr(
|
|||||||
if (separator[0] == '<' && !isFullAnyStateExpr) {
|
if (separator[0] == '<' && !isFullAnyStateExpr) {
|
||||||
expressions.push(new AnimationStateTransitionExpression(toState, fromState));
|
expressions.push(new AnimationStateTransitionExpression(toState, fromState));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
expressions.push(
|
||||||
|
new AnimationStateTransitionFnExpression(<Function|StaticSymbol>transitionValue));
|
||||||
|
}
|
||||||
return expressions;
|
return expressions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,9 +8,8 @@
|
|||||||
|
|
||||||
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, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, componentFactoryName, createHostComponentMeta, identifierName} from '../compile_metadata';
|
||||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||||
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
|
||||||
import {ListWrapper} from '../facade/collection';
|
import {ListWrapper} from '../facade/collection';
|
||||||
import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers';
|
import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers';
|
||||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||||
@ -20,13 +19,14 @@ 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, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
|
import {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 {StaticSymbolResolver} from './static_symbol_resolver';
|
||||||
import {serializeSummaries, summaryFileName} from './summary_serializer';
|
import {serializeSummaries} from './summary_serializer';
|
||||||
|
import {ngfactoryFilePath, splitTypescriptSuffix, summaryFileName} from './util';
|
||||||
|
|
||||||
export class AotCompiler {
|
export class AotCompiler {
|
||||||
private _animationCompiler = new AnimationCompiler();
|
private _animationCompiler = new AnimationCompiler();
|
||||||
@ -63,12 +63,13 @@ 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(srcFileUrl, directives, pipes, ngModules, injectables));
|
generatedFiles.push(this._createSummary(
|
||||||
|
srcFileUrl, directives, pipes, ngModules, injectables, statements, exportedVars));
|
||||||
|
|
||||||
// compile all ng modules
|
// compile all ng modules
|
||||||
exportedVars.push(
|
exportedVars.push(
|
||||||
@ -107,7 +108,7 @@ export class AotCompiler {
|
|||||||
});
|
});
|
||||||
if (statements.length > 0) {
|
if (statements.length > 0) {
|
||||||
const srcModule = this._codegenSourceModule(
|
const srcModule = this._codegenSourceModule(
|
||||||
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
|
srcFileUrl, ngfactoryFilePath(srcFileUrl), statements, exportedVars);
|
||||||
generatedFiles.unshift(srcModule);
|
generatedFiles.unshift(srcModule);
|
||||||
}
|
}
|
||||||
return generatedFiles;
|
return generatedFiles;
|
||||||
@ -115,7 +116,8 @@ export class AotCompiler {
|
|||||||
|
|
||||||
private _createSummary(
|
private _createSummary(
|
||||||
srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[],
|
srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[],
|
||||||
ngModules: StaticSymbol[], injectables: StaticSymbol[]): GeneratedFile {
|
ngModules: StaticSymbol[], injectables: StaticSymbol[], targetStatements: o.Statement[],
|
||||||
|
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 = [
|
||||||
@ -124,8 +126,13 @@ 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 = serializeSummaries(
|
const {json, exportAs} = serializeSummaries(
|
||||||
this._host, this._summaryResolver, this._symbolResolver, symbolSummaries, typeSummaries);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,12 +155,6 @@ 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;
|
||||||
}
|
}
|
||||||
@ -170,13 +171,12 @@ 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(
|
||||||
this._symbolResolver.getStaticSymbol(
|
hostType, compMeta, this._metadataResolver.getHostComponentViewClass(hostType));
|
||||||
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);
|
const compFactoryVar = componentFactoryName(compMeta.type.reference);
|
||||||
targetStatements.push(
|
targetStatements.push(
|
||||||
o.variable(compFactoryVar)
|
o.variable(compFactoryVar)
|
||||||
.set(o.importExpr(
|
.set(o.importExpr(
|
||||||
@ -217,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(..._resolveViewStatements(this._symbolResolver, viewResult));
|
targetStatements.push(...viewResult.statements);
|
||||||
return viewResult.viewClassVar;
|
return viewResult.viewClassVar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,27 +239,6 @@ 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[] {
|
||||||
@ -270,15 +249,6 @@ 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}`;
|
||||||
}
|
}
|
||||||
@ -290,20 +260,6 @@ 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,11 +34,12 @@ 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 {StaticSymbolCache} from './static_symbol';
|
import {StaticSymbol, 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.
|
||||||
*/
|
*/
|
||||||
@ -69,13 +70,19 @@ 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,
|
||||||
staticReflector);
|
symbolCache, 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(compilerHost), summaryResolver, options.locale,
|
new NgModuleCompiler(), new TypeScriptEmitter(importResolver), summaryResolver,
|
||||||
options.i18nFormat, new AnimationParser(elementSchemaRegistry), symbolResolver);
|
options.locale, options.i18nFormat, new AnimationParser(elementSchemaRegistry),
|
||||||
|
symbolResolver);
|
||||||
return {compiler, reflector: staticReflector};
|
return {compiler, reflector: staticReflector};
|
||||||
}
|
}
|
||||||
|
@ -6,19 +6,23 @@
|
|||||||
* 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 {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, ImportResolver,
|
export interface AotCompilerHost extends StaticSymbolResolverHost, AotSummaryResolverHost {
|
||||||
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,7 +9,6 @@
|
|||||||
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} from './static_symbol';
|
import {StaticSymbol} from './static_symbol';
|
||||||
import {StaticSymbolResolver} from './static_symbol_resolver';
|
import {StaticSymbolResolver} from './static_symbol_resolver';
|
||||||
@ -18,7 +17,8 @@ const ANGULAR_IMPORT_LOCATIONS = {
|
|||||||
coreDecorators: '@angular/core/src/metadata',
|
coreDecorators: '@angular/core/src/metadata',
|
||||||
diDecorators: '@angular/core/src/di/metadata',
|
diDecorators: '@angular/core/src/di/metadata',
|
||||||
diMetadata: '@angular/core/src/di/metadata',
|
diMetadata: '@angular/core/src/di/metadata',
|
||||||
diOpaqueToken: '@angular/core/src/di/opaque_token',
|
diInjectionToken: '@angular/core/src/di/injection_token',
|
||||||
|
diOpaqueToken: '@angular/core/src/di/injection_token',
|
||||||
animationMetadata: '@angular/core/src/animation/metadata',
|
animationMetadata: '@angular/core/src/animation/metadata',
|
||||||
provider: '@angular/core/src/di/provider'
|
provider: '@angular/core/src/di/provider'
|
||||||
};
|
};
|
||||||
@ -35,6 +35,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
private parameterCache = new Map<StaticSymbol, any[]>();
|
private parameterCache = new Map<StaticSymbol, any[]>();
|
||||||
private methodCache = new Map<StaticSymbol, {[key: string]: boolean}>();
|
private methodCache = new Map<StaticSymbol, {[key: string]: boolean}>();
|
||||||
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
|
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
|
||||||
|
private injectionToken: StaticSymbol;
|
||||||
private opaqueToken: StaticSymbol;
|
private opaqueToken: StaticSymbol;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -84,9 +85,12 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
annotations = [];
|
annotations = [];
|
||||||
const classMetadata = this.getTypeMetadata(type);
|
const classMetadata = this.getTypeMetadata(type);
|
||||||
if (classMetadata['extends']) {
|
if (classMetadata['extends']) {
|
||||||
const parentAnnotations = this.annotations(this.simplify(type, classMetadata['extends']));
|
const parentType = this.simplify(type, classMetadata['extends']);
|
||||||
|
if (parentType && (parentType instanceof StaticSymbol)) {
|
||||||
|
const parentAnnotations = this.annotations(parentType);
|
||||||
annotations.push(...parentAnnotations);
|
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']);
|
||||||
annotations.push(...ownAnnotations);
|
annotations.push(...ownAnnotations);
|
||||||
@ -102,11 +106,14 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
const classMetadata = this.getTypeMetadata(type);
|
const classMetadata = this.getTypeMetadata(type);
|
||||||
propMetadata = {};
|
propMetadata = {};
|
||||||
if (classMetadata['extends']) {
|
if (classMetadata['extends']) {
|
||||||
const parentPropMetadata = this.propMetadata(this.simplify(type, classMetadata['extends']));
|
const parentType = this.simplify(type, classMetadata['extends']);
|
||||||
|
if (parentType instanceof StaticSymbol) {
|
||||||
|
const parentPropMetadata = this.propMetadata(parentType);
|
||||||
Object.keys(parentPropMetadata).forEach((parentProp) => {
|
Object.keys(parentPropMetadata).forEach((parentProp) => {
|
||||||
propMetadata[parentProp] = parentPropMetadata[parentProp];
|
propMetadata[parentProp] = parentPropMetadata[parentProp];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const members = classMetadata['members'] || {};
|
const members = classMetadata['members'] || {};
|
||||||
Object.keys(members).forEach((propName) => {
|
Object.keys(members).forEach((propName) => {
|
||||||
@ -157,7 +164,10 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
parameters.push(nestedResult);
|
parameters.push(nestedResult);
|
||||||
});
|
});
|
||||||
} else if (classMetadata['extends']) {
|
} else if (classMetadata['extends']) {
|
||||||
parameters = this.parameters(this.simplify(type, classMetadata['extends']));
|
const parentType = this.simplify(type, classMetadata['extends']);
|
||||||
|
if (parentType instanceof StaticSymbol) {
|
||||||
|
parameters = this.parameters(parentType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!parameters) {
|
if (!parameters) {
|
||||||
parameters = [];
|
parameters = [];
|
||||||
@ -177,11 +187,14 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
const classMetadata = this.getTypeMetadata(type);
|
const classMetadata = this.getTypeMetadata(type);
|
||||||
methodNames = {};
|
methodNames = {};
|
||||||
if (classMetadata['extends']) {
|
if (classMetadata['extends']) {
|
||||||
const parentMethodNames = this._methodNames(this.simplify(type, classMetadata['extends']));
|
const parentType = this.simplify(type, classMetadata['extends']);
|
||||||
|
if (parentType instanceof StaticSymbol) {
|
||||||
|
const parentMethodNames = this._methodNames(parentType);
|
||||||
Object.keys(parentMethodNames).forEach((parentProp) => {
|
Object.keys(parentMethodNames).forEach((parentProp) => {
|
||||||
methodNames[parentProp] = parentMethodNames[parentProp];
|
methodNames[parentProp] = parentMethodNames[parentProp];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const members = classMetadata['members'] || {};
|
const members = classMetadata['members'] || {};
|
||||||
Object.keys(members).forEach((propName) => {
|
Object.keys(members).forEach((propName) => {
|
||||||
@ -218,9 +231,10 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private initializeConversionMap(): void {
|
private initializeConversionMap(): void {
|
||||||
const {coreDecorators, diDecorators, diMetadata, diOpaqueToken, animationMetadata, provider} =
|
const {coreDecorators, diDecorators, diMetadata, diInjectionToken,
|
||||||
ANGULAR_IMPORT_LOCATIONS;
|
diOpaqueToken, animationMetadata, provider} = ANGULAR_IMPORT_LOCATIONS;
|
||||||
this.opaqueToken = this.findDeclaration(diOpaqueToken, 'OpaqueToken');
|
this.injectionToken = this.findDeclaration(diInjectionToken, 'InjectionToken');
|
||||||
|
this.opaqueToken = this.findDeclaration(diInjectionToken, 'OpaqueToken');
|
||||||
|
|
||||||
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Host'), Host);
|
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Host'), Host);
|
||||||
this._registerDecoratorOrConstructor(
|
this._registerDecoratorOrConstructor(
|
||||||
@ -371,7 +385,8 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
}
|
}
|
||||||
if (expression instanceof StaticSymbol) {
|
if (expression instanceof StaticSymbol) {
|
||||||
// Stop simplification at builtin symbols
|
// Stop simplification at builtin symbols
|
||||||
if (expression === self.opaqueToken || self.conversionMap.has(expression)) {
|
if (expression === self.injectionToken || expression === self.opaqueToken ||
|
||||||
|
self.conversionMap.has(expression)) {
|
||||||
return expression;
|
return expression;
|
||||||
} else {
|
} else {
|
||||||
const staticSymbol = expression;
|
const staticSymbol = expression;
|
||||||
@ -495,9 +510,9 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
// Determine if the function is a built-in conversion
|
// Determine if the function is a built-in conversion
|
||||||
staticSymbol = simplifyInContext(context, expression['expression'], depth + 1);
|
staticSymbol = simplifyInContext(context, expression['expression'], depth + 1);
|
||||||
if (staticSymbol instanceof StaticSymbol) {
|
if (staticSymbol instanceof StaticSymbol) {
|
||||||
if (staticSymbol === self.opaqueToken) {
|
if (staticSymbol === self.injectionToken || staticSymbol === self.opaqueToken) {
|
||||||
// if somebody calls new OpaqueToken, don't create an OpaqueToken,
|
// if somebody calls new InjectionToken, don't create an InjectionToken,
|
||||||
// but rather return the symbol to which the OpaqueToken is assigned to.
|
// but rather return the symbol to which the InjectionToken is assigned to.
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
const argExpressions: any[] = expression['arguments'] || [];
|
const argExpressions: any[] = expression['arguments'] || [];
|
||||||
@ -539,7 +554,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 SyntaxError(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,14 @@
|
|||||||
* 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,11 +45,18 @@ 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,
|
||||||
@ -60,13 +67,33 @@ export class StaticSymbolResolver {
|
|||||||
if (staticSymbol.members.length > 0) {
|
if (staticSymbol.members.length > 0) {
|
||||||
return this._resolveSymbolMembers(staticSymbol);
|
return this._resolveSymbolMembers(staticSymbol);
|
||||||
}
|
}
|
||||||
let result = this._resolveSymbolFromSummary(staticSymbol);
|
let result = this.resolvedSymbols.get(staticSymbol);
|
||||||
if (!result) {
|
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
|
// 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
|
// have summaries, only .d.ts files. So we always need to check both, the summary
|
||||||
// and metadata.
|
// and metadata.
|
||||||
this._createSymbolsOf(staticSymbol.filePath);
|
this._createSymbolsOf(staticSymbol.filePath);
|
||||||
result = this.resolvedSymbols.get(staticSymbol);
|
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) {
|
||||||
|
result = this.importAs.get(staticSymbol);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -135,10 +162,13 @@ 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
|
||||||
Object.keys(metadata['metadata']).forEach((symbolName) => {
|
const topLevelSymbolNames =
|
||||||
const symbolMeta = metadata['metadata'][symbolName];
|
new Set<string>(Object.keys(metadata['metadata']).map(unescapeIdentifier));
|
||||||
resolvedSymbols.push(
|
Object.keys(metadata['metadata']).forEach((metadataKey) => {
|
||||||
this.createResolvedSymbol(this.getStaticSymbol(filePath, symbolName), symbolMeta));
|
const symbolMeta = metadata['metadata'][metadataKey];
|
||||||
|
resolvedSymbols.push(this.createResolvedSymbol(
|
||||||
|
this.getStaticSymbol(filePath, unescapeIdentifier(metadataKey)), topLevelSymbolNames,
|
||||||
|
symbolMeta));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,15 +184,16 @@ 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 = exportSymbol.name;
|
symName = unescapeIdentifier(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(new ResolvedStaticSymbol(sourceSymbol, targetSymbol));
|
resolvedSymbols.push(this.createExport(sourceSymbol, targetSymbol));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -172,7 +203,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(new ResolvedStaticSymbol(sourceSymbol, targetSymbol));
|
resolvedSymbols.push(this.createExport(sourceSymbol, targetSymbol));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,7 +213,9 @@ export class StaticSymbolResolver {
|
|||||||
(resolvedSymbol) => this.resolvedSymbols.set(resolvedSymbol.symbol, resolvedSymbol));
|
(resolvedSymbol) => this.resolvedSymbols.set(resolvedSymbol.symbol, resolvedSymbol));
|
||||||
}
|
}
|
||||||
|
|
||||||
private createResolvedSymbol(sourceSymbol: StaticSymbol, metadata: any): ResolvedStaticSymbol {
|
private createResolvedSymbol(
|
||||||
|
sourceSymbol: StaticSymbol, topLevelSymbolNames: Set<string>,
|
||||||
|
metadata: any): ResolvedStaticSymbol {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
class ReferenceTransformer extends ValueTransformer {
|
class ReferenceTransformer extends ValueTransformer {
|
||||||
@ -196,7 +229,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'];
|
const name = map['name'] ? unescapeIdentifier(map['name']) : map['name'];
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -209,28 +242,43 @@ 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 {
|
} else if (functionParams.indexOf(name) >= 0) {
|
||||||
// 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(), []);
|
const transformedMeta = visitValue(metadata, new ReferenceTransformer(), []);
|
||||||
|
if (transformedMeta instanceof StaticSymbol) {
|
||||||
|
return this.createExport(sourceSymbol, transformedMeta);
|
||||||
|
}
|
||||||
return new ResolvedStaticSymbol(sourceSymbol, transformedMeta);
|
return new ResolvedStaticSymbol(sourceSymbol, transformedMeta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createExport(sourceSymbol: StaticSymbol, targetSymbol: StaticSymbol):
|
||||||
|
ResolvedStaticSymbol {
|
||||||
|
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) {
|
||||||
if (this.errorRecorder) {
|
if (this.errorRecorder) {
|
||||||
this.errorRecorder(error, (context && context.filePath) || path);
|
this.errorRecorder(error, (context && context.filePath) || path);
|
||||||
@ -273,12 +321,7 @@ 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) {
|
||||||
this.reportError(
|
throw new Error(`Could not resolve module ${module} relative to ${containingFile}`);
|
||||||
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);
|
||||||
}
|
}
|
||||||
@ -292,3 +335,9 @@ export class StaticSymbolResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
@ -9,10 +9,8 @@
|
|||||||
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 {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 {
|
||||||
/**
|
/**
|
||||||
@ -24,23 +22,34 @@ 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) {}
|
||||||
|
|
||||||
private _assertNoMembers(symbol: StaticSymbol) {
|
isLibraryFile(filePath: string): boolean {
|
||||||
if (symbol.members.length) {
|
// Note: We need to strip the .ngfactory. file path,
|
||||||
throw new Error(
|
// so this method also works for generated files
|
||||||
`Internal state: StaticSymbols in summaries can't have members! ${JSON.stringify(symbol)}`);
|
// (for which host.isSourceFile will always return false).
|
||||||
}
|
return !this.host.isSourceFile(stripNgFactory(filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLibraryFileName(filePath: string) { return this.host.getOutputFileName(filePath); }
|
||||||
|
|
||||||
resolveSummary(staticSymbol: StaticSymbol): Summary<StaticSymbol> {
|
resolveSummary(staticSymbol: StaticSymbol): Summary<StaticSymbol> {
|
||||||
this._assertNoMembers(staticSymbol);
|
staticSymbol.assertNoMembers();
|
||||||
let summary = this.summaryCache.get(staticSymbol);
|
let summary = this.summaryCache.get(staticSymbol);
|
||||||
if (!summary) {
|
if (!summary) {
|
||||||
this._loadSummaryFile(staticSymbol.filePath);
|
this._loadSummaryFile(staticSymbol.filePath);
|
||||||
@ -54,12 +63,17 @@ 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.host.isSourceFile(filePath)) {
|
if (this.isLibraryFile(filePath)) {
|
||||||
const summaryFilePath = summaryFileName(filePath);
|
const summaryFilePath = summaryFileName(filePath);
|
||||||
let json: string;
|
let json: string;
|
||||||
try {
|
try {
|
||||||
@ -69,8 +83,13 @@ export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
if (json) {
|
if (json) {
|
||||||
const readSummaries = deserializeSummaries(this.staticSymbolCache, json);
|
const {summaries, importAs} = deserializeSummaries(this.staticSymbolCache, json);
|
||||||
readSummaries.forEach((summary) => { this.summaryCache.set(summary.symbol, summary); });
|
summaries.forEach((summary) => this.summaryCache.set(summary.symbol, summary));
|
||||||
|
importAs.forEach((importAs) => {
|
||||||
|
this.importAs.set(
|
||||||
|
importAs.symbol,
|
||||||
|
this.staticSymbolCache.get(ngfactoryFilePath(filePath), importAs.importAs));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,28 +12,12 @@ import {ValueTransformer, visitValue} from '../util';
|
|||||||
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)$/;
|
|
||||||
|
|
||||||
|
|
||||||
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(
|
||||||
host: AotSummarySerializerHost, summaryResolver: SummaryResolver<StaticSymbol>,
|
summaryResolver: SummaryResolver<StaticSymbol>, symbolResolver: StaticSymbolResolver,
|
||||||
symbolResolver: StaticSymbolResolver,
|
symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]):
|
||||||
|
{json: string, exportAs: {symbol: StaticSymbol, exportAs: string}[]} {
|
||||||
symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]): string {
|
const serializer = new Serializer(symbolResolver, summaryResolver);
|
||||||
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
|
||||||
@ -46,7 +30,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 (!host.isSourceFile(symbol.filePath)) {
|
if (summaryResolver.isLibraryFile(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
|
||||||
@ -74,8 +58,11 @@ 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 (!host.isSourceFile(symbol.filePath)) {
|
if (summaryResolver.isLibraryFile(symbol.filePath)) {
|
||||||
serializer.addOrMergeSummary(summaryResolver.resolveSummary(symbol));
|
const summary = summaryResolver.resolveSummary(symbol);
|
||||||
|
if (summary) {
|
||||||
|
serializer.addOrMergeSummary(summary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -83,18 +70,14 @@ export function serializeSummaries(
|
|||||||
return serializer.serialize();
|
return serializer.serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deserializeSummaries(
|
export function deserializeSummaries(symbolCache: StaticSymbolCache, json: string):
|
||||||
symbolCache: StaticSymbolCache, json: string): Summary<StaticSymbol>[] {
|
{summaries: Summary<StaticSymbol>[], importAs: {symbol: StaticSymbol, importAs: string}[]} {
|
||||||
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
|
||||||
@ -102,7 +85,11 @@ class Serializer extends ValueTransformer {
|
|||||||
private processedSummaryBySymbol = new Map<StaticSymbol, any>();
|
private processedSummaryBySymbol = new Map<StaticSymbol, any>();
|
||||||
private processedSummaries: any[] = [];
|
private processedSummaries: any[] = [];
|
||||||
|
|
||||||
constructor(private host: AotSummarySerializerHost) { super(); }
|
constructor(
|
||||||
|
private symbolResolver: StaticSymbolResolver,
|
||||||
|
private summaryResolver: SummaryResolver<StaticSymbol>) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
addOrMergeSummary(summary: Summary<StaticSymbol>) {
|
addOrMergeSummary(summary: Summary<StaticSymbol>) {
|
||||||
let symbolMeta = summary.metadata;
|
let symbolMeta = summary.metadata;
|
||||||
@ -129,34 +116,44 @@ class Serializer extends ValueTransformer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(): string {
|
serialize(): {json: string, exportAs: {symbol: StaticSymbol, exportAs: string}[]} {
|
||||||
return JSON.stringify({
|
const exportAs: {symbol: StaticSymbol, exportAs: string}[] = [];
|
||||||
|
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.host.getOutputFileName(symbol.filePath)
|
filePath: this.summaryResolver.getLibraryFileName(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) {
|
||||||
let index = this.indexBySymbol.get(value);
|
const baseSymbol = this.symbolResolver.getStaticSymbol(value.filePath, value.name);
|
||||||
|
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(value, index);
|
this.indexBySymbol.set(baseSymbol, index);
|
||||||
this.symbols.push(value);
|
this.symbols.push(baseSymbol);
|
||||||
}
|
}
|
||||||
return {__symbol: index};
|
return {__symbol: index, members: value.members};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,16 +163,28 @@ class Deserializer extends ValueTransformer {
|
|||||||
|
|
||||||
constructor(private symbolCache: StaticSymbolCache) { super(); }
|
constructor(private symbolCache: StaticSymbolCache) { super(); }
|
||||||
|
|
||||||
deserialize(json: string): Summary<StaticSymbol>[] {
|
deserialize(json: string):
|
||||||
|
{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);
|
||||||
this.symbols = data.symbols.map(
|
const importAs: {symbol: StaticSymbol, importAs: string}[] = [];
|
||||||
serializedSymbol => this.symbolCache.get(serializedSymbol.filePath, serializedSymbol.name));
|
this.symbols = [];
|
||||||
return visitValue(data.summaries, this, null);
|
data.symbols.forEach((serializedSymbol) => {
|
||||||
|
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) {
|
||||||
return this.symbols[map['__symbol']];
|
const baseSymbol = 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);
|
||||||
}
|
}
|
||||||
|
37
modules/@angular/compiler/src/aot/util.ts
Normal file
37
modules/@angular/compiler/src/aot/util.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
|
||||||
|
*/
|
||||||
|
|
||||||
|
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, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
|
import {ChangeDetectionStrategy, ComponentFactory, 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,6 +15,10 @@ 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)"
|
||||||
@ -35,7 +39,11 @@ export class CompileAnimationStateDeclarationMetadata extends CompileAnimationSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class CompileAnimationStateTransitionMetadata extends CompileAnimationStateMetadata {
|
export class CompileAnimationStateTransitionMetadata extends CompileAnimationStateMetadata {
|
||||||
constructor(public stateChangeExpr: string, public steps: CompileAnimationMetadata) { super(); }
|
constructor(
|
||||||
|
public stateChangeExpr: string|StaticSymbol|((stateA: string, stateB: string) => boolean),
|
||||||
|
public steps: CompileAnimationMetadata) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class CompileAnimationMetadata {}
|
export abstract class CompileAnimationMetadata {}
|
||||||
@ -108,6 +116,24 @@ 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 {
|
||||||
@ -237,7 +263,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;
|
||||||
@ -257,11 +283,16 @@ 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 {
|
||||||
@ -277,9 +308,12 @@ export interface CompileDirectiveSummary extends CompileTypeSummary {
|
|||||||
providers: CompileProviderMetadata[];
|
providers: CompileProviderMetadata[];
|
||||||
viewProviders: CompileProviderMetadata[];
|
viewProviders: CompileProviderMetadata[];
|
||||||
queries: CompileQueryMetadata[];
|
queries: CompileQueryMetadata[];
|
||||||
entryComponents: CompileIdentifierMetadata[];
|
entryComponents: CompileEntryComponentMetadata[];
|
||||||
changeDetection: ChangeDetectionStrategy;
|
changeDetection: ChangeDetectionStrategy;
|
||||||
template: CompileTemplateSummary;
|
template: CompileTemplateSummary;
|
||||||
|
wrapperType: StaticSymbol|ProxyClass;
|
||||||
|
componentViewType: StaticSymbol|ProxyClass;
|
||||||
|
componentFactory: StaticSymbol|ComponentFactory<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -288,7 +322,8 @@ 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}: {
|
providers, viewProviders, queries, viewQueries, entryComponents, template, wrapperType,
|
||||||
|
componentViewType, componentFactory}: {
|
||||||
isHost?: boolean,
|
isHost?: boolean,
|
||||||
type?: CompileTypeMetadata,
|
type?: CompileTypeMetadata,
|
||||||
isComponent?: boolean,
|
isComponent?: boolean,
|
||||||
@ -302,8 +337,11 @@ export class CompileDirectiveMetadata {
|
|||||||
viewProviders?: CompileProviderMetadata[],
|
viewProviders?: CompileProviderMetadata[],
|
||||||
queries?: CompileQueryMetadata[],
|
queries?: CompileQueryMetadata[],
|
||||||
viewQueries?: CompileQueryMetadata[],
|
viewQueries?: CompileQueryMetadata[],
|
||||||
entryComponents?: CompileIdentifierMetadata[],
|
entryComponents?: CompileEntryComponentMetadata[],
|
||||||
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} = {};
|
||||||
@ -355,6 +393,9 @@ export class CompileDirectiveMetadata {
|
|||||||
viewQueries,
|
viewQueries,
|
||||||
entryComponents,
|
entryComponents,
|
||||||
template,
|
template,
|
||||||
|
wrapperType,
|
||||||
|
componentViewType,
|
||||||
|
componentFactory,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
isHost: boolean;
|
isHost: boolean;
|
||||||
@ -372,14 +413,18 @@ export class CompileDirectiveMetadata {
|
|||||||
viewProviders: CompileProviderMetadata[];
|
viewProviders: CompileProviderMetadata[];
|
||||||
queries: CompileQueryMetadata[];
|
queries: CompileQueryMetadata[];
|
||||||
viewQueries: CompileQueryMetadata[];
|
viewQueries: CompileQueryMetadata[];
|
||||||
entryComponents: CompileIdentifierMetadata[];
|
entryComponents: CompileEntryComponentMetadata[];
|
||||||
|
|
||||||
template: CompileTemplateMetadata;
|
template: CompileTemplateMetadata;
|
||||||
|
|
||||||
constructor(
|
wrapperType: StaticSymbol|ProxyClass;
|
||||||
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs,
|
componentViewType: StaticSymbol|ProxyClass;
|
||||||
hostListeners, hostProperties, hostAttributes, providers, viewProviders, queries,
|
componentFactory: StaticSymbol|ComponentFactory<any>;
|
||||||
viewQueries, entryComponents, template}: {
|
|
||||||
|
constructor({isHost, type, isComponent, selector, exportAs,
|
||||||
|
changeDetection, inputs, outputs, hostListeners, hostProperties,
|
||||||
|
hostAttributes, providers, viewProviders, queries, viewQueries,
|
||||||
|
entryComponents, template, wrapperType, componentViewType, componentFactory}: {
|
||||||
isHost?: boolean,
|
isHost?: boolean,
|
||||||
type?: CompileTypeMetadata,
|
type?: CompileTypeMetadata,
|
||||||
isComponent?: boolean,
|
isComponent?: boolean,
|
||||||
@ -395,8 +440,11 @@ export class CompileDirectiveMetadata {
|
|||||||
viewProviders?: CompileProviderMetadata[],
|
viewProviders?: CompileProviderMetadata[],
|
||||||
queries?: CompileQueryMetadata[],
|
queries?: CompileQueryMetadata[],
|
||||||
viewQueries?: CompileQueryMetadata[],
|
viewQueries?: CompileQueryMetadata[],
|
||||||
entryComponents?: CompileIdentifierMetadata[],
|
entryComponents?: CompileEntryComponentMetadata[],
|
||||||
template?: CompileTemplateMetadata,
|
template?: CompileTemplateMetadata,
|
||||||
|
wrapperType?: StaticSymbol|ProxyClass,
|
||||||
|
componentViewType?: StaticSymbol|ProxyClass,
|
||||||
|
componentFactory?: StaticSymbol|ComponentFactory<any>,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.isHost = !!isHost;
|
this.isHost = !!isHost;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@ -414,8 +462,11 @@ 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.template = template;
|
||||||
|
|
||||||
|
this.wrapperType = wrapperType;
|
||||||
|
this.componentViewType = componentViewType;
|
||||||
|
this.componentFactory = componentFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
toSummary(): CompileDirectiveSummary {
|
toSummary(): CompileDirectiveSummary {
|
||||||
@ -435,7 +486,10 @@ 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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -444,11 +498,12 @@ 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(
|
||||||
typeReference: any, compMeta: CompileDirectiveMetadata): CompileDirectiveMetadata {
|
hostTypeReference: any, compMeta: 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: typeReference, diDeps: [], lifecycleHooks: []},
|
type: {reference: hostTypeReference, diDeps: [], lifecycleHooks: []},
|
||||||
template: new CompileTemplateMetadata({
|
template: new CompileTemplateMetadata({
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
template: template,
|
template: template,
|
||||||
@ -467,7 +522,8 @@ export function createHostComponentMeta(
|
|||||||
providers: [],
|
providers: [],
|
||||||
viewProviders: [],
|
viewProviders: [],
|
||||||
queries: [],
|
queries: [],
|
||||||
viewQueries: []
|
viewQueries: [],
|
||||||
|
componentViewType: hostViewType
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,7 +569,7 @@ export interface CompileNgModuleSummary extends CompileTypeSummary {
|
|||||||
exportedPipes: CompileIdentifierMetadata[];
|
exportedPipes: CompileIdentifierMetadata[];
|
||||||
|
|
||||||
// Note: This is transitive.
|
// Note: This is transitive.
|
||||||
entryComponents: CompileIdentifierMetadata[];
|
entryComponents: CompileEntryComponentMetadata[];
|
||||||
// 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.
|
||||||
@ -530,7 +586,7 @@ export class CompileNgModuleMetadata {
|
|||||||
declaredPipes: CompileIdentifierMetadata[];
|
declaredPipes: CompileIdentifierMetadata[];
|
||||||
|
|
||||||
exportedPipes: CompileIdentifierMetadata[];
|
exportedPipes: CompileIdentifierMetadata[];
|
||||||
entryComponents: CompileIdentifierMetadata[];
|
entryComponents: CompileEntryComponentMetadata[];
|
||||||
bootstrapComponents: CompileIdentifierMetadata[];
|
bootstrapComponents: CompileIdentifierMetadata[];
|
||||||
providers: CompileProviderMetadata[];
|
providers: CompileProviderMetadata[];
|
||||||
|
|
||||||
@ -551,7 +607,7 @@ export class CompileNgModuleMetadata {
|
|||||||
exportedDirectives?: CompileIdentifierMetadata[],
|
exportedDirectives?: CompileIdentifierMetadata[],
|
||||||
declaredPipes?: CompileIdentifierMetadata[],
|
declaredPipes?: CompileIdentifierMetadata[],
|
||||||
exportedPipes?: CompileIdentifierMetadata[],
|
exportedPipes?: CompileIdentifierMetadata[],
|
||||||
entryComponents?: CompileIdentifierMetadata[],
|
entryComponents?: CompileEntryComponentMetadata[],
|
||||||
bootstrapComponents?: CompileIdentifierMetadata[],
|
bootstrapComponents?: CompileIdentifierMetadata[],
|
||||||
importedModules?: CompileNgModuleSummary[],
|
importedModules?: CompileNgModuleSummary[],
|
||||||
exportedModules?: CompileNgModuleSummary[],
|
exportedModules?: CompileNgModuleSummary[],
|
||||||
@ -599,7 +655,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: CompileIdentifierMetadata[] = [];
|
entryComponents: CompileEntryComponentMetadata[] = [];
|
||||||
|
|
||||||
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[] = [];
|
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[] = [];
|
||||||
|
|
||||||
@ -637,10 +693,10 @@ export class TransitiveCompileNgModuleMetadata {
|
|||||||
this.modules.push(id);
|
this.modules.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addEntryComponent(id: CompileIdentifierMetadata) {
|
addEntryComponent(ec: CompileEntryComponentMetadata) {
|
||||||
if (!this.entryComponentsSet.has(id.reference)) {
|
if (!this.entryComponentsSet.has(ec.componentType)) {
|
||||||
this.entryComponentsSet.add(id.reference);
|
this.entryComponentsSet.add(ec.componentType);
|
||||||
this.entryComponents.push(id);
|
this.entryComponents.push(ec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,10 @@
|
|||||||
* 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 {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';
|
||||||
|
|
||||||
import {ConvertPropertyBindingResult} from './expression_converter';
|
|
||||||
|
|
||||||
export class CheckBindingField {
|
export class CheckBindingField {
|
||||||
constructor(public expression: o.ReadPropExpr, public bindingId: string) {}
|
constructor(public expression: o.ReadPropExpr, public bindingId: string) {}
|
||||||
}
|
}
|
||||||
@ -20,28 +18,14 @@ 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)
|
builder.ctorStmts.push(o.THIS_EXPR.prop(fieldExpr.name).set(o.literal(undefined)).toStmt());
|
||||||
.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'));
|
||||||
|
}
|
@ -7,75 +7,69 @@
|
|||||||
*/
|
*/
|
||||||
import {SecurityContext} from '@angular/core';
|
import {SecurityContext} from '@angular/core';
|
||||||
|
|
||||||
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';
|
||||||
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 writeToRenderer(
|
export function createCheckRenderBindingStmt(
|
||||||
view: o.Expression, boundProp: BoundElementPropertyAst, renderElement: o.Expression,
|
view: o.Expression, renderElement: o.Expression, boundProp: BoundElementPropertyAst,
|
||||||
renderValue: o.Expression, logBindingUpdate: boolean,
|
oldValue: o.ReadPropExpr, evalResult: ConvertPropertyBindingResult,
|
||||||
securityContextExpression?: o.Expression): o.Statement[] {
|
securityContextExpression?: o.Expression): o.Statement[] {
|
||||||
const updateStmts: o.Statement[] = [];
|
const checkStmts: o.Statement[] = [...evalResult.stmts];
|
||||||
const renderer = view.prop('renderer');
|
const securityContext = calcSecurityContext(boundProp, securityContextExpression);
|
||||||
renderValue = sanitizedValue(view, boundProp, renderValue, securityContextExpression);
|
|
||||||
switch (boundProp.type) {
|
switch (boundProp.type) {
|
||||||
case PropertyBindingType.Property:
|
case PropertyBindingType.Property:
|
||||||
if (logBindingUpdate) {
|
checkStmts.push(o.importExpr(createIdentifier(Identifiers.checkRenderProperty))
|
||||||
updateStmts.push(
|
.callFn([
|
||||||
o.importExpr(createIdentifier(Identifiers.setBindingDebugInfo))
|
view, renderElement, o.literal(boundProp.name), oldValue,
|
||||||
.callFn([renderer, renderElement, o.literal(boundProp.name), renderValue])
|
oldValue.set(evalResult.currValExpr),
|
||||||
.toStmt());
|
evalResult.forceUpdate || o.literal(false), securityContext
|
||||||
}
|
])
|
||||||
updateStmts.push(
|
|
||||||
renderer
|
|
||||||
.callMethod(
|
|
||||||
'setElementProperty', [renderElement, o.literal(boundProp.name), renderValue])
|
|
||||||
.toStmt());
|
.toStmt());
|
||||||
break;
|
break;
|
||||||
case PropertyBindingType.Attribute:
|
case PropertyBindingType.Attribute:
|
||||||
renderValue =
|
checkStmts.push(o.importExpr(createIdentifier(Identifiers.checkRenderAttribute))
|
||||||
renderValue.isBlank().conditional(o.NULL_EXPR, renderValue.callMethod('toString', []));
|
.callFn([
|
||||||
updateStmts.push(
|
view, renderElement, o.literal(boundProp.name), oldValue,
|
||||||
renderer
|
oldValue.set(evalResult.currValExpr),
|
||||||
.callMethod(
|
evalResult.forceUpdate || o.literal(false), securityContext
|
||||||
'setElementAttribute', [renderElement, o.literal(boundProp.name), renderValue])
|
])
|
||||||
.toStmt());
|
.toStmt());
|
||||||
break;
|
break;
|
||||||
case PropertyBindingType.Class:
|
case PropertyBindingType.Class:
|
||||||
updateStmts.push(
|
checkStmts.push(
|
||||||
renderer
|
o.importExpr(createIdentifier(Identifiers.checkRenderClass))
|
||||||
.callMethod(
|
.callFn([
|
||||||
'setElementClass', [renderElement, o.literal(boundProp.name), renderValue])
|
view, renderElement, o.literal(boundProp.name), oldValue,
|
||||||
|
oldValue.set(evalResult.currValExpr), evalResult.forceUpdate || o.literal(false)
|
||||||
|
])
|
||||||
.toStmt());
|
.toStmt());
|
||||||
break;
|
break;
|
||||||
case PropertyBindingType.Style:
|
case PropertyBindingType.Style:
|
||||||
let strValue: o.Expression = renderValue.callMethod('toString', []);
|
checkStmts.push(
|
||||||
if (isPresent(boundProp.unit)) {
|
o.importExpr(createIdentifier(Identifiers.checkRenderStyle))
|
||||||
strValue = strValue.plus(o.literal(boundProp.unit));
|
.callFn([
|
||||||
}
|
view, renderElement, o.literal(boundProp.name), o.literal(boundProp.unit), oldValue,
|
||||||
|
oldValue.set(evalResult.currValExpr), evalResult.forceUpdate || o.literal(false),
|
||||||
renderValue = renderValue.isBlank().conditional(o.NULL_EXPR, strValue);
|
securityContext
|
||||||
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 updateStmts;
|
return checkStmts;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sanitizedValue(
|
function calcSecurityContext(
|
||||||
view: o.Expression, boundProp: BoundElementPropertyAst, renderValue: o.Expression,
|
boundProp: BoundElementPropertyAst, securityContextExpression?: o.Expression): o.Expression {
|
||||||
securityContextExpression?: o.Expression): o.Expression {
|
|
||||||
if (boundProp.securityContext === SecurityContext.NONE) {
|
if (boundProp.securityContext === SecurityContext.NONE) {
|
||||||
return renderValue; // No sanitization needed.
|
return o.NULL_EXPR; // No sanitization needed.
|
||||||
}
|
}
|
||||||
if (!boundProp.needsRuntimeSecurityContext) {
|
if (!boundProp.needsRuntimeSecurityContext) {
|
||||||
securityContextExpression =
|
securityContextExpression =
|
||||||
@ -84,15 +78,13 @@ function sanitizedValue(
|
|||||||
if (!securityContextExpression) {
|
if (!securityContextExpression) {
|
||||||
throw new Error(`internal error, no SecurityContext given ${boundProp.name}`);
|
throw new Error(`internal error, no SecurityContext given ${boundProp.name}`);
|
||||||
}
|
}
|
||||||
const ctx = view.prop('viewUtils').prop('sanitizer');
|
return securityContextExpression;
|
||||||
const args = [securityContextExpression, renderValue];
|
|
||||||
return ctx.callMethod('sanitize', args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function triggerAnimation(
|
export function createCheckAnimationBindingStmts(
|
||||||
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,
|
||||||
renderValue: o.Expression, lastRenderValue: o.Expression) {
|
oldValue: o.ReadPropExpr, evalResult: ConvertPropertyBindingResult) {
|
||||||
const detachStmts: o.Statement[] = [];
|
const detachStmts: o.Statement[] = [];
|
||||||
const updateStmts: o.Statement[] = [];
|
const updateStmts: o.Statement[] = [];
|
||||||
|
|
||||||
@ -104,21 +96,20 @@ export function triggerAnimation(
|
|||||||
// 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,
|
view, renderElement, isFirstViewCheck(view).conditional(emptyStateValue, oldValue),
|
||||||
lastRenderValue.equals(unitializedValue).conditional(emptyStateValue, lastRenderValue),
|
evalResult.currValExpr
|
||||||
renderValue.equals(unitializedValue).conditional(emptyStateValue, renderValue)
|
|
||||||
]))
|
]))
|
||||||
.toDeclStmt());
|
.toDeclStmt());
|
||||||
|
updateStmts.push(oldValue.set(evalResult.currValExpr).toStmt());
|
||||||
|
|
||||||
detachStmts.push(
|
detachStmts.push(animationTransitionVar
|
||||||
animationTransitionVar
|
.set(animationFnExpr.callFn(
|
||||||
.set(animationFnExpr.callFn([view, renderElement, lastRenderValue, emptyStateValue]))
|
[view, renderElement, evalResult.currValExpr, emptyStateValue]))
|
||||||
.toDeclStmt());
|
.toDeclStmt());
|
||||||
|
|
||||||
const registerStmts: o.Statement[] = [];
|
const registerStmts: o.Statement[] = [];
|
||||||
@ -151,5 +142,14 @@ export function triggerAnimation(
|
|||||||
updateStmts.push(...registerStmts);
|
updateStmts.push(...registerStmts);
|
||||||
detachStmts.push(...registerStmts);
|
detachStmts.push(...registerStmts);
|
||||||
|
|
||||||
return {updateStmts, detachStmts};
|
const checkUpdateStmts: o.Statement[] = [
|
||||||
|
...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,6 +11,10 @@ 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;
|
||||||
@ -48,12 +52,12 @@ export class CompilerConfig {
|
|||||||
* to help tree shaking.
|
* to help tree shaking.
|
||||||
*/
|
*/
|
||||||
export abstract class RenderTypes {
|
export abstract class RenderTypes {
|
||||||
abstract get renderer(): CompileIdentifierMetadata;
|
get renderer(): CompileIdentifierMetadata { return unimplemented(); }
|
||||||
abstract get renderText(): CompileIdentifierMetadata;
|
get renderText(): CompileIdentifierMetadata { return unimplemented(); }
|
||||||
abstract get renderElement(): CompileIdentifierMetadata;
|
get renderElement(): CompileIdentifierMetadata { return unimplemented(); }
|
||||||
abstract get renderComment(): CompileIdentifierMetadata;
|
get renderComment(): CompileIdentifierMetadata { return unimplemented(); }
|
||||||
abstract get renderNode(): CompileIdentifierMetadata;
|
get renderNode(): CompileIdentifierMetadata { return unimplemented(); }
|
||||||
abstract get renderEvent(): CompileIdentifierMetadata;
|
get renderEvent(): CompileIdentifierMetadata { return unimplemented(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DefaultRenderTypes implements RenderTypes {
|
export class DefaultRenderTypes implements RenderTypes {
|
||||||
|
@ -10,7 +10,7 @@ import {ViewEncapsulation} from '@angular/core';
|
|||||||
|
|
||||||
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata} from './compile_metadata';
|
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata} from './compile_metadata';
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
import {isBlank, isPresent, stringify} from './facade/lang';
|
import {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() { this._resourceLoaderCache.clear(); }
|
clearCache(): void { this._resourceLoaderCache.clear(); }
|
||||||
|
|
||||||
clearCacheFor(normalizedDirective: CompileDirectiveMetadata) {
|
clearCacheFor(normalizedDirective: CompileDirectiveMetadata): void {
|
||||||
if (!normalizedDirective.isComponent) {
|
if (!normalizedDirective.isComponent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -65,10 +65,18 @@ export class DirectiveNormalizer {
|
|||||||
SyncAsyncResult<CompileTemplateMetadata> {
|
SyncAsyncResult<CompileTemplateMetadata> {
|
||||||
let normalizedTemplateSync: CompileTemplateMetadata = null;
|
let normalizedTemplateSync: CompileTemplateMetadata = null;
|
||||||
let normalizedTemplateAsync: Promise<CompileTemplateMetadata>;
|
let normalizedTemplateAsync: Promise<CompileTemplateMetadata>;
|
||||||
if (isPresent(prenormData.template)) {
|
if (prenormData.template != null) {
|
||||||
|
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(
|
||||||
@ -120,7 +128,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 (isBlank(encapsulation)) {
|
if (encapsulation == null) {
|
||||||
encapsulation = this._config.defaultEncapsulation;
|
encapsulation = this._config.defaultEncapsulation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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, identifierModuleUrl, identifierName} from './compile_metadata';
|
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, dirWrapperClassName, identifierModuleUrl, identifierName} from './compile_metadata';
|
||||||
import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util';
|
import {createCheckBindingField, isFirstViewCheck} from './compiler_util/binding_util';
|
||||||
import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
|
import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
|
||||||
import {triggerAnimation, writeToRenderer} from './compiler_util/render_util';
|
import {createCheckAnimationBindingStmts, createCheckRenderBindingStmt} 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,10 +52,6 @@ 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) {}
|
||||||
@ -133,8 +129,9 @@ class DirectiveWrapperBuilder implements ClassBuilder {
|
|||||||
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]));
|
||||||
@ -148,7 +145,7 @@ class DirectiveWrapperBuilder implements ClassBuilder {
|
|||||||
.toStmt());
|
.toStmt());
|
||||||
|
|
||||||
return createClassStmt({
|
return createClassStmt({
|
||||||
name: DirectiveWrapperCompiler.dirWrapperClassName(this.dirMeta.type),
|
name: dirWrapperClassName(this.dirMeta.type.reference),
|
||||||
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]
|
||||||
});
|
});
|
||||||
@ -183,14 +180,14 @@ function addNgDoCheckMethod(builder: DirectiveWrapperBuilder) {
|
|||||||
|
|
||||||
if (builder.ngOnInit) {
|
if (builder.ngOnInit) {
|
||||||
lifecycleStmts.push(new o.IfStmt(
|
lifecycleStmts.push(new o.IfStmt(
|
||||||
VIEW_VAR.prop('numberOfChecks').identical(new o.LiteralExpr(0)),
|
isFirstViewCheck(VIEW_VAR),
|
||||||
[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(THROW_ON_CHANGE_VAR), lifecycleStmts));
|
stmts.push(new o.IfStmt(o.not(VIEW_VAR.prop('throwOnChange')), lifecycleStmts));
|
||||||
}
|
}
|
||||||
stmts.push(new o.ReturnStatement(changedVar));
|
stmts.push(new o.ReturnStatement(changedVar));
|
||||||
|
|
||||||
@ -200,7 +197,6 @@ 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));
|
||||||
}
|
}
|
||||||
@ -210,24 +206,35 @@ 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(o.THIS_EXPR.prop(CHANGES_FIELD_NAME)
|
onChangeStatements.push(
|
||||||
.key(o.literal(input))
|
o.THIS_EXPR.prop(CHANGES_FIELD_NAME).key(o.literal(input)).set(CHANGE_VAR).toStmt());
|
||||||
.set(o.importExpr(createIdentifier(Identifiers.SimpleChange))
|
methodBody = [
|
||||||
.instantiate([field.expression, CURR_VALUE_VAR]))
|
CHANGE_VAR
|
||||||
.toStmt());
|
.set(o.importExpr(createIdentifier(Identifiers.checkBindingChange)).callFn([
|
||||||
|
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(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
|
new o.FnParam(FORCE_UPDATE_VAR.name, o.BOOL_TYPE)
|
||||||
new o.FnParam(FORCE_UPDATE_VAR.name, o.BOOL_TYPE),
|
|
||||||
],
|
],
|
||||||
methodBody));
|
methodBody));
|
||||||
}
|
}
|
||||||
@ -243,7 +250,6 @@ 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);
|
||||||
@ -258,23 +264,18 @@ 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 {updateStmts, detachStmts} = triggerAnimation(
|
const {checkUpdateStmts, checkDetachStmts} = createCheckAnimationBindingStmts(
|
||||||
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, evalResult.currValExpr, field.expression);
|
RENDER_EL_VAR, field.expression, evalResult);
|
||||||
checkBindingStmts = updateStmts;
|
builder.detachStmts.push(...checkDetachStmts);
|
||||||
builder.detachStmts.push(...detachStmts);
|
stmts.push(...checkUpdateStmts);
|
||||||
} else {
|
} else {
|
||||||
checkBindingStmts = writeToRenderer(
|
stmts.push(...createCheckRenderBindingStmt(
|
||||||
VIEW_VAR, hostProp, RENDER_EL_VAR, evalResult.currValExpr,
|
VIEW_VAR, RENDER_EL_VAR, hostProp, field.expression, evalResult, securityContextExpr));
|
||||||
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));
|
||||||
}
|
}
|
||||||
@ -384,20 +385,19 @@ export class DirectiveWrapperExpressions {
|
|||||||
return dirWrapper.prop(CONTEXT_FIELD_NAME);
|
return dirWrapper.prop(CONTEXT_FIELD_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ngDoCheck(
|
static ngDoCheck(dirWrapper: o.Expression, view: o.Expression, renderElement: o.Expression, ):
|
||||||
dirWrapper: o.Expression, view: o.Expression, renderElement: o.Expression,
|
o.Expression {
|
||||||
throwOnChange: o.Expression): o.Expression {
|
return dirWrapper.callMethod('ngDoCheck', [view, renderElement]);
|
||||||
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, throwOnChange: o.Expression,
|
componentView: o.Expression, renderElement: 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', [view, componentView, renderElement, throwOnChange].concat(
|
'checkHost',
|
||||||
runtimeSecurityContexts))
|
[view, componentView, renderElement].concat(runtimeSecurityContexts))
|
||||||
.toStmt()];
|
.toStmt()];
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
|
@ -9,10 +9,14 @@
|
|||||||
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 sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
|
return message.id || sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decimalDigest(message: i18n.Message): string {
|
export function decimalDigest(message: i18n.Message): string {
|
||||||
|
if (message.id) {
|
||||||
|
return message.id;
|
||||||
|
}
|
||||||
|
|
||||||
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 computeMsgId(parts.join(''), message.meaning);
|
return computeMsgId(parts.join(''), message.meaning);
|
||||||
|
@ -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,
|
||||||
staticReflector);
|
symbolCache, staticReflector);
|
||||||
|
|
||||||
// TODO(vicb): implicit tags & attributes
|
// TODO(vicb): implicit tags & attributes
|
||||||
const messageBundle = new MessageBundle(htmlParser, [], {});
|
const messageBundle = new MessageBundle(htmlParser, [], {});
|
||||||
|
@ -18,6 +18,8 @@ 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
|
||||||
@ -79,7 +81,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) => i18n.Message;
|
(msg: html.Node[], meaning: string, description: string, id: string) => i18n.Message;
|
||||||
|
|
||||||
|
|
||||||
constructor(private _implicitTags: string[], private _implicitAttrs: {[k: string]: string[]}) {}
|
constructor(private _implicitTags: string[], private _implicitAttrs: {[k: string]: string[]}) {}
|
||||||
@ -311,21 +313,20 @@ class _Visitor implements html.Visitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add a translatable message
|
// add a translatable message
|
||||||
private _addMessage(ast: html.Node[], meaningAndDesc?: string): i18n.Message {
|
private _addMessage(ast: html.Node[], msgMeta?: 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] = _splitMeaningAndDesc(meaningAndDesc);
|
const {meaning, description, id} = _parseMessageMeta(msgMeta);
|
||||||
const message = this._createI18nMessage(ast, meaning, description);
|
const message = this._createI18nMessage(ast, meaning, description, id);
|
||||||
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) {
|
||||||
@ -350,7 +351,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)] =
|
||||||
_splitMeaningAndDesc(attr.value)[0];
|
_parseMessageMeta(attr.value).meaning;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -364,12 +365,10 @@ 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.length == 0) {
|
if (nodes[0] instanceof html.Text) {
|
||||||
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 {
|
||||||
@ -480,8 +479,16 @@ 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 _splitMeaningAndDesc(i18n: string): [string, string] {
|
function _parseMessageMeta(i18n: string): {meaning: string, description: string, id: string} {
|
||||||
if (!i18n) return ['', ''];
|
if (!i18n) return {meaning: '', description: '', id: ''};
|
||||||
const pipeIndex = i18n.indexOf('|');
|
|
||||||
return pipeIndex == -1 ? ['', i18n] : [i18n.slice(0, pipeIndex), i18n.slice(pipeIndex + 1)];
|
const idIndex = i18n.indexOf(ID_SEPARATOR);
|
||||||
|
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,11 +15,12 @@ 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 description: string, public id: string) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Node {
|
export interface Node {
|
||||||
@ -78,3 +79,56 @@ export interface Visitor {
|
|||||||
visitPlaceholder(ph: Placeholder, context?: any): any;
|
visitPlaceholder(ph: Placeholder, context?: any): any;
|
||||||
visitIcuPlaceholder(ph: IcuPlaceholder, context?: any): any;
|
visitIcuPlaceholder(ph: IcuPlaceholder, context?: any): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clone the AST
|
||||||
|
export class CloneVisitor implements Visitor {
|
||||||
|
visitText(text: Text, context?: any): Text { return new Text(text.value, text.sourceSpan); }
|
||||||
|
|
||||||
|
visitContainer(container: Container, context?: any): Container {
|
||||||
|
const children = container.children.map(n => n.visit(this, context));
|
||||||
|
return new Container(children, container.sourceSpan);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitIcu(icu: Icu, context?: any): Icu {
|
||||||
|
const cases: {[k: string]: Node} = {};
|
||||||
|
Object.keys(icu.cases).forEach(key => cases[key] = icu.cases[key].visit(this, context));
|
||||||
|
const msg = new Icu(icu.expression, icu.type, cases, icu.sourceSpan);
|
||||||
|
msg.expressionPlaceholder = icu.expressionPlaceholder;
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitTagPlaceholder(ph: TagPlaceholder, context?: any): TagPlaceholder {
|
||||||
|
const children = ph.children.map(n => n.visit(this, context));
|
||||||
|
return new TagPlaceholder(
|
||||||
|
ph.tag, ph.attrs, ph.startName, ph.closeName, children, ph.isVoid, ph.sourceSpan);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitPlaceholder(ph: Placeholder, context?: any): Placeholder {
|
||||||
|
return new Placeholder(ph.value, ph.name, ph.sourceSpan);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitIcuPlaceholder(ph: IcuPlaceholder, context?: any): IcuPlaceholder {
|
||||||
|
return new IcuPlaceholder(ph.value, ph.name, ph.sourceSpan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visit all the nodes recursively
|
||||||
|
export class RecurseVisitor implements Visitor {
|
||||||
|
visitText(text: Text, context?: any): any{};
|
||||||
|
|
||||||
|
visitContainer(container: Container, context?: any): any {
|
||||||
|
container.children.forEach(child => child.visit(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitIcu(icu: Icu, context?: any): any {
|
||||||
|
Object.keys(icu.cases).forEach(k => { icu.cases[k].visit(this); });
|
||||||
|
}
|
||||||
|
|
||||||
|
visitTagPlaceholder(ph: TagPlaceholder, context?: any): any {
|
||||||
|
ph.children.forEach(child => child.visit(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitPlaceholder(ph: Placeholder, context?: any): any{};
|
||||||
|
|
||||||
|
visitIcuPlaceholder(ph: IcuPlaceholder, context?: any): any{};
|
||||||
|
}
|
@ -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) => i18n.Message {
|
nodes: html.Node[], meaning: string, description: string, id: string) => i18n.Message {
|
||||||
const visitor = new _I18nVisitor(_expParser, interpolationConfig);
|
const visitor = new _I18nVisitor(_expParser, interpolationConfig);
|
||||||
|
|
||||||
return (nodes: html.Node[], meaning: string, description: string) =>
|
return (nodes: html.Node[], meaning: string, description: string, id: string) =>
|
||||||
visitor.toI18nMessage(nodes, meaning, description);
|
visitor.toI18nMessage(nodes, meaning, description, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _I18nVisitor implements html.Visitor {
|
class _I18nVisitor implements html.Visitor {
|
||||||
@ -40,7 +40,8 @@ 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): i18n.Message {
|
public toI18nMessage(nodes: html.Node[], meaning: string, description: string, id: string):
|
||||||
|
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();
|
||||||
@ -50,7 +51,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);
|
i18nodes, this._placeholderToContent, this._placeholderToMessage, meaning, description, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitElement(el: html.Element, context: any): i18n.Node {
|
visitElement(el: html.Element, context: any): i18n.Node {
|
||||||
@ -115,7 +116,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,14 +11,15 @@ import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
|||||||
import {ParseError} from '../parse_util';
|
import {ParseError} from '../parse_util';
|
||||||
|
|
||||||
import {extractMessages} from './extractor_merger';
|
import {extractMessages} from './extractor_merger';
|
||||||
import {Message} from './i18n_ast';
|
import * as i18n from './i18n_ast';
|
||||||
import {Serializer} from './serializers/serializer';
|
import {PlaceholderMapper, Serializer} from './serializers/serializer';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A container for message extracted from the templates.
|
* A container for message extracted from the templates.
|
||||||
*/
|
*/
|
||||||
export class MessageBundle {
|
export class MessageBundle {
|
||||||
private _messages: Message[] = [];
|
private _messages: i18n.Message[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _htmlParser: HtmlParser, private _implicitTags: string[],
|
private _htmlParser: HtmlParser, private _implicitTags: string[],
|
||||||
@ -42,7 +43,53 @@ export class MessageBundle {
|
|||||||
this._messages.push(...i18nParserResult.messages);
|
this._messages.push(...i18nParserResult.messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMessages(): Message[] { return this._messages; }
|
// Return the message in the internal format
|
||||||
|
// The public (serialized) format might be different, see the `write` method.
|
||||||
|
getMessages(): i18n.Message[] { return this._messages; }
|
||||||
|
|
||||||
write(serializer: Serializer): string { return serializer.write(this._messages); }
|
write(serializer: Serializer): string {
|
||||||
|
const messages: {[id: string]: i18n.Message} = {};
|
||||||
|
const mapperVisitor = new MapPlaceholderNames();
|
||||||
|
|
||||||
|
// Deduplicate messages based on their ID
|
||||||
|
this._messages.forEach(message => {
|
||||||
|
const id = serializer.digest(message);
|
||||||
|
if (!messages.hasOwnProperty(id)) {
|
||||||
|
messages[id] = message;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Transform placeholder names using the serializer mapping
|
||||||
|
const msgList = Object.keys(messages).map(id => {
|
||||||
|
const mapper = serializer.createNameMapper(messages[id]);
|
||||||
|
const src = messages[id];
|
||||||
|
const nodes = mapper ? mapperVisitor.convert(src.nodes, mapper) : src.nodes;
|
||||||
|
return new i18n.Message(nodes, {}, {}, src.meaning, src.description, id);
|
||||||
|
});
|
||||||
|
|
||||||
|
return serializer.write(msgList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform an i18n AST by renaming the placeholder nodes with the given mapper
|
||||||
|
class MapPlaceholderNames extends i18n.CloneVisitor {
|
||||||
|
convert(nodes: i18n.Node[], mapper: PlaceholderMapper): i18n.Node[] {
|
||||||
|
return mapper ? nodes.map(n => n.visit(this, mapper)) : nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitTagPlaceholder(ph: i18n.TagPlaceholder, mapper: PlaceholderMapper): i18n.TagPlaceholder {
|
||||||
|
const startName = mapper.toPublicName(ph.startName);
|
||||||
|
const closeName = ph.closeName ? mapper.toPublicName(ph.closeName) : ph.closeName;
|
||||||
|
const children = ph.children.map(n => n.visit(this, mapper));
|
||||||
|
return new i18n.TagPlaceholder(
|
||||||
|
ph.tag, ph.attrs, startName, closeName, children, ph.isVoid, ph.sourceSpan);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitPlaceholder(ph: i18n.Placeholder, mapper: PlaceholderMapper): i18n.Placeholder {
|
||||||
|
return new i18n.Placeholder(ph.value, mapper.toPublicName(ph.name), ph.sourceSpan);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, mapper: PlaceholderMapper): i18n.IcuPlaceholder {
|
||||||
|
return new i18n.IcuPlaceholder(ph.value, mapper.toPublicName(ph.name), ph.sourceSpan);
|
||||||
|
}
|
||||||
}
|
}
|
@ -9,6 +9,9 @@
|
|||||||
import * as i18n from '../i18n_ast';
|
import * as i18n from '../i18n_ast';
|
||||||
|
|
||||||
export abstract class Serializer {
|
export abstract class Serializer {
|
||||||
|
// - The `placeholders` and `placeholderToMessage` properties are irrelevant in the input messages
|
||||||
|
// - The `id` contains the message id that the serializer is expected to use
|
||||||
|
// - Placeholder names are already map to public names using the provided mapper
|
||||||
abstract write(messages: i18n.Message[]): string;
|
abstract write(messages: i18n.Message[]): string;
|
||||||
|
|
||||||
abstract load(content: string, url: string): {[msgId: string]: i18n.Node[]};
|
abstract load(content: string, url: string): {[msgId: string]: i18n.Node[]};
|
||||||
@ -31,3 +34,64 @@ export interface PlaceholderMapper {
|
|||||||
|
|
||||||
toInternalName(publicName: string): string;
|
toInternalName(publicName: string): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple mapper that take a function to transform an internal name to a public name
|
||||||
|
*/
|
||||||
|
export class SimplePlaceholderMapper extends i18n.RecurseVisitor implements PlaceholderMapper {
|
||||||
|
private internalToPublic: {[k: string]: string} = {};
|
||||||
|
private publicToNextId: {[k: string]: number} = {};
|
||||||
|
private publicToInternal: {[k: string]: string} = {};
|
||||||
|
|
||||||
|
// create a mapping from the message
|
||||||
|
constructor(message: i18n.Message, private mapName: (name: string) => string) {
|
||||||
|
super();
|
||||||
|
message.nodes.forEach(node => node.visit(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
toPublicName(internalName: string): string {
|
||||||
|
return this.internalToPublic.hasOwnProperty(internalName) ?
|
||||||
|
this.internalToPublic[internalName] :
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
|
||||||
|
toInternalName(publicName: string): string {
|
||||||
|
return this.publicToInternal.hasOwnProperty(publicName) ? this.publicToInternal[publicName] :
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitText(text: i18n.Text, context?: any): any { return null; }
|
||||||
|
|
||||||
|
visitTagPlaceholder(ph: i18n.TagPlaceholder, context?: any): any {
|
||||||
|
this.visitPlaceholderName(ph.startName);
|
||||||
|
super.visitTagPlaceholder(ph, context);
|
||||||
|
this.visitPlaceholderName(ph.closeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitPlaceholder(ph: i18n.Placeholder, context?: any): any { this.visitPlaceholderName(ph.name); }
|
||||||
|
|
||||||
|
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any {
|
||||||
|
this.visitPlaceholderName(ph.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// XMB placeholders could only contains A-Z, 0-9 and _
|
||||||
|
private visitPlaceholderName(internalName: string): void {
|
||||||
|
if (!internalName || this.internalToPublic.hasOwnProperty(internalName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let publicName = this.mapName(internalName);
|
||||||
|
|
||||||
|
if (this.publicToInternal.hasOwnProperty(publicName)) {
|
||||||
|
// Create a new XMB when it has already been used
|
||||||
|
const nextId = this.publicToNextId[publicName];
|
||||||
|
this.publicToNextId[publicName] = nextId + 1;
|
||||||
|
publicName = `${publicName}_${nextId}`;
|
||||||
|
} else {
|
||||||
|
this.publicToNextId[publicName] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.internalToPublic[internalName] = publicName;
|
||||||
|
this.publicToInternal[publicName] = internalName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -30,17 +30,10 @@ const _UNIT_TAG = 'trans-unit';
|
|||||||
export class Xliff extends 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 transUnits: xml.Node[] = [];
|
const transUnits: xml.Node[] = [];
|
||||||
|
|
||||||
messages.forEach(message => {
|
messages.forEach(message => {
|
||||||
const id = this.digest(message);
|
const transUnit = new xml.Tag(_UNIT_TAG, {id: message.id, datatype: 'html'});
|
||||||
|
|
||||||
// deduplicate messages
|
|
||||||
if (visited[id]) return;
|
|
||||||
visited[id] = true;
|
|
||||||
|
|
||||||
const transUnit = new xml.Tag(_UNIT_TAG, {id, datatype: 'html'});
|
|
||||||
transUnit.children.push(
|
transUnit.children.push(
|
||||||
new xml.CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)),
|
new xml.CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)),
|
||||||
new xml.CR(8), new xml.Tag(_TARGET_TAG));
|
new xml.CR(8), new xml.Tag(_TARGET_TAG));
|
||||||
|
@ -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 {PlaceholderMapper, Serializer} from './serializer';
|
import {PlaceholderMapper, Serializer, SimplePlaceholderMapper} from './serializer';
|
||||||
import * as xml from './xml_helper';
|
import * as xml from './xml_helper';
|
||||||
|
|
||||||
const _MESSAGES_TAG = 'messagebundle';
|
const _MESSAGES_TAG = 'messagebundle';
|
||||||
@ -41,19 +41,10 @@ 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();
|
||||||
const visited: {[id: string]: boolean} = {};
|
|
||||||
let rootNode = new xml.Tag(_MESSAGES_TAG);
|
let rootNode = new xml.Tag(_MESSAGES_TAG);
|
||||||
|
|
||||||
messages.forEach(message => {
|
messages.forEach(message => {
|
||||||
const id = this.digest(message);
|
const attrs: {[k: string]: string} = {id: message.id};
|
||||||
|
|
||||||
// deduplicate messages
|
|
||||||
if (visited[id]) return;
|
|
||||||
visited[id] = true;
|
|
||||||
|
|
||||||
const mapper = this.createNameMapper(message);
|
|
||||||
|
|
||||||
const attrs: {[k: string]: string} = {id};
|
|
||||||
|
|
||||||
if (message.description) {
|
if (message.description) {
|
||||||
attrs['desc'] = message.description;
|
attrs['desc'] = message.description;
|
||||||
@ -64,8 +55,7 @@ export class Xmb extends Serializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rootNode.children.push(
|
rootNode.children.push(
|
||||||
new xml.CR(2),
|
new xml.CR(2), new xml.Tag(_MESSAGE_TAG, attrs, visitor.serialize(message.nodes)));
|
||||||
new xml.Tag(_MESSAGE_TAG, attrs, visitor.serialize(message.nodes, {mapper})));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
rootNode.children.push(new xml.CR());
|
rootNode.children.push(new xml.CR());
|
||||||
@ -88,26 +78,24 @@ export class Xmb extends Serializer {
|
|||||||
|
|
||||||
|
|
||||||
createNameMapper(message: i18n.Message): PlaceholderMapper {
|
createNameMapper(message: i18n.Message): PlaceholderMapper {
|
||||||
return new XmbPlaceholderMapper(message);
|
return new SimplePlaceholderMapper(message, toPublicName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Visitor implements i18n.Visitor {
|
class _Visitor implements i18n.Visitor {
|
||||||
visitText(text: i18n.Text, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
visitText(text: i18n.Text, context?: any): xml.Node[] { return [new xml.Text(text.value)]; }
|
||||||
return [new xml.Text(text.value)];
|
|
||||||
}
|
|
||||||
|
|
||||||
visitContainer(container: i18n.Container, ctx: any): xml.Node[] {
|
visitContainer(container: i18n.Container, context: any): xml.Node[] {
|
||||||
const nodes: xml.Node[] = [];
|
const nodes: xml.Node[] = [];
|
||||||
container.children.forEach((node: i18n.Node) => nodes.push(...node.visit(this, ctx)));
|
container.children.forEach((node: i18n.Node) => nodes.push(...node.visit(this)));
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitIcu(icu: i18n.Icu, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
visitIcu(icu: i18n.Icu, context?: any): 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, ctx), new xml.Text(`} `));
|
nodes.push(new xml.Text(`${c} {`), ...icu.cases[c].visit(this), new xml.Text(`} `));
|
||||||
});
|
});
|
||||||
|
|
||||||
nodes.push(new xml.Text(`}`));
|
nodes.push(new xml.Text(`}`));
|
||||||
@ -115,34 +103,30 @@ class _Visitor implements i18n.Visitor {
|
|||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitTagPlaceholder(ph: i18n.TagPlaceholder, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
visitTagPlaceholder(ph: i18n.TagPlaceholder, context?: any): 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}>`)]);
|
||||||
let name = ctx.mapper.toPublicName(ph.startName);
|
const startTagPh = new xml.Tag(_PLACEHOLDER_TAG, {name: ph.startName}, [startEx]);
|
||||||
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}>`)]);
|
||||||
name = ctx.mapper.toPublicName(ph.closeName);
|
const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {name: ph.closeName}, [closeEx]);
|
||||||
const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {name}, [closeEx]);
|
|
||||||
|
|
||||||
return [startTagPh, ...this.serialize(ph.children, ctx), closeTagPh];
|
return [startTagPh, ...this.serialize(ph.children), closeTagPh];
|
||||||
}
|
}
|
||||||
|
|
||||||
visitPlaceholder(ph: i18n.Placeholder, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
visitPlaceholder(ph: i18n.Placeholder, context?: any): xml.Node[] {
|
||||||
const name = ctx.mapper.toPublicName(ph.name);
|
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name})];
|
||||||
return [new xml.Tag(_PLACEHOLDER_TAG, {name})];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): xml.Node[] {
|
||||||
const name = ctx.mapper.toPublicName(ph.name);
|
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name})];
|
||||||
return [new xml.Tag(_PLACEHOLDER_TAG, {name})];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(nodes: i18n.Node[], ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
serialize(nodes: i18n.Node[]): xml.Node[] {
|
||||||
return [].concat(...nodes.map(node => node.visit(this, ctx)));
|
return [].concat(...nodes.map(node => node.visit(this)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,68 +157,7 @@ class ExampleVisitor implements xml.IVisitor {
|
|||||||
visitDoctype(doctype: xml.Doctype): void {}
|
visitDoctype(doctype: xml.Doctype): void {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// XMB/XTB placeholders can only contain A-Z, 0-9 and _
|
||||||
* XMB/XTB placeholders can only contain A-Z, 0-9 and _
|
export function toPublicName(internalName: string): string {
|
||||||
*
|
return internalName.toUpperCase().replace(/[^A-Z0-9_]/g, '_');
|
||||||
* 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,8 +11,8 @@ 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 {PlaceholderMapper, Serializer} from './serializer';
|
import {PlaceholderMapper, Serializer, SimplePlaceholderMapper} from './serializer';
|
||||||
import {XmbPlaceholderMapper, digest} from './xmb';
|
import {digest, toPublicName} from './xmb';
|
||||||
|
|
||||||
const _TRANSLATIONS_TAG = 'translationbundle';
|
const _TRANSLATIONS_TAG = 'translationbundle';
|
||||||
const _TRANSLATION_TAG = 'translation';
|
const _TRANSLATION_TAG = 'translation';
|
||||||
@ -45,7 +45,7 @@ export class Xtb extends Serializer {
|
|||||||
digest(message: i18n.Message): string { return digest(message); }
|
digest(message: i18n.Message): string { return digest(message); }
|
||||||
|
|
||||||
createNameMapper(message: i18n.Message): PlaceholderMapper {
|
createNameMapper(message: i18n.Message): PlaceholderMapper {
|
||||||
return new XmbPlaceholderMapper(message);
|
return new SimplePlaceholderMapper(message, toPublicName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,9 +8,8 @@
|
|||||||
|
|
||||||
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 {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 {CompileIdentifierMetadata, CompileTokenMetadata} 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,8 +160,6 @@ 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,
|
||||||
@ -173,6 +170,36 @@ 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 = {
|
||||||
|
@ -6,13 +6,12 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Compiler, ComponentFactory, Injector, ModuleWithComponentFactories, NgModuleFactory, SchemaMetadata, Type} from '@angular/core';
|
import {Compiler, ComponentFactory, Injector, ModuleWithComponentFactories, NgModuleFactory, Type} 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, ProviderMeta, createHostComponentMeta, identifierName} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, ProviderMeta, ProxyClass, createHostComponentMeta, identifierName} from '../compile_metadata';
|
||||||
import {CompilerConfig} from '../config';
|
import {CompilerConfig} from '../config';
|
||||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
|
||||||
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||||
import {stringify} from '../facade/lang';
|
import {stringify} from '../facade/lang';
|
||||||
import {CompilerInjectable} from '../injectable';
|
import {CompilerInjectable} from '../injectable';
|
||||||
@ -21,10 +20,11 @@ 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, DirectiveWrapperDependency, ViewClassDependency, ViewCompiler} from '../view_compiler/view_compiler';
|
import {ViewCompiler} from '../view_compiler/view_compiler';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ export class JitCompiler implements Compiler {
|
|||||||
// Note: the loadingPromise for a module only includes the loading of the exported directives
|
// Note: the loadingPromise for a module only includes the loading of the exported directives
|
||||||
// of imported modules.
|
// of imported modules.
|
||||||
// However, for runtime compilation, we want to transitively compile all modules,
|
// However, for runtime compilation, we want to transitively compile all modules,
|
||||||
// so we also need to call loadNgModuleDirectiveAndPipeMetadata for all nested modules.
|
// so we also need to call loadNgModuleMetadata for all nested modules.
|
||||||
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
|
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
|
||||||
loadingPromises.push(this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
loadingPromises.push(this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
||||||
localModuleMeta.reference, isSync));
|
localModuleMeta.reference, isSync));
|
||||||
@ -130,10 +130,6 @@ 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);
|
||||||
@ -168,7 +164,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(template.proxyComponentFactory);
|
allComponentFactories.push(<ComponentFactory<any>>dirMeta.componentFactory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -180,15 +176,16 @@ 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.reference);
|
const moduleMeta = moduleByDirective.get(entryComponentType.componentType);
|
||||||
templates.add(
|
templates.add(
|
||||||
this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta));
|
this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
localModuleMeta.entryComponents.forEach((entryComponentType) => {
|
localModuleMeta.entryComponents.forEach((entryComponentType) => {
|
||||||
const moduleMeta = moduleByDirective.get(entryComponentType.reference);
|
const moduleMeta = moduleByDirective.get(entryComponentType.componentType);
|
||||||
templates.add(this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta));
|
templates.add(
|
||||||
|
this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
templates.forEach((template) => this._compileTemplate(template));
|
templates.forEach((template) => this._compileTemplate(template));
|
||||||
@ -222,12 +219,12 @@ export class JitCompiler implements Compiler {
|
|||||||
const compMeta = this._metadataResolver.getDirectiveMetadata(compType);
|
const compMeta = this._metadataResolver.getDirectiveMetadata(compType);
|
||||||
assertComponent(compMeta);
|
assertComponent(compMeta);
|
||||||
|
|
||||||
const HostClass = function HostClass() {};
|
const componentFactory = <ComponentFactory<any>>compMeta.componentFactory;
|
||||||
(<any>HostClass).overriddenName = `${identifierName(compMeta.type)}_Host`;
|
const hostClass = this._metadataResolver.getHostComponentType(compType);
|
||||||
|
const hostMeta = createHostComponentMeta(
|
||||||
const hostMeta = createHostComponentMeta(HostClass, compMeta);
|
hostClass, compMeta, <any>view_utils.getComponentFactoryViewClass(componentFactory));
|
||||||
compiledTemplate = new CompiledTemplate(
|
compiledTemplate =
|
||||||
true, compMeta.selector, compMeta.type, hostMeta, ngModule, [compMeta.type]);
|
new CompiledTemplate(true, compMeta.type, hostMeta, ngModule, [compMeta.type]);
|
||||||
this._compiledHostTemplateCache.set(compType, compiledTemplate);
|
this._compiledHostTemplateCache.set(compType, compiledTemplate);
|
||||||
}
|
}
|
||||||
return compiledTemplate;
|
return compiledTemplate;
|
||||||
@ -239,32 +236,12 @@ export class JitCompiler implements Compiler {
|
|||||||
if (!compiledTemplate) {
|
if (!compiledTemplate) {
|
||||||
assertComponent(compMeta);
|
assertComponent(compMeta);
|
||||||
compiledTemplate = new CompiledTemplate(
|
compiledTemplate = new CompiledTemplate(
|
||||||
false, compMeta.selector, compMeta.type, compMeta, ngModule,
|
false, compMeta.type, compMeta, ngModule, ngModule.transitiveModule.directives);
|
||||||
ngModule.transitiveModule.directives);
|
|
||||||
this._compiledTemplateCache.set(compMeta.type.reference, compiledTemplate);
|
this._compiledTemplateCache.set(compMeta.type.reference, compiledTemplate);
|
||||||
}
|
}
|
||||||
return compiledTemplate;
|
return compiledTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _assertComponentKnown(compType: any, isHost: boolean): CompiledTemplate {
|
|
||||||
const compiledTemplate = isHost ? this._compiledHostTemplateCache.get(compType) :
|
|
||||||
this._compiledTemplateCache.get(compType);
|
|
||||||
if (!compiledTemplate) {
|
|
||||||
throw new Error(
|
|
||||||
`Illegal state: Compiled view for component ${stringify(compType)} (host: ${isHost}) does not exist!`);
|
|
||||||
}
|
|
||||||
return compiledTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _assertDirectiveWrapper(dirType: any): Type<any> {
|
|
||||||
const dirWrapper = this._compiledDirectiveWrapperCache.get(dirType);
|
|
||||||
if (!dirWrapper) {
|
|
||||||
throw new Error(
|
|
||||||
`Illegal state: Directive wrapper for ${stringify(dirType)} has not been compiled!`);
|
|
||||||
}
|
|
||||||
return dirWrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _compileDirectiveWrapper(
|
private _compileDirectiveWrapper(
|
||||||
dirMeta: CompileDirectiveMetadata, moduleMeta: CompileNgModuleMetadata): void {
|
dirMeta: CompileDirectiveMetadata, moduleMeta: CompileNgModuleMetadata): void {
|
||||||
const compileResult = this._directiveWrapperCompiler.compile(dirMeta);
|
const compileResult = this._directiveWrapperCompiler.compile(dirMeta);
|
||||||
@ -277,6 +254,7 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,21 +282,6 @@ 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);
|
||||||
@ -358,30 +321,16 @@ 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, selector: string, public compType: CompileIdentifierMetadata,
|
public isHost: boolean, public compType: CompileIdentifierMetadata,
|
||||||
public compMeta: CompileDirectiveMetadata, public ngModule: CompileNgModuleMetadata,
|
public compMeta: CompileDirectiveMetadata, public ngModule: CompileNgModuleMetadata,
|
||||||
public directives: CompileIdentifierMetadata[]) {
|
public directives: CompileIdentifierMetadata[]) {}
|
||||||
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;
|
||||||
this.proxyViewClass.prototype = viewClass.prototype;
|
(<ProxyClass>this.compMeta.componentViewType).setDelegate(viewClass);
|
||||||
this.isCompiled = true;
|
this.isCompiled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, OpaqueToken, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
|
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, InjectionToken, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
|
||||||
|
|
||||||
import {AnimationParser} from '../animation/animation_parser';
|
import {AnimationParser} from '../animation/animation_parser';
|
||||||
import {CompilerConfig} from '../config';
|
import {CompilerConfig} from '../config';
|
||||||
@ -40,7 +40,7 @@ const _NO_RESOURCE_LOADER: ResourceLoader = {
|
|||||||
`No ResourceLoader implementation has been provided. Can't read the url "${url}"`);}
|
`No ResourceLoader implementation has been provided. Can't read the url "${url}"`);}
|
||||||
};
|
};
|
||||||
|
|
||||||
const baseHtmlParser = new OpaqueToken('HtmlParser');
|
const baseHtmlParser = new InjectionToken('HtmlParser');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A set of providers that provide `JitCompiler` and its dependencies to use for
|
* A set of providers that provide `JitCompiler` and its dependencies to use for
|
||||||
|
@ -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, 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, ComponentFactory, Directive, Host, Inject, Injectable, InjectionToken, ModuleWithProviders, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
|
||||||
|
|
||||||
import {StaticSymbol} from './aot/static_symbol';
|
import {StaticSymbol, StaticSymbolCache} 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 {ListWrapper, StringMapWrapper} from './facade/collection';
|
import {stringify} from './facade/lang';
|
||||||
import {isBlank, isPresent, stringify} from './facade/lang';
|
import {Identifiers, resolveIdentifier} from './identifiers';
|
||||||
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,10 +24,10 @@ 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, SyncAsyncResult, SyntaxError, ValueTransformer, visitValue} from './util';
|
import {MODULE_SUFFIX, 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 InjectionToken('ErrorCollector');
|
||||||
|
|
||||||
// Design notes:
|
// Design notes:
|
||||||
// - don't lazily create metadata:
|
// - don't lazily create metadata:
|
||||||
@ -38,6 +38,8 @@ 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>();
|
||||||
@ -49,12 +51,14 @@ 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);
|
||||||
@ -65,8 +69,9 @@ export class CompileMetadataResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCache() {
|
clearCache(): void {
|
||||||
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();
|
||||||
@ -74,6 +79,66 @@ 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);
|
||||||
@ -162,6 +227,9 @@ 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);
|
||||||
@ -201,7 +269,14 @@ 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);
|
||||||
const dirMeta = this._directiveResolver.resolve(directiveType);
|
if (!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;
|
||||||
}
|
}
|
||||||
@ -230,7 +305,7 @@ export class CompileMetadataResolver {
|
|||||||
|
|
||||||
let changeDetectionStrategy: ChangeDetectionStrategy = null;
|
let changeDetectionStrategy: ChangeDetectionStrategy = null;
|
||||||
let viewProviders: cpl.CompileProviderMetadata[] = [];
|
let viewProviders: cpl.CompileProviderMetadata[] = [];
|
||||||
let entryComponentMetadata: cpl.CompileIdentifierMetadata[] = [];
|
let entryComponentMetadata: cpl.CompileEntryComponentMetadata[] = [];
|
||||||
let selector = dirMeta.selector;
|
let selector = dirMeta.selector;
|
||||||
|
|
||||||
if (dirMeta instanceof Component) {
|
if (dirMeta instanceof Component) {
|
||||||
@ -243,7 +318,7 @@ export class CompileMetadataResolver {
|
|||||||
}
|
}
|
||||||
if (dirMeta.entryComponents) {
|
if (dirMeta.entryComponents) {
|
||||||
entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents)
|
entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents)
|
||||||
.map((type) => this._getIdentifierMetadata(type))
|
.map((type) => this._getEntryComponentMetadata(type))
|
||||||
.concat(entryComponentMetadata);
|
.concat(entryComponentMetadata);
|
||||||
}
|
}
|
||||||
if (!selector) {
|
if (!selector) {
|
||||||
@ -261,14 +336,14 @@ export class CompileMetadataResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let providers: cpl.CompileProviderMetadata[] = [];
|
let providers: cpl.CompileProviderMetadata[] = [];
|
||||||
if (isPresent(dirMeta.providers)) {
|
if (dirMeta.providers != null) {
|
||||||
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 (isPresent(dirMeta.queries)) {
|
if (dirMeta.queries != null) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
@ -287,21 +362,29 @@ 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
|
||||||
});
|
});
|
||||||
return {metadata, annotation: dirMeta};
|
cacheEntry = {metadata, annotation: dirMeta};
|
||||||
|
this._nonNormalizedDirectiveCache.set(directiveType, cacheEntry);
|
||||||
|
return cacheEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the metadata for the given directive.
|
* Gets the metadata for the given directive.
|
||||||
* This assumes `loadNgModuleDirectiveAndPipeMetadata` has been called first.
|
* This assumes `loadNgModuleMetadata` has been called first.
|
||||||
*/
|
*/
|
||||||
getDirectiveMetadata(directiveType: any): cpl.CompileDirectiveMetadata {
|
getDirectiveMetadata(directiveType: any): cpl.CompileDirectiveMetadata {
|
||||||
const dirMeta = this._directiveCache.get(directiveType);
|
const dirMeta = this._directiveCache.get(directiveType);
|
||||||
if (!dirMeta) {
|
if (!dirMeta) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new SyntaxError(
|
new SyntaxError(
|
||||||
`Illegal state: getDirectiveMetadata can only be called after loadNgModuleDirectiveAndPipeMetadata for a module that declares it. Directive ${stringifyType(directiveType)}.`),
|
`Illegal state: getDirectiveMetadata can only be called after loadNgModuleMetadata for a module that declares it. Directive ${stringifyType(directiveType)}.`),
|
||||||
directiveType);
|
directiveType);
|
||||||
}
|
}
|
||||||
return dirMeta;
|
return dirMeta;
|
||||||
@ -371,7 +454,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.CompileIdentifierMetadata[] = [];
|
const entryComponents: cpl.CompileEntryComponentMetadata[] = [];
|
||||||
const bootstrapComponents: cpl.CompileIdentifierMetadata[] = [];
|
const bootstrapComponents: cpl.CompileIdentifierMetadata[] = [];
|
||||||
const schemas: SchemaMetadata[] = [];
|
const schemas: SchemaMetadata[] = [];
|
||||||
|
|
||||||
@ -488,7 +571,7 @@ export class CompileMetadataResolver {
|
|||||||
|
|
||||||
if (meta.entryComponents) {
|
if (meta.entryComponents) {
|
||||||
entryComponents.push(...flattenAndDedupeArray(meta.entryComponents)
|
entryComponents.push(...flattenAndDedupeArray(meta.entryComponents)
|
||||||
.map(type => this._getIdentifierMetadata(type)));
|
.map(type => this._getEntryComponentMetadata(type)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.bootstrap) {
|
if (meta.bootstrap) {
|
||||||
@ -504,7 +587,8 @@ export class CompileMetadataResolver {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
entryComponents.push(...bootstrapComponents);
|
entryComponents.push(
|
||||||
|
...bootstrapComponents.map(type => this._getEntryComponentMetadata(type.reference)));
|
||||||
|
|
||||||
if (meta.schemas) {
|
if (meta.schemas) {
|
||||||
schemas.push(...flattenAndDedupeArray(meta.schemas));
|
schemas.push(...flattenAndDedupeArray(meta.schemas));
|
||||||
@ -648,14 +732,14 @@ export class CompileMetadataResolver {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the metadata for the given pipe.
|
* Gets the metadata for the given pipe.
|
||||||
* This assumes `loadNgModuleDirectiveAndPipeMetadata` has been called first.
|
* This assumes `loadNgModuleMetadata` has been called first.
|
||||||
*/
|
*/
|
||||||
getPipeMetadata(pipeType: any): cpl.CompilePipeMetadata {
|
getPipeMetadata(pipeType: any): cpl.CompilePipeMetadata {
|
||||||
const pipeMeta = this._pipeCache.get(pipeType);
|
const pipeMeta = this._pipeCache.get(pipeType);
|
||||||
if (!pipeMeta) {
|
if (!pipeMeta) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
new SyntaxError(
|
new SyntaxError(
|
||||||
`Illegal state: getPipeMetadata can only be called after loadNgModuleDirectiveAndPipeMetadata for a module that declares it. Pipe ${stringifyType(pipeType)}.`),
|
`Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringifyType(pipeType)}.`),
|
||||||
pipeType);
|
pipeType);
|
||||||
}
|
}
|
||||||
return pipeMeta;
|
return pipeMeta;
|
||||||
@ -722,14 +806,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) && isBlank(token)) {
|
} else if (isValidType(paramEntry) && token == null) {
|
||||||
token = paramEntry;
|
token = paramEntry;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
token = param;
|
token = param;
|
||||||
}
|
}
|
||||||
if (isBlank(token)) {
|
if (token == null) {
|
||||||
hasUnknownDeps = true;
|
hasUnknownDeps = true;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -769,7 +853,7 @@ export class CompileMetadataResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _getProvidersMetadata(
|
private _getProvidersMetadata(
|
||||||
providers: Provider[], targetEntryComponents: cpl.CompileIdentifierMetadata[],
|
providers: Provider[], targetEntryComponents: cpl.CompileEntryComponentMetadata[],
|
||||||
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) => {
|
||||||
@ -778,10 +862,14 @@ 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(
|
||||||
@ -812,9 +900,19 @@ 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.CompileIdentifierMetadata[] {
|
cpl.CompileEntryComponentMetadata[] {
|
||||||
const components: cpl.CompileIdentifierMetadata[] = [];
|
const components: cpl.CompileEntryComponentMetadata[] = [];
|
||||||
const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
|
const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
|
||||||
|
|
||||||
if (provider.useFactory || provider.useExisting || provider.useClass) {
|
if (provider.useFactory || provider.useExisting || provider.useClass) {
|
||||||
@ -832,14 +930,27 @@ export class CompileMetadataResolver {
|
|||||||
|
|
||||||
extractIdentifiers(provider.useValue, collectedIdentifiers);
|
extractIdentifiers(provider.useValue, collectedIdentifiers);
|
||||||
collectedIdentifiers.forEach((identifier) => {
|
collectedIdentifiers.forEach((identifier) => {
|
||||||
if (this._directiveResolver.isDirective(identifier.reference) ||
|
const entry = this._getEntryComponentMetadata(identifier.reference);
|
||||||
this._loadSummary(identifier.reference, cpl.CompileSummaryKind.Directive)) {
|
if (entry) {
|
||||||
components.push(identifier);
|
components.push(entry);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
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;
|
||||||
|
@ -19,9 +19,12 @@ 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(
|
constructor(public compType: any) {}
|
||||||
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NgModuleCompileResult {
|
export class NgModuleCompileResult {
|
||||||
@ -46,13 +49,12 @@ 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.reference)) {
|
(id) => id.reference === entryComponent.componentType)) {
|
||||||
bootstrapComponentFactories.push(id);
|
bootstrapComponentFactories.push({reference: entryComponent.componentFactory});
|
||||||
}
|
}
|
||||||
deps.push(new ComponentFactoryDependency(entryComponent, id));
|
deps.push(new ComponentFactoryDependency(entryComponent.componentType));
|
||||||
return id;
|
return {reference: entryComponent.componentFactory};
|
||||||
});
|
});
|
||||||
const builder = new _InjectorBuilder(
|
const builder = new _InjectorBuilder(
|
||||||
ngModuleMeta, entryComponentFactories, bootstrapComponentFactories, sourceSpan);
|
ngModuleMeta, entryComponentFactories, bootstrapComponentFactories, sourceSpan);
|
||||||
|
@ -7,8 +7,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {identifierModuleUrl, identifierName} from '../compile_metadata';
|
import {StaticSymbol} from '../aot/static_symbol';
|
||||||
import {isBlank, isPresent} from '../facade/lang';
|
import {CompileIdentifierMetadata} from '../compile_metadata';
|
||||||
|
import {isBlank} from '../facade/lang';
|
||||||
|
|
||||||
import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
|
import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
|
||||||
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
|
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
|
||||||
@ -16,17 +17,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 _importGenerator: ImportResolver) {}
|
constructor(private _importResolver: ImportResolver) {}
|
||||||
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
|
emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string {
|
||||||
const converter = new JsEmitterVisitor(moduleUrl);
|
const converter = new JsEmitterVisitor(genFilePath, this._importResolver);
|
||||||
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, importedModuleUrl) => {
|
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(
|
||||||
`var ${prefix} = req` +
|
`var ${prefix} = req` +
|
||||||
`uire('${this._importGenerator.fileNameToModuleName(importedModuleUrl, moduleUrl)}');`);
|
`uire('${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}');`);
|
||||||
});
|
});
|
||||||
srcParts.push(ctx.toSource());
|
srcParts.push(ctx.toSource());
|
||||||
return srcParts.join('\n');
|
return srcParts.join('\n');
|
||||||
@ -36,20 +37,23 @@ 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 _moduleUrl: string) { super(); }
|
constructor(private _genFilePath: string, private _importResolver: ImportResolver) { 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 = identifierName(ast.value);
|
const {name, filePath} = this._resolveStaticSymbol(ast.value);
|
||||||
const moduleUrl = identifierModuleUrl(ast.value);
|
if (filePath != this._genFilePath) {
|
||||||
if (isBlank(name)) {
|
let prefix = this.importsWithPrefixes.get(filePath);
|
||||||
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(moduleUrl, prefix);
|
this.importsWithPrefixes.set(filePath, prefix);
|
||||||
}
|
}
|
||||||
ctx.print(`${prefix}.`);
|
ctx.print(`${prefix}.`);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
* 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.
|
||||||
*/
|
*/
|
||||||
@ -16,4 +18,10 @@ 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,18 +7,22 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {CompileIdentifierMetadata, identifierModuleUrl, identifierName} from '../compile_metadata';
|
import {StaticSymbol} from '../aot/static_symbol';
|
||||||
|
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 _debugModuleUrl = '/debug/lib';
|
const _debugFilePath = '/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(_debugModuleUrl);
|
const converter = new _TsEmitterVisitor(_debugFilePath, {
|
||||||
|
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];
|
||||||
|
|
||||||
@ -37,17 +41,23 @@ export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class TypeScriptEmitter implements OutputEmitter {
|
export class TypeScriptEmitter implements OutputEmitter {
|
||||||
constructor(private _importGenerator: ImportResolver) {}
|
constructor(private _importResolver: ImportResolver) {}
|
||||||
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
|
emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string {
|
||||||
const converter = new _TsEmitterVisitor(moduleUrl);
|
const converter = new _TsEmitterVisitor(genFilePath, this._importResolver);
|
||||||
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, importedModuleUrl) => {
|
converter.reexports.forEach((reexports, exportedFilePath) => {
|
||||||
|
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._importGenerator.fileNameToModuleName(importedModuleUrl, moduleUrl)}';`);
|
`ort * as ${prefix} from '${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}';`);
|
||||||
});
|
});
|
||||||
srcParts.push(ctx.toSource());
|
srcParts.push(ctx.toSource());
|
||||||
return srcParts.join('\n');
|
return srcParts.join('\n');
|
||||||
@ -55,9 +65,12 @@ export class TypeScriptEmitter implements OutputEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor {
|
class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor {
|
||||||
constructor(private _moduleUrl: string) { super(false); }
|
constructor(private _genFilePath: string, private _importResolver: ImportResolver) {
|
||||||
|
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)) {
|
||||||
@ -98,6 +111,19 @@ 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 `);
|
||||||
}
|
}
|
||||||
@ -320,25 +346,29 @@ 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 = identifierName(value);
|
const {name, filePath, members} = this._resolveStaticSymbol(value);
|
||||||
const moduleUrl = identifierModuleUrl(value);
|
if (filePath != this._genFilePath) {
|
||||||
if (isBlank(name)) {
|
let prefix = this.importsWithPrefixes.get(filePath);
|
||||||
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(moduleUrl, prefix);
|
this.importsWithPrefixes.set(filePath, prefix);
|
||||||
}
|
}
|
||||||
ctx.print(`${prefix}.`);
|
ctx.print(`${prefix}.`);
|
||||||
}
|
}
|
||||||
if (value.reference && value.reference.members && value.reference.members.length) {
|
if (members.length) {
|
||||||
ctx.print(value.reference.name);
|
ctx.print(name);
|
||||||
ctx.print('.');
|
ctx.print('.');
|
||||||
ctx.print(value.reference.members.join('.'));
|
ctx.print(members.join('.'));
|
||||||
} else {
|
} else {
|
||||||
ctx.print(name);
|
ctx.print(name);
|
||||||
}
|
}
|
||||||
|
@ -48,51 +48,6 @@ 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 {
|
||||||
@ -119,9 +74,47 @@ export class ParseError {
|
|||||||
public level: ParseErrorLevel = ParseErrorLevel.FATAL) {}
|
public level: ParseErrorLevel = ParseErrorLevel.FATAL) {}
|
||||||
|
|
||||||
toString(): string {
|
toString(): string {
|
||||||
const ctx = this.span.start.getContext(100, 3);
|
const source = this.span.start.file.content;
|
||||||
const contextStr = ctx ? ` ("${ctx.before}[ERROR ->]${ctx.after}")` : '';
|
let ctxStart = this.span.start.offset;
|
||||||
const details = this.span.details ? `, ${this.span.details}` : '';
|
let contextStr = '';
|
||||||
|
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}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ 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;
|
||||||
|
@ -16,6 +16,9 @@ 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; }
|
||||||
}
|
}
|
||||||
|
@ -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 {Inject, OpaqueToken, Optional, SchemaMetadata} from '@angular/core';
|
import {Inject, InjectionToken, Optional, SchemaMetadata} from '@angular/core';
|
||||||
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileTemplateSummary, CompileTokenMetadata, CompileTypeMetadata, identifierName} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileTemplateSummary, CompileTokenMetadata, CompileTypeMetadata, identifierName} from '../compile_metadata';
|
||||||
import {Parser} from '../expression_parser/parser';
|
import {Parser} from '../expression_parser/parser';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
@ -67,7 +67,7 @@ const TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
|
|||||||
*
|
*
|
||||||
* This is currently an internal-only feature and not meant for general use.
|
* This is currently an internal-only feature and not meant for general use.
|
||||||
*/
|
*/
|
||||||
export const TEMPLATE_TRANSFORMS = new OpaqueToken('TemplateTransforms');
|
export const TEMPLATE_TRANSFORMS = new InjectionToken('TemplateTransforms');
|
||||||
|
|
||||||
export class TemplateParseError extends ParseError {
|
export class TemplateParseError extends ParseError {
|
||||||
constructor(message: string, span: ParseSourceSpan, level: ParseErrorLevel) {
|
constructor(message: string, span: ParseSourceSpan, level: ParseErrorLevel) {
|
||||||
|
@ -6,12 +6,13 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, PACKAGE_ROOT_URL} from '@angular/core';
|
import {Inject, InjectionToken, PACKAGE_ROOT_URL} from '@angular/core';
|
||||||
|
|
||||||
import {isBlank, isPresent} from './facade/lang';
|
import {isBlank, isPresent} from './facade/lang';
|
||||||
import {CompilerInjectable} from './injectable';
|
import {CompilerInjectable} from './injectable';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link UrlResolver} with no package prefix.
|
* Create a {@link UrlResolver} with no package prefix.
|
||||||
*/
|
*/
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {CompileDiDependencyMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, identifierName, tokenName, tokenReference} from '../compile_metadata';
|
import {CompileDiDependencyMetadata, CompileDirectiveSummary, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, tokenName, tokenReference} from '../compile_metadata';
|
||||||
import {createDiTokenExpression} from '../compiler_util/identifier_util';
|
import {createDiTokenExpression} from '../compiler_util/identifier_util';
|
||||||
import {DirectiveWrapperCompiler, DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {Identifiers, createIdentifier, createIdentifierToken, identifierToken, resolveIdentifier} from '../identifiers';
|
import {Identifiers, createIdentifier, createIdentifierToken, identifierToken, resolveIdentifier} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
@ -97,11 +97,10 @@ export class CompileElement extends CompileNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _createComponentFactoryResolver() {
|
private _createComponentFactoryResolver() {
|
||||||
const entryComponents =
|
const entryComponents = this.component.entryComponents.map((entryComponent) => {
|
||||||
this.component.entryComponents.map((entryComponent: CompileIdentifierMetadata) => {
|
this.view.targetDependencies.push(
|
||||||
const id: CompileIdentifierMetadata = {reference: null};
|
new ComponentFactoryDependency(entryComponent.componentType));
|
||||||
this.view.targetDependencies.push(new ComponentFactoryDependency(entryComponent, id));
|
return {reference: entryComponent.componentFactory};
|
||||||
return id;
|
|
||||||
});
|
});
|
||||||
if (!entryComponents || entryComponents.length === 0) {
|
if (!entryComponents || entryComponents.length === 0) {
|
||||||
return;
|
return;
|
||||||
@ -179,11 +178,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 directiveWrapperIdentifier: CompileIdentifierMetadata = {reference: null};
|
const dirMeta =
|
||||||
this.view.targetDependencies.push(new DirectiveWrapperDependency(
|
this._directives.find(dir => dir.type.reference === provider.useClass.reference);
|
||||||
provider.useClass, DirectiveWrapperCompiler.dirWrapperClassName(provider.useClass),
|
this.view.targetDependencies.push(
|
||||||
directiveWrapperIdentifier));
|
new DirectiveWrapperDependency(dirMeta.type.reference));
|
||||||
return DirectiveWrapperExpressions.create(directiveWrapperIdentifier, depsExpr);
|
return DirectiveWrapperExpressions.create({reference: dirMeta.wrapperType}, 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));
|
||||||
|
@ -7,9 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
|
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeSummary, tokenName} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompilePipeSummary, tokenName, viewClassName} 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 * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
@ -19,8 +18,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, DirectiveWrapperDependency, ViewClassDependency} from './deps';
|
import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency} from './deps';
|
||||||
import {getPropertyInView, getViewClassName} from './util';
|
import {getPropertyInView} from './util';
|
||||||
|
|
||||||
export enum CompileViewRootNodeType {
|
export enum CompileViewRootNodeType {
|
||||||
Node,
|
Node,
|
||||||
@ -86,7 +85,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<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {
|
Array<ComponentViewDependency|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 +101,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 = getViewClassName(component, viewIndex);
|
this.className = viewClassName(component.type.reference, 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) {
|
||||||
|
@ -41,6 +41,7 @@ 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 {
|
||||||
@ -48,9 +49,3 @@ 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`);
|
|
||||||
}
|
|
||||||
|
@ -6,21 +6,26 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompileIdentifierMetadata} from '../compile_metadata';
|
/**
|
||||||
|
* This is currently not read, but will probably be used in the future.
|
||||||
export class ViewClassDependency {
|
* We keep it as we already pass it through all the right places...
|
||||||
constructor(
|
*/
|
||||||
public comp: CompileIdentifierMetadata, public name: string,
|
export class ComponentViewDependency {
|
||||||
public placeholder: CompileIdentifierMetadata) {}
|
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 right places...
|
||||||
|
*/
|
||||||
export class ComponentFactoryDependency {
|
export class ComponentFactoryDependency {
|
||||||
constructor(
|
constructor(public compType: any) {}
|
||||||
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 right places...
|
||||||
|
*/
|
||||||
export class DirectiveWrapperDependency {
|
export class DirectiveWrapperDependency {
|
||||||
constructor(
|
constructor(public dirType: any) {}
|
||||||
public dir: CompileIdentifierMetadata, public name: string,
|
|
||||||
public placeholder: CompileIdentifierMetadata) {}
|
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user