Compare commits
218 Commits
Author | SHA1 | Date | |
---|---|---|---|
540b1197a6 | |||
d30cc8461b | |||
f27954e62c | |||
69b52eb2b3 | |||
b9b557cdb0 | |||
a72a002a8d | |||
a0437f8c9d | |||
1c279b3264 | |||
cd03c77364 | |||
f6ef7d6e5a | |||
6aeaca3fb4 | |||
af62050729 | |||
cb69656b56 | |||
2fc0560988 | |||
86c50983d7 | |||
21976446e0 | |||
998ce9ad7e | |||
111523677c | |||
2d74a224d0 | |||
4d6ac9d414 | |||
6557bc34f6 | |||
e2622add07 | |||
ecfad467a1 | |||
5918133784 | |||
700bce9ec1 | |||
a64a35a8c1 | |||
b3dcff0cc1 | |||
124267c87a | |||
547bfa92ef | |||
d40bbf4d5c | |||
94b7031fe9 | |||
df0bf1dd74 | |||
c8a9b70890 | |||
efa2d80df8 | |||
a58e5efd09 | |||
86cf0ef892 | |||
5c568fab86 | |||
566104504c | |||
307d305b2d | |||
0a7364feea | |||
4544b1d7a6 | |||
9e0e6b59d1 | |||
14dd2b367a | |||
91eb8914dd | |||
77823d721f | |||
2afe2d107f | |||
17f40fb75f | |||
98936fdf16 | |||
7383e4a801 | |||
65c9b5b6aa | |||
5fab8710cb | |||
f106a18b96 | |||
8db184d349 | |||
c18eb298eb | |||
3f4aa59cfa | |||
79728b4c41 | |||
413167ab1b | |||
203cc7e1f1 | |||
b0cd514709 | |||
392c9ac214 | |||
a26e054857 | |||
c0b001a6af | |||
c8c1f22f9c | |||
e4d5a5f003 | |||
03d9de33a1 | |||
a8a80cf523 | |||
6c1d7908d5 | |||
9aab6d24eb | |||
5ee8155e4e | |||
21de0f239d | |||
13b41bd631 | |||
f3524af68f | |||
0a56f4ea82 | |||
cf52284ac3 | |||
4a09c81724 | |||
16efb13dd1 | |||
986abbe0b2 | |||
25c2141991 | |||
2893c2c0a2 | |||
393c1007a8 | |||
66b6fc010d | |||
f31c9470fa | |||
4bd8f58552 | |||
93556a5720 | |||
5614c4ff0f | |||
c3065aac7a | |||
c767df0e4e | |||
25e5b2fdf0 | |||
307c4693dc | |||
349ad75de3 | |||
f562cbf86c | |||
804943c9b1 | |||
dea59165de | |||
a1322873c8 | |||
b8c839bd51 | |||
d2e5198b93 | |||
6cf7a1bf84 | |||
d46b8deeea | |||
bbb7a39414 | |||
d7d8fab211 | |||
51b06924bd | |||
aa4bd14b3f | |||
3ff6554cbc | |||
dfd8140084 | |||
6ea3ab7e14 | |||
9761db5ac2 | |||
75d1617b63 | |||
614a35d539 | |||
9ab401f4d3 | |||
82c81cd0d2 | |||
12959f444c | |||
25a6da244c | |||
5908b66ae9 | |||
480ef20eb1 | |||
c066281bad | |||
1b9493f725 | |||
ae26504e84 | |||
d420080b3b | |||
2975d8933c | |||
43c0e9a6bb | |||
f275f36081 | |||
e628b66cca | |||
3e73bea3e7 | |||
42cf06fa12 | |||
c4bbafc291 | |||
2d6a003dba | |||
e45b7ffcd9 | |||
627282d2c8 | |||
2f7492c986 | |||
2452cd14e0 | |||
bc69c74be0 | |||
897555ca78 | |||
966bcbad5a | |||
94b8612e4e | |||
b2b72190f8 | |||
f5c8e0989d | |||
4a09251921 | |||
36caaaa8e4 | |||
808275a9d5 | |||
be3784c957 | |||
555301ce3a | |||
7194fc2b9e | |||
2a3ca7bfcf | |||
4cbf8ccf05 | |||
a6c4490fce | |||
2c02d34c05 | |||
6c2d931744 | |||
86ffa884b7 | |||
3e548de99d | |||
909268036b | |||
519a324454 | |||
ef96763fa4 | |||
7dcca307d9 | |||
491d5a22a9 | |||
44572f114f | |||
1ef4696cb7 | |||
07a986d330 | |||
59d2b4c831 | |||
2a5bd2f345 | |||
3c06a5dc25 | |||
adeea5d86a | |||
dddbb1c1cb | |||
bccf0e69dc | |||
b15039d228 | |||
2235048432 | |||
484119e59f | |||
24099bdbd2 | |||
912ca44979 | |||
664a6273e1 | |||
c1a62e2154 | |||
aac37bedc0 | |||
a3884db87c | |||
fc5ac1ebc4 | |||
ad20d7d260 | |||
602522beb2 | |||
4e047302f2 | |||
419a812f04 | |||
f340e1a414 | |||
481c9b3258 | |||
8b2dfb2eca | |||
824ea8406c | |||
1f96a93f59 | |||
009d545787 | |||
53c25210a6 | |||
927aa69726 | |||
ce89039036 | |||
42198cd7d5 | |||
d6ba092a27 | |||
773b31de8f | |||
f79b320fc4 | |||
6a212fd561 | |||
be010a292a | |||
7c36e7f956 | |||
13ba2f90b9 | |||
75277cd94b | |||
46d150266b | |||
1b5384ee54 | |||
9f7d32a326 | |||
9de76ebfa5 | |||
46023e4792 | |||
b55aaf094f | |||
d90b622fa4 | |||
79e2bb9291 | |||
efbbefd353 | |||
c2fae72bc6 | |||
7908679c4b | |||
9ed9ff40b3 | |||
2f14415836 | |||
76e4911e8b | |||
ed5e98d0df | |||
146af1fed9 | |||
c60ba7a72f | |||
05beffe0d0 | |||
08c038ebd9 | |||
582550a90d | |||
1d53a870dd | |||
a0c58a6b5c | |||
d3eff6c483 |
150
CHANGELOG.md
150
CHANGELOG.md
@ -1,3 +1,149 @@
|
|||||||
|
<a name="2.3.1"></a>
|
||||||
|
## [2.3.1](https://github.com/angular/angular/compare/2.3.0...2.3.1) (2016-12-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **animations:** always cleanup players after they have finished internally ([#13334](https://github.com/angular/angular/issues/13334)) ([a26e054](https://github.com/angular/angular/commit/a26e054)), closes [#13333](https://github.com/angular/angular/issues/13333)
|
||||||
|
* **animations:** throw errors and normalize offset beyond the range of [0,1] ([6557bc3](https://github.com/angular/angular/commit/6557bc3)), closes [#13348](https://github.com/angular/angular/issues/13348) [#13440](https://github.com/angular/angular/issues/13440)
|
||||||
|
* **compiler:** emit quoted object literal keys if the source is quoted ([ecfad46](https://github.com/angular/angular/commit/ecfad46)), closes [#13249](https://github.com/angular/angular/issues/13249) [#13356](https://github.com/angular/angular/issues/13356)
|
||||||
|
* **compiler:** fix merge error in compiler_host ([69b52eb](https://github.com/angular/angular/commit/69b52eb))
|
||||||
|
* **compiler:** fix PR 13322 ([#13331](https://github.com/angular/angular/issues/13331)) ([79728b4](https://github.com/angular/angular/commit/79728b4))
|
||||||
|
* **compiler:** fix simplify a reference without a name ([1c279b3](https://github.com/angular/angular/commit/1c279b3)), closes [#13470](https://github.com/angular/angular/issues/13470)
|
||||||
|
* **compiler:** fix transpiled ES5 code ([#13322](https://github.com/angular/angular/issues/13322)) ([6c1d790](https://github.com/angular/angular/commit/6c1d790)), closes [#13301](https://github.com/angular/angular/issues/13301)
|
||||||
|
* **compiler:** generated CSS files suffixed with ngstyle. ([#13353](https://github.com/angular/angular/issues/13353)) ([c8a9b70](https://github.com/angular/angular/commit/c8a9b70)), closes [#13141](https://github.com/angular/angular/issues/13141)
|
||||||
|
* **compiler:** make sure provider values with `name` property don’t break. ([efa2d80](https://github.com/angular/angular/commit/efa2d80)), closes [#13394](https://github.com/angular/angular/issues/13394) [#13445](https://github.com/angular/angular/issues/13445)
|
||||||
|
* **compiler:** narrow the span reported for invalid pipes ([307d305](https://github.com/angular/angular/commit/307d305)), closes [#13326](https://github.com/angular/angular/issues/13326) [#13411](https://github.com/angular/angular/issues/13411)
|
||||||
|
* **compiler:** propagate exports when upgrading metadata to v2 ([f6ef7d6](https://github.com/angular/angular/commit/f6ef7d6))
|
||||||
|
* **compiler:** resolver should merge host bindings and listeners ([#13474](https://github.com/angular/angular/issues/13474)) ([6aeaca3](https://github.com/angular/angular/commit/6aeaca3)), closes [#13327](https://github.com/angular/angular/issues/13327)
|
||||||
|
* **compiler:** support dotted property binding ([8db184d](https://github.com/angular/angular/commit/8db184d)), closes [angular/flex-layout#34](https://github.com/angular/flex-layout/issues/34)
|
||||||
|
* **compiler:** update to metadata version 3 ([#13464](https://github.com/angular/angular/issues/13464)) ([b9b557c](https://github.com/angular/angular/commit/b9b557c))
|
||||||
|
* **core:** detectChanges() doesn't work on detached instance ([4d6ac9d](https://github.com/angular/angular/commit/4d6ac9d)), closes [#13426](https://github.com/angular/angular/issues/13426) [#13472](https://github.com/angular/angular/issues/13472)
|
||||||
|
* **core:** properly destroy embedded Views attatched to ApplicationRef ([#13459](https://github.com/angular/angular/issues/13459)) ([d40bbf4](https://github.com/angular/angular/commit/d40bbf4)), closes [#13062](https://github.com/angular/angular/issues/13062)
|
||||||
|
* **dom_adapter:** remove logError from logGroup ([#12925](https://github.com/angular/angular/issues/12925)) ([5fab871](https://github.com/angular/angular/commit/5fab871))
|
||||||
|
* **forms:** ensure `select[multiple]` retains selections ([b3dcff0](https://github.com/angular/angular/commit/b3dcff0)), closes [#12527](https://github.com/angular/angular/issues/12527) [#12654](https://github.com/angular/angular/issues/12654)
|
||||||
|
* **forms:** fix Validators.min/maxLength with FormArray ([#13095](https://github.com/angular/angular/issues/13095)) ([7383e4a](https://github.com/angular/angular/commit/7383e4a)), closes [#13089](https://github.com/angular/angular/issues/13089)
|
||||||
|
* **forms:** introduce checkbox required validator ([124267c](https://github.com/angular/angular/commit/124267c)), closes [#11459](https://github.com/angular/angular/issues/11459) [#13364](https://github.com/angular/angular/issues/13364)
|
||||||
|
* **http:** check response body text against undefined ([#13017](https://github.com/angular/angular/issues/13017)) ([f106a18](https://github.com/angular/angular/commit/f106a18))
|
||||||
|
* **http:** create a copy of headers when merge options ([#13365](https://github.com/angular/angular/issues/13365)) ([65c9b5b](https://github.com/angular/angular/commit/65c9b5b)), closes [#11980](https://github.com/angular/angular/issues/11980)
|
||||||
|
* **language-service:** correctly type `undefined` ([0a7364f](https://github.com/angular/angular/commit/0a7364f)), closes [#13412](https://github.com/angular/angular/issues/13412) [#13414](https://github.com/angular/angular/issues/13414)
|
||||||
|
* Better error when directive not listed in NgModule.declarations ([b0cd514](https://github.com/angular/angular/commit/b0cd514))
|
||||||
|
* Better instructions on running examples and their tests ([203cc7e](https://github.com/angular/angular/commit/203cc7e))
|
||||||
|
* **language-service:** treat string unions as strings ([#13406](https://github.com/angular/angular/issues/13406)) ([14dd2b3](https://github.com/angular/angular/commit/14dd2b3)), closes [#13403](https://github.com/angular/angular/issues/13403)
|
||||||
|
* **router:** add support for query params with multiple values ([e4d5a5f](https://github.com/angular/angular/commit/e4d5a5f)), closes [#11373](https://github.com/angular/angular/issues/11373)
|
||||||
|
* **router:** Use T type in Resolve interface ([#13242](https://github.com/angular/angular/issues/13242)) ([5ee8155](https://github.com/angular/angular/commit/5ee8155))
|
||||||
|
* **selector:** SelectorMatcher match elements with :not selector ([#12977](https://github.com/angular/angular/issues/12977)) ([392c9ac](https://github.com/angular/angular/commit/392c9ac))
|
||||||
|
* **tsc-wrapped:** generate metadata for exports without module specifier ([cd03c77](https://github.com/angular/angular/commit/cd03c77)), closes [#13327](https://github.com/angular/angular/issues/13327)
|
||||||
|
* **upgrade:** fix downgrade content projection and injector inheritance ([86c5098](https://github.com/angular/angular/commit/86c5098)), closes [#6629](https://github.com/angular/angular/issues/6629) [#7727](https://github.com/angular/angular/issues/7727) [#8729](https://github.com/angular/angular/issues/8729) [#9643](https://github.com/angular/angular/issues/9643) [#9649](https://github.com/angular/angular/issues/9649) [#12675](https://github.com/angular/angular/issues/12675)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **upgrade:** enable Angular 1 unit testing of upgrade module ([2fc0560](https://github.com/angular/angular/commit/2fc0560)), closes [#5462](https://github.com/angular/angular/issues/5462) [#12675](https://github.com/angular/angular/issues/12675)
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **animations:** always run the animation queue outside of zones ([e2622ad](https://github.com/angular/angular/commit/e2622ad)), closes [#13440](https://github.com/angular/angular/issues/13440)
|
||||||
|
|
||||||
|
|
||||||
|
<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)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** make sure the plural category exists ([#13169](https://github.com/angular/angular/issues/13169)) ([82c81cd](https://github.com/angular/angular/commit/82c81cd)), closes [#12379](https://github.com/angular/angular/issues/12379)
|
||||||
|
* **compiler:** include the summaries of reexported modules / directives / pipes ([#13196](https://github.com/angular/angular/issues/13196)) ([75d1617](https://github.com/angular/angular/commit/75d1617))
|
||||||
|
* **compiler:** serialize any `StaticSymbol` correctly, not matter in which context ([5614c4f](https://github.com/angular/angular/commit/5614c4f))
|
||||||
|
* **compiler:** short-circut expressions with an index ([#13263](https://github.com/angular/angular/issues/13263)) ([f31c947](https://github.com/angular/angular/commit/f31c947)), closes [#13254](https://github.com/angular/angular/issues/13254)
|
||||||
|
* **core:** display framework version on bootstrapped component ([#13252](https://github.com/angular/angular/issues/13252)) ([16efb13](https://github.com/angular/angular/commit/16efb13))
|
||||||
|
* **facade:** cache original format string ([#12764](https://github.com/angular/angular/issues/12764)) ([a132287](https://github.com/angular/angular/commit/a132287))
|
||||||
|
* **http:** set the default Accept header ([#12989](https://github.com/angular/angular/issues/12989)) ([986abbe](https://github.com/angular/angular/commit/986abbe)), closes [#6354](https://github.com/angular/angular/issues/6354)
|
||||||
|
* **language-service:** avoid throwing for invalid class declarations ([#13257](https://github.com/angular/angular/issues/13257)) ([93556a5](https://github.com/angular/angular/commit/93556a5)), closes [#13253](https://github.com/angular/angular/issues/13253)
|
||||||
|
* **language-service:** do not throw for invalid metadata ([#13261](https://github.com/angular/angular/issues/13261)) ([4a09c81](https://github.com/angular/angular/commit/4a09c81)), closes [#13255](https://github.com/angular/angular/issues/13255)
|
||||||
|
* **language-service:** remove incompletely used parameter from `createLanguageServiceFromTypescript()` ([#13278](https://github.com/angular/angular/issues/13278)) ([25c2141](https://github.com/angular/angular/commit/25c2141)), closes [#13277](https://github.com/angular/angular/issues/13277)
|
||||||
|
* **language-service:** update to use `CompilerHost` from compiler-cli ([#13189](https://github.com/angular/angular/issues/13189)) ([3ff6554](https://github.com/angular/angular/commit/3ff6554))
|
||||||
|
* **router:** allow specifying a matcher without specifying a path ([bbb7a39](https://github.com/angular/angular/commit/bbb7a39)), closes [#12972](https://github.com/angular/angular/issues/12972)
|
||||||
|
* **router:** fix replaceUrl on RouterLink directives ([349ad75](https://github.com/angular/angular/commit/349ad75))
|
||||||
|
* **router:** fix skipLocationChanges on RouterLink directives ([f562cbf](https://github.com/angular/angular/commit/f562cbf)), closes [#13156](https://github.com/angular/angular/issues/13156)
|
||||||
|
* **router:** make setUpLocationChangeListener idempotent ([25e5b2f](https://github.com/angular/angular/commit/25e5b2f))
|
||||||
|
* **router:** runs guards every time when unsuccessfully navigating to the same url over and over again ([#13209](https://github.com/angular/angular/issues/13209)) ([d46b8de](https://github.com/angular/angular/commit/d46b8de))
|
||||||
|
* **router:** throw a better error message when angular 1 is not bootstraped ([c767df0](https://github.com/angular/angular/commit/c767df0))
|
||||||
|
* **router:** validate nested routes ([#13224](https://github.com/angular/angular/issues/13224)) ([2893c2c](https://github.com/angular/angular/commit/2893c2c)), closes [#12827](https://github.com/angular/angular/issues/12827)
|
||||||
|
* **tsc-wrapped:** have UserError display the actual error ([393c100](https://github.com/angular/angular/commit/393c100))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **compiler:** read and write `.ngsummary.json` files ([614a35d](https://github.com/angular/angular/commit/614a35d)), closes [#12787](https://github.com/angular/angular/issues/12787)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.3.0-rc.0"></a>
|
||||||
|
# [2.3.0-rc.0](https://github.com/angular/angular/compare/2.3.0-beta.0...2.3.0-rc.0) (2016-11-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **animations:** blend in all previously transitioned styles into next animation if interrupted ([#13014](https://github.com/angular/angular/issues/13014)) ([ef96763](https://github.com/angular/angular/commit/ef96763)), closes [#13013](https://github.com/angular/angular/issues/13013)
|
||||||
|
* **benchmarks:** use sanitized style values ([#12943](https://github.com/angular/angular/issues/12943)) ([fc5ac1e](https://github.com/angular/angular/commit/fc5ac1e))
|
||||||
|
* **build:** update versions of umd bundles ([#13038](https://github.com/angular/angular/issues/13038)) ([86ffa88](https://github.com/angular/angular/commit/86ffa88)), closes [#13037](https://github.com/angular/angular/issues/13037)
|
||||||
|
* **changelog:** replace beta.1 with beta.0 ([#12961](https://github.com/angular/angular/issues/12961)) ([07a986d](https://github.com/angular/angular/commit/07a986d))
|
||||||
|
* **ci:** pin version of npm on CircleCI ([#12954](https://github.com/angular/angular/issues/12954)) ([a3884db](https://github.com/angular/angular/commit/a3884db))
|
||||||
|
* **closure:** quote date pattern aliases ([#13012](https://github.com/angular/angular/issues/13012)) ([7dcca30](https://github.com/angular/angular/commit/7dcca30))
|
||||||
|
* **common:** update DatePipe to allow closure compilation ([b2b7219](https://github.com/angular/angular/commit/b2b7219))
|
||||||
|
* **compiler:** correctly evaluate references to static functions ([#13133](https://github.com/angular/angular/issues/13133)) ([627282d](https://github.com/angular/angular/commit/627282d))
|
||||||
|
* **compiler:** fix performance regression caused by 5b0f9e2 ([43c0e9a](https://github.com/angular/angular/commit/43c0e9a)), closes [#13146](https://github.com/angular/angular/issues/13146)
|
||||||
|
* **compiler:** fix versions of `@angular/tsc-wrapped` ([bccf0e6](https://github.com/angular/angular/commit/bccf0e6))
|
||||||
|
* **compiler-cli:** fix paths in source maps to be relative ([2a3ca7b](https://github.com/angular/angular/commit/2a3ca7b)), closes [#13040](https://github.com/angular/angular/issues/13040)
|
||||||
|
* **compiler-cli:** pin the version of `tsc-wrapped` ([966bcba](https://github.com/angular/angular/commit/966bcba))
|
||||||
|
* **language-service:** harden against partial normalization of directives ([2975d89](https://github.com/angular/angular/commit/2975d89))
|
||||||
|
* **core:** shrinkwrap was out of date with packages. ([e45b7ff](https://github.com/angular/angular/commit/e45b7ff))
|
||||||
|
* **language-service:** make link check pass ([7194fc2](https://github.com/angular/angular/commit/7194fc2))
|
||||||
|
* **router:** guards restore an incorrect url when used with skipLocationChange ([ad20d7d](https://github.com/angular/angular/commit/ad20d7d)), closes [#12825](https://github.com/angular/angular/issues/12825)
|
||||||
|
* **router:** support redirects to named outlets ([602522b](https://github.com/angular/angular/commit/602522b)), closes [#12740](https://github.com/angular/angular/issues/12740) [#9921](https://github.com/angular/angular/issues/9921)
|
||||||
|
* **tsc-wrapped:** set correct version number ([897555c](https://github.com/angular/angular/commit/897555c))
|
||||||
|
* **tsc-wrapped:** still emit version 1 metadata to allow use of new components in old setups ([bc69c74](https://github.com/angular/angular/commit/bc69c74))
|
||||||
|
* **upgrade:** call ng1 lifecycle hooks ([#12875](https://github.com/angular/angular/issues/12875)) ([1ef4696](https://github.com/angular/angular/commit/1ef4696))
|
||||||
|
* **version:** take all of version string after patch version ([f275f36](https://github.com/angular/angular/commit/f275f36))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **core:** update RxJS peer dependency to 5.0.0-rc.4 Please see [this gist](https://gist.github.com/robwormald/19dea0c70a6e01aadced6731aed4f9f7) if you depend on the `cache` operator ([2d6a003](https://github.com/angular/angular/commit/2d6a003)), closes [#13125](https://github.com/angular/angular/issues/13125)
|
||||||
|
* **core:** upgrade zone.js to v0.7.1 ([c4bbafc](https://github.com/angular/angular/commit/c4bbafc))
|
||||||
|
* **build:** record angular version in the dom ([#13164](https://github.com/angular/angular/issues/13164)) ([e628b66](https://github.com/angular/angular/commit/e628b66))
|
||||||
|
* **core:** expose destroy() method on ViewRef ([808275a](https://github.com/angular/angular/commit/808275a))
|
||||||
|
* **core:** properly support inheritance ([f5c8e09](https://github.com/angular/angular/commit/f5c8e09)), closes [#11606](https://github.com/angular/angular/issues/11606) [#12892](https://github.com/angular/angular/issues/12892)
|
||||||
|
* **language-service:** add services to support editors ([#12987](https://github.com/angular/angular/issues/12987)) ([519a324](https://github.com/angular/angular/commit/519a324))
|
||||||
|
* **router:** add support for custom route reuse strategies ([42cf06f](https://github.com/angular/angular/commit/42cf06f))
|
||||||
|
* **tools:** allow disabling annotation lowering ([c1a62e2](https://github.com/angular/angular/commit/c1a62e2))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.2.4"></a>
|
||||||
|
## [2.2.4](https://github.com/angular/angular/compare/2.2.3...2.2.4) (2016-11-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** update DatePipe to allow closure compilation ([eba53fd](https://github.com/angular/angular/commit/eba53fd))
|
||||||
|
* **compiler:** fix performance regression caused by 5b0f9e2 ([ee2d6e5](https://github.com/angular/angular/commit/ee2d6e5)), closes [#13146](https://github.com/angular/angular/issues/13146)
|
||||||
|
* **compiler-cli:** fix paths in source maps to be relative ([eb173bc](https://github.com/angular/angular/commit/eb173bc)), closes [#13040](https://github.com/angular/angular/issues/13040)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.2.3"></a>
|
||||||
|
## [2.2.3](https://github.com/angular/angular/compare/2.2.2...2.2.3) (2016-11-23)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** Revert: fix versions of `@angular/tsc-wrapped` ([015ca47](https://github.com/angular/angular/commit/015ca47))
|
||||||
|
* **animations:** Revert: blend in all previously transitioned styles into next animation if interrupted ([c12e56e](https://github.com/angular/angular/commit/c12e56e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.2.2"></a>
|
<a name="2.2.2"></a>
|
||||||
## [2.2.2](https://github.com/angular/angular/compare/2.2.1...2.2.2) (2016-11-22)
|
## [2.2.2](https://github.com/angular/angular/compare/2.2.1...2.2.2) (2016-11-22)
|
||||||
|
|
||||||
@ -39,6 +185,8 @@
|
|||||||
|
|
||||||
Note: The 2.3.0-beta.0 release also contains all the changes present in the 2.2.1 release.
|
Note: The 2.3.0-beta.0 release also contains all the changes present in the 2.2.1 release.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.2.1"></a>
|
<a name="2.2.1"></a>
|
||||||
## [2.2.1](https://github.com/angular/angular/compare/2.2.0...2.2.1) (2016-11-17)
|
## [2.2.1](https://github.com/angular/angular/compare/2.2.0...2.2.1) (2016-11-17)
|
||||||
|
|
||||||
@ -860,7 +1008,7 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
|||||||
* core:
|
* core:
|
||||||
- `ApplicationRef.dispose` is deprecated. Destroy the module that was
|
- `ApplicationRef.dispose` is deprecated. Destroy the module that was
|
||||||
created during bootstrap instead by calling `NgModuleRef.destroy`.
|
created during bootstrap instead by calling `NgModuleRef.destroy`.
|
||||||
- `AplicationRef.registerDisposeListener` is deprecated.
|
- `ApplicationRef.registerDisposeListener` is deprecated.
|
||||||
Use the `ngOnDestroy` lifecycle hook for providers or
|
Use the `ngOnDestroy` lifecycle hook for providers or
|
||||||
`NgModuleRef.onDestroy` instead.
|
`NgModuleRef.onDestroy` instead.
|
||||||
- `disposePlatform` is deprecated. Use `destroyPlatform` instead.
|
- `disposePlatform` is deprecated. Use `destroyPlatform` instead.
|
||||||
|
@ -57,7 +57,7 @@ We want to fix all the issues as soon as possible, but before fixing a bug we ne
|
|||||||
- 3rd-party libraries and their versions
|
- 3rd-party libraries and their versions
|
||||||
- and most importantly - a use-case that fails
|
- and most importantly - a use-case that fails
|
||||||
|
|
||||||
A minimal reproduce scenario using http://plnkr.co/ allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem. If plunker is not a suitable way to demostrate the problem (for example for issues related to our npm packaging), please create a standalone git repository demostrating the problem.
|
A minimal reproduce scenario using http://plnkr.co/ allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem. If plunker is not a suitable way to demonstrate the problem (for example for issues related to our npm packaging), please create a standalone git repository demonstrating the problem.
|
||||||
|
|
||||||
We will be insisting on a minimal reproduce scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience users often find coding problems themselves while preparing a minimal plunk. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate the problem before we can fix it.
|
We will be insisting on a minimal reproduce scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience users often find coding problems themselves while preparing a minimal plunk. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate the problem before we can fix it.
|
||||||
|
|
||||||
@ -244,7 +244,7 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
|||||||
[github]: https://github.com/angular/angular
|
[github]: https://github.com/angular/angular
|
||||||
[gitter]: https://gitter.im/angular/angular
|
[gitter]: https://gitter.im/angular/angular
|
||||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||||
[js-style-guide]: https://google.github.io/styleguide/javascriptguide.xml
|
[js-style-guide]: https://google.github.io/styleguide/jsguide.html
|
||||||
[jsfiddle]: http://jsfiddle.net
|
[jsfiddle]: http://jsfiddle.net
|
||||||
[plunker]: http://plnkr.co/edit
|
[plunker]: http://plnkr.co/edit
|
||||||
[runnable]: http://runnable.com
|
[runnable]: http://runnable.com
|
||||||
|
@ -126,7 +126,7 @@ $ gulp public-api:update
|
|||||||
|
|
||||||
Note: The command `./test.sh tools` fails when the API doesn't match the golden files.
|
Note: The command `./test.sh tools` fails when the API doesn't match the golden files.
|
||||||
|
|
||||||
## Formatting your source code
|
## <a name="clang-format"></a> Formatting your source code
|
||||||
|
|
||||||
Angular uses [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to format the source code. If the source code
|
Angular uses [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to format the source code. If the source code
|
||||||
is not properly formatted, the CI will fail and the PR can not be merged.
|
is not properly formatted, the CI will fail and the PR can not be merged.
|
||||||
|
@ -24,8 +24,9 @@ with it.
|
|||||||
* `comp: forms`: `@kara`
|
* `comp: forms`: `@kara`
|
||||||
* `comp: http`: `@jeffbcross`
|
* `comp: http`: `@jeffbcross`
|
||||||
* `comp: i18n`: `@vicb`
|
* `comp: i18n`: `@vicb`
|
||||||
|
* `comp: language service`: `@chuckjaz`
|
||||||
* `comp: metadata-extractor`: `@chuckjaz`
|
* `comp: metadata-extractor`: `@chuckjaz`
|
||||||
* `comp: router`: `@vsavkin`
|
* `comp: router`: `@vicb`
|
||||||
* `comp: testing`: `@juliemr`
|
* `comp: testing`: `@juliemr`
|
||||||
* `comp: upgrade`: `@mhevery`
|
* `comp: upgrade`: `@mhevery`
|
||||||
* `comp: web-worker`: `@vicb`
|
* `comp: web-worker`: `@vicb`
|
||||||
|
@ -36,7 +36,7 @@ var CIconfiguration = {
|
|||||||
'iOS7': {unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
|
'iOS7': {unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
|
||||||
'iOS8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
'iOS8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
'iOS9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
'iOS9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
'iOS10': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
'iOS10': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||||
'WindowsPhone': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
'WindowsPhone': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -44,10 +44,10 @@ var customLaunchers = {
|
|||||||
'DartiumWithWebPlatform':
|
'DartiumWithWebPlatform':
|
||||||
{base: 'Dartium', flags: ['--enable-experimental-web-platform-features']},
|
{base: 'Dartium', flags: ['--enable-experimental-web-platform-features']},
|
||||||
'ChromeNoSandbox': {base: 'Chrome', flags: ['--no-sandbox']},
|
'ChromeNoSandbox': {base: 'Chrome', flags: ['--no-sandbox']},
|
||||||
'SL_CHROME': {base: 'SauceLabs', browserName: 'chrome', version: '52'},
|
'SL_CHROME': {base: 'SauceLabs', browserName: 'chrome', version: '54'},
|
||||||
'SL_CHROMEBETA': {base: 'SauceLabs', browserName: 'chrome', version: 'beta'},
|
'SL_CHROMEBETA': {base: 'SauceLabs', browserName: 'chrome', version: 'beta'},
|
||||||
'SL_CHROMEDEV': {base: 'SauceLabs', browserName: 'chrome', version: 'dev'},
|
'SL_CHROMEDEV': {base: 'SauceLabs', browserName: 'chrome', version: 'dev'},
|
||||||
'SL_FIREFOX': {base: 'SauceLabs', browserName: 'firefox', version: '46'},
|
'SL_FIREFOX': {base: 'SauceLabs', browserName: 'firefox', version: '50'},
|
||||||
'SL_FIREFOXBETA': {base: 'SauceLabs', browserName: 'firefox', version: 'beta'},
|
'SL_FIREFOXBETA': {base: 'SauceLabs', browserName: 'firefox', version: 'beta'},
|
||||||
'SL_FIREFOXDEV': {base: 'SauceLabs', browserName: 'firefox', version: 'dev'},
|
'SL_FIREFOXDEV': {base: 'SauceLabs', browserName: 'firefox', version: 'dev'},
|
||||||
'SL_SAFARI7': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.9', version: '7.0'},
|
'SL_SAFARI7': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.9', version: '7.0'},
|
||||||
@ -96,7 +96,7 @@ var customLaunchers = {
|
|||||||
'BS_IE10': {
|
'BS_IE10': {
|
||||||
base: 'BrowserStack',
|
base: 'BrowserStack',
|
||||||
browser: 'ie',
|
browser: 'ie',
|
||||||
browser_version: '10.0',
|
browser_version: '10.1',
|
||||||
os: 'Windows',
|
os: 'Windows',
|
||||||
os_version: '8'
|
os_version: '8'
|
||||||
},
|
},
|
||||||
|
7
build.sh
7
build.sh
@ -17,6 +17,7 @@ PACKAGES=(core
|
|||||||
upgrade
|
upgrade
|
||||||
router
|
router
|
||||||
compiler-cli
|
compiler-cli
|
||||||
|
language-service
|
||||||
benchpress)
|
benchpress)
|
||||||
BUILD_ALL=true
|
BUILD_ALL=true
|
||||||
BUNDLE=true
|
BUNDLE=true
|
||||||
@ -144,6 +145,11 @@ do
|
|||||||
$TSC -p ${SRCDIR}/tsconfig-testing.json
|
$TSC -p ${SRCDIR}/tsconfig-testing.json
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ -e ${SRCDIR}/tsconfig-2015.json ]]; then
|
||||||
|
echo "====== COMPILING ESM: ${TSC} -p ${SRCDIR}/tsconfig-2015.json"
|
||||||
|
${TSC} -p ${SRCDIR}/tsconfig-2015.json
|
||||||
|
fi
|
||||||
|
|
||||||
echo "====== TSC 1.8 d.ts compat for ${DESTDIR} ====="
|
echo "====== TSC 1.8 d.ts compat for ${DESTDIR} ====="
|
||||||
# safely strips 'readonly' specifier from d.ts files to make them compatible with tsc 1.8
|
# safely strips 'readonly' specifier from d.ts files to make them compatible with tsc 1.8
|
||||||
if [ "$(uname)" == "Darwin" ]; then
|
if [ "$(uname)" == "Darwin" ]; then
|
||||||
@ -175,7 +181,6 @@ do
|
|||||||
mv ${UMD_ES5_PATH}.tmp ${UMD_ES5_PATH}
|
mv ${UMD_ES5_PATH}.tmp ${UMD_ES5_PATH}
|
||||||
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_ES5_MIN_PATH} ${UMD_ES5_PATH}
|
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_ES5_MIN_PATH} ${UMD_ES5_PATH}
|
||||||
|
|
||||||
|
|
||||||
if [[ -e rollup-testing.config.js ]]; then
|
if [[ -e rollup-testing.config.js ]]; then
|
||||||
echo "====== Rollup ${PACKAGE} testing"
|
echo "====== Rollup ${PACKAGE} testing"
|
||||||
../../../node_modules/.bin/rollup -c rollup-testing.config.js
|
../../../node_modules/.bin/rollup -c rollup-testing.config.js
|
||||||
|
@ -48,12 +48,15 @@ module.exports = function(config) {
|
|||||||
|
|
||||||
exclude: [
|
exclude: [
|
||||||
'dist/all/@angular/**/e2e_test/**',
|
'dist/all/@angular/**/e2e_test/**',
|
||||||
'dist/all/@angular/router/**',
|
'dist/all/@angular/**/*node_only_spec.js',
|
||||||
|
'dist/all/@angular/benchpress/**',
|
||||||
'dist/all/@angular/compiler-cli/**',
|
'dist/all/@angular/compiler-cli/**',
|
||||||
'dist/all/@angular/compiler/test/aot/**',
|
'dist/all/@angular/compiler/test/aot/**',
|
||||||
'dist/all/@angular/benchpress/**',
|
'dist/all/@angular/examples/**/e2e_test/*',
|
||||||
'dist/all/angular1_router.js',
|
'dist/all/@angular/language-service/**',
|
||||||
|
'dist/all/@angular/router/**',
|
||||||
'dist/all/@angular/platform-browser/testing/e2e_util.js',
|
'dist/all/@angular/platform-browser/testing/e2e_util.js',
|
||||||
|
'dist/all/angular1_router.js',
|
||||||
'dist/examples/**/e2e_test/**',
|
'dist/examples/**/e2e_test/**',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/core": "^2.0.0-rc.7",
|
"@angular/core": "^2.0.0-rc.7",
|
||||||
"reflect-metadata": "^0.1.2",
|
"reflect-metadata": "^0.1.2",
|
||||||
"rxjs": "5.0.0-beta.12",
|
"rxjs": "5.0.0-rc.4",
|
||||||
"jpm": "1.1.4",
|
"jpm": "1.1.4",
|
||||||
"firefox-profile": "0.4.0",
|
"firefox-profile": "0.4.0",
|
||||||
"selenium-webdriver": "^2.53.3"
|
"selenium-webdriver": "^2.53.3"
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* tslint:disable:no-console */
|
||||||
import {browser} from 'protractor';
|
import {browser} from 'protractor';
|
||||||
|
|
||||||
const assertEventsContainsName = function(events: any[], eventName: string) {
|
const assertEventsContainsName = function(events: any[], eventName: string) {
|
||||||
|
@ -11,9 +11,6 @@
|
|||||||
* @description
|
* @description
|
||||||
* Entry point for all public APIs of the common package.
|
* Entry point for all public APIs of the common package.
|
||||||
*/
|
*/
|
||||||
export * from './src/location';
|
export * from './src/common';
|
||||||
export {NgLocalization} from './src/localization';
|
|
||||||
export {CommonModule} from './src/common_module';
|
|
||||||
|
|
||||||
export {NgClass, NgFor, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet} from './src/directives/index';
|
// This file only reexports content of the `src` folder. Keep it that way.
|
||||||
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe} from './src/pipes/index';
|
|
||||||
|
20
modules/@angular/common/src/common.ts
Normal file
20
modules/@angular/common/src/common.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module
|
||||||
|
* @description
|
||||||
|
* Entry point for all public APIs of the common package.
|
||||||
|
*/
|
||||||
|
export * from './location/index';
|
||||||
|
export {NgLocalization} from './localization';
|
||||||
|
export {CommonModule} from './common_module';
|
||||||
|
export {NgClass, NgFor, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet} from './directives/index';
|
||||||
|
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe} from './pipes/index';
|
||||||
|
export {VERSION} from './version';
|
||||||
|
export {Version} from '@angular/core';
|
@ -23,9 +23,23 @@ export abstract class NgLocalization { abstract getPluralCategory(value: any): s
|
|||||||
*/
|
*/
|
||||||
export function getPluralCategory(
|
export function getPluralCategory(
|
||||||
value: number, cases: string[], ngLocalization: NgLocalization): string {
|
value: number, cases: string[], ngLocalization: NgLocalization): string {
|
||||||
const nbCase = `=${value}`;
|
let key = `=${value}`;
|
||||||
|
|
||||||
return cases.indexOf(nbCase) > -1 ? nbCase : ngLocalization.getPluralCategory(value);
|
if (cases.indexOf(key) > -1) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
key = ngLocalization.getPluralCategory(value);
|
||||||
|
|
||||||
|
if (cases.indexOf(key) > -1) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cases.indexOf('other') > -1) {
|
||||||
|
return 'other';
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`No plural message found for value "${value}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,13 +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
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from './location/platform_location';
|
|
||||||
export * from './location/location_strategy';
|
|
||||||
export * from './location/hash_location_strategy';
|
|
||||||
export * from './location/path_location_strategy';
|
|
||||||
export * from './location/location';
|
|
@ -17,6 +17,8 @@ import {LocationChangeListener, PlatformLocation} from './platform_location';
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @whatItDoes Use URL hash for storing application location data.
|
||||||
|
* @description
|
||||||
* `HashLocationStrategy` is a {@link LocationStrategy} used to configure the
|
* `HashLocationStrategy` is a {@link LocationStrategy} used to configure the
|
||||||
* {@link Location} service to represent its state in the
|
* {@link Location} service to represent its state in the
|
||||||
* [hash fragment](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax)
|
* [hash fragment](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax)
|
||||||
@ -27,18 +29,7 @@ import {LocationChangeListener, PlatformLocation} from './platform_location';
|
|||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* ```
|
* {@example common/location/ts/hash_location_component.ts region='LocationComponent'}
|
||||||
* import {Component, NgModule} from '@angular/core';
|
|
||||||
* import {
|
|
||||||
* LocationStrategy,
|
|
||||||
* HashLocationStrategy
|
|
||||||
* } from '@angular/common';
|
|
||||||
*
|
|
||||||
* @NgModule({
|
|
||||||
* providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}]
|
|
||||||
* })
|
|
||||||
* class AppModule {}
|
|
||||||
* ```
|
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
|
13
modules/@angular/common/src/location/index.ts
Normal file
13
modules/@angular/common/src/location/index.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './platform_location';
|
||||||
|
export * from './location_strategy';
|
||||||
|
export * from './hash_location_strategy';
|
||||||
|
export * from './path_location_strategy';
|
||||||
|
export * from './location';
|
@ -12,7 +12,8 @@ import {LocationStrategy} from './location_strategy';
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `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.
|
||||||
|
* @description
|
||||||
* Depending on which {@link LocationStrategy} is used, `Location` will either persist
|
* Depending on which {@link LocationStrategy} is used, `Location` will either persist
|
||||||
* to the URL's path or the URL's hash segment.
|
* to the URL's path or the URL's hash segment.
|
||||||
*
|
*
|
||||||
@ -28,19 +29,7 @@ import {LocationStrategy} from './location_strategy';
|
|||||||
* - `/my/app/user/123/` **is not** normalized
|
* - `/my/app/user/123/` **is not** normalized
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
* {@example common/location/ts/path_location_component.ts region='LocationComponent'}
|
||||||
* ```
|
|
||||||
* import {Component} from '@angular/core';
|
|
||||||
* import {Location} from '@angular/common';
|
|
||||||
*
|
|
||||||
* @Component({selector: 'app-component'})
|
|
||||||
* class AppCmp {
|
|
||||||
* constructor(location: Location) {
|
|
||||||
* location.go('/foo');
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -96,7 +85,7 @@ export class Location {
|
|||||||
* used, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use.
|
* used, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use.
|
||||||
*/
|
*/
|
||||||
prepareExternalUrl(url: string): string {
|
prepareExternalUrl(url: string): string {
|
||||||
if (url.length > 0 && !url.startsWith('/')) {
|
if (url && url[0] !== '/') {
|
||||||
url = '/' + url;
|
url = '/' + url;
|
||||||
}
|
}
|
||||||
return this._platformStrategy.prepareExternalUrl(url);
|
return this._platformStrategy.prepareExternalUrl(url);
|
||||||
@ -143,7 +132,7 @@ export class Location {
|
|||||||
* is.
|
* is.
|
||||||
*/
|
*/
|
||||||
public static normalizeQueryParams(params: string): string {
|
public static normalizeQueryParams(params: string): string {
|
||||||
return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params;
|
return params && params[0] !== '?' ? '?' + params : params;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -175,25 +164,13 @@ export class Location {
|
|||||||
/**
|
/**
|
||||||
* If url has a trailing slash, remove it, otherwise return url as is.
|
* If url has a trailing slash, remove it, otherwise return url as is.
|
||||||
*/
|
*/
|
||||||
public static stripTrailingSlash(url: string): string {
|
public static stripTrailingSlash(url: string): string { return url.replace(/\/$/, ''); }
|
||||||
if (/\/$/g.test(url)) {
|
|
||||||
url = url.substring(0, url.length - 1);
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _stripBaseHref(baseHref: string, url: string): string {
|
function _stripBaseHref(baseHref: string, url: string): string {
|
||||||
if (baseHref.length > 0 && url.startsWith(baseHref)) {
|
return baseHref && url.startsWith(baseHref) ? url.substring(baseHref.length) : url;
|
||||||
return url.substring(baseHref.length);
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _stripIndexHtml(url: string): string {
|
function _stripIndexHtml(url: string): string {
|
||||||
if (/\/index.html$/g.test(url)) {
|
return url.replace(/\/index.html$/, '');
|
||||||
// '/index.html'.length == 11
|
|
||||||
return url.substring(0, url.length - 11);
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import {LocationChangeListener} from './platform_location';
|
|||||||
/**
|
/**
|
||||||
* `LocationStrategy` is responsible for representing and reading route state
|
* `LocationStrategy` is responsible for representing and reading route state
|
||||||
* from the browser's URL. Angular provides two strategies:
|
* from the browser's URL. Angular provides two strategies:
|
||||||
* {@link HashLocationStrategy} and {@link PathLocationStrategy} (default).
|
* {@link HashLocationStrategy} and {@link PathLocationStrategy}.
|
||||||
*
|
*
|
||||||
* This is used under the hood of the {@link Location} service.
|
* This is used under the hood of the {@link Location} service.
|
||||||
*
|
*
|
||||||
|
@ -17,14 +17,13 @@ import {LocationChangeListener, PlatformLocation} from './platform_location';
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @whatItDoes Use URL for storing application location data.
|
||||||
|
* @description
|
||||||
* `PathLocationStrategy` is a {@link LocationStrategy} used to configure the
|
* `PathLocationStrategy` is a {@link LocationStrategy} used to configure the
|
||||||
* {@link Location} service to represent its state in the
|
* {@link Location} service to represent its state in the
|
||||||
* [path](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) of the
|
* [path](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) of the
|
||||||
* browser's URL.
|
* browser's URL.
|
||||||
*
|
*
|
||||||
* `PathLocationStrategy` is the default binding for {@link LocationStrategy}
|
|
||||||
* provided in {@link ROUTER_PROVIDERS}.
|
|
||||||
*
|
|
||||||
* If you're using `PathLocationStrategy`, you must provide a {@link APP_BASE_HREF}
|
* If you're using `PathLocationStrategy`, you must provide a {@link APP_BASE_HREF}
|
||||||
* or add a base element to the document. This URL prefix that will be preserved
|
* or add a base element to the document. This URL prefix that will be preserved
|
||||||
* when generating and recognizing URLs.
|
* when generating and recognizing URLs.
|
||||||
@ -37,6 +36,10 @@ import {LocationChangeListener, PlatformLocation} from './platform_location';
|
|||||||
* `location.go('/foo')`, the browser's URL will become
|
* `location.go('/foo')`, the browser's URL will become
|
||||||
* `example.com/my/app/foo`.
|
* `example.com/my/app/foo`.
|
||||||
*
|
*
|
||||||
|
* ### Example
|
||||||
|
*
|
||||||
|
* {@example common/location/ts/path_location_component.ts region='LocationComponent'}
|
||||||
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -59,7 +59,7 @@ const _observableStrategy = new ObservableStrategy();
|
|||||||
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'}
|
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'}
|
||||||
*
|
*
|
||||||
* It's also possible to use `async` with Observables. The example below binds the `time` Observable
|
* It's also possible to use `async` with Observables. The example below binds the `time` Observable
|
||||||
* to the view. The Observable continuesly updates the view with the current time.
|
* to the view. The Observable continuously updates the view with the current time.
|
||||||
*
|
*
|
||||||
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'}
|
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'}
|
||||||
*
|
*
|
||||||
|
@ -7,11 +7,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
||||||
import {DateFormatter} from '../facade/intl';
|
|
||||||
import {NumberWrapper, isDate} from '../facade/lang';
|
import {NumberWrapper, isDate} from '../facade/lang';
|
||||||
|
|
||||||
|
import {DateFormatter} from './intl';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngModule CommonModule
|
* @ngModule CommonModule
|
||||||
* @whatItDoes Formats a date according to locale rules.
|
* @whatItDoes Formats a date according to locale rules.
|
||||||
|
@ -44,6 +44,7 @@ const DATE_FORMATS_SPLIT =
|
|||||||
/((?:[^yMLdHhmsazZEwGjJ']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|J+|j+|m+|s+|a|z|Z|G+|w+))(.*)/;
|
/((?:[^yMLdHhmsazZEwGjJ']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|J+|j+|m+|s+|a|z|Z|G+|w+))(.*)/;
|
||||||
|
|
||||||
const PATTERN_ALIASES: {[format: string]: DateFormatterFn} = {
|
const PATTERN_ALIASES: {[format: string]: DateFormatterFn} = {
|
||||||
|
// Keys are quoted so they do not get renamed during closure compilation.
|
||||||
'yMMMdjms': datePartGetterFactory(combine([
|
'yMMMdjms': datePartGetterFactory(combine([
|
||||||
digitCondition('year', 1),
|
digitCondition('year', 1),
|
||||||
nameCondition('month', 3),
|
nameCondition('month', 3),
|
||||||
@ -72,48 +73,50 @@ const PATTERN_ALIASES: {[format: string]: DateFormatterFn} = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const DATE_FORMATS: {[format: string]: DateFormatterFn} = {
|
const DATE_FORMATS: {[format: string]: DateFormatterFn} = {
|
||||||
yyyy: datePartGetterFactory(digitCondition('year', 4)),
|
// Keys are quoted so they do not get renamed.
|
||||||
yy: datePartGetterFactory(digitCondition('year', 2)),
|
'yyyy': datePartGetterFactory(digitCondition('year', 4)),
|
||||||
y: datePartGetterFactory(digitCondition('year', 1)),
|
'yy': datePartGetterFactory(digitCondition('year', 2)),
|
||||||
MMMM: datePartGetterFactory(nameCondition('month', 4)),
|
'y': datePartGetterFactory(digitCondition('year', 1)),
|
||||||
MMM: datePartGetterFactory(nameCondition('month', 3)),
|
'MMMM': datePartGetterFactory(nameCondition('month', 4)),
|
||||||
MM: datePartGetterFactory(digitCondition('month', 2)),
|
'MMM': datePartGetterFactory(nameCondition('month', 3)),
|
||||||
M: datePartGetterFactory(digitCondition('month', 1)),
|
'MM': datePartGetterFactory(digitCondition('month', 2)),
|
||||||
LLLL: datePartGetterFactory(nameCondition('month', 4)),
|
'M': datePartGetterFactory(digitCondition('month', 1)),
|
||||||
L: datePartGetterFactory(nameCondition('month', 1)),
|
'LLLL': datePartGetterFactory(nameCondition('month', 4)),
|
||||||
dd: datePartGetterFactory(digitCondition('day', 2)),
|
'L': datePartGetterFactory(nameCondition('month', 1)),
|
||||||
d: datePartGetterFactory(digitCondition('day', 1)),
|
'dd': datePartGetterFactory(digitCondition('day', 2)),
|
||||||
HH: digitModifier(
|
'd': datePartGetterFactory(digitCondition('day', 1)),
|
||||||
|
'HH': digitModifier(
|
||||||
hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 2), false)))),
|
hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 2), false)))),
|
||||||
H: hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), false))),
|
'H': hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), false))),
|
||||||
hh: digitModifier(
|
'hh': digitModifier(
|
||||||
hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 2), true)))),
|
hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 2), true)))),
|
||||||
h: hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))),
|
'h': hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))),
|
||||||
jj: datePartGetterFactory(digitCondition('hour', 2)),
|
'jj': datePartGetterFactory(digitCondition('hour', 2)),
|
||||||
j: datePartGetterFactory(digitCondition('hour', 1)),
|
'j': datePartGetterFactory(digitCondition('hour', 1)),
|
||||||
mm: digitModifier(datePartGetterFactory(digitCondition('minute', 2))),
|
'mm': digitModifier(datePartGetterFactory(digitCondition('minute', 2))),
|
||||||
m: datePartGetterFactory(digitCondition('minute', 1)),
|
'm': datePartGetterFactory(digitCondition('minute', 1)),
|
||||||
ss: digitModifier(datePartGetterFactory(digitCondition('second', 2))),
|
'ss': digitModifier(datePartGetterFactory(digitCondition('second', 2))),
|
||||||
s: datePartGetterFactory(digitCondition('second', 1)),
|
's': datePartGetterFactory(digitCondition('second', 1)),
|
||||||
// while ISO 8601 requires fractions to be prefixed with `.` or `,`
|
// while ISO 8601 requires fractions to be prefixed with `.` or `,`
|
||||||
// we can be just safely rely on using `sss` since we currently don't support single or two digit
|
// we can be just safely rely on using `sss` since we currently don't support single or two digit
|
||||||
// fractions
|
// fractions
|
||||||
sss: datePartGetterFactory(digitCondition('second', 3)),
|
'sss': datePartGetterFactory(digitCondition('second', 3)),
|
||||||
EEEE: datePartGetterFactory(nameCondition('weekday', 4)),
|
'EEEE': datePartGetterFactory(nameCondition('weekday', 4)),
|
||||||
EEE: datePartGetterFactory(nameCondition('weekday', 3)),
|
'EEE': datePartGetterFactory(nameCondition('weekday', 3)),
|
||||||
EE: datePartGetterFactory(nameCondition('weekday', 2)),
|
'EE': datePartGetterFactory(nameCondition('weekday', 2)),
|
||||||
E: datePartGetterFactory(nameCondition('weekday', 1)),
|
'E': datePartGetterFactory(nameCondition('weekday', 1)),
|
||||||
a: hourClockExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))),
|
'a': hourClockExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))),
|
||||||
Z: timeZoneGetter('short'),
|
'Z': timeZoneGetter('short'),
|
||||||
z: timeZoneGetter('long'),
|
'z': timeZoneGetter('long'),
|
||||||
ww: datePartGetterFactory({}), // Week of year, padded (00-53). Week 01 is the week with the
|
'ww': datePartGetterFactory({}), // Week of year, padded (00-53). Week 01 is the week with the
|
||||||
// first Thursday of the year. not support ?
|
// first Thursday of the year. not support ?
|
||||||
w: datePartGetterFactory({}), // Week of year (0-53). Week 1 is the week with the first Thursday
|
'w':
|
||||||
|
datePartGetterFactory({}), // Week of year (0-53). Week 1 is the week with the first Thursday
|
||||||
// of the year not support ?
|
// of the year not support ?
|
||||||
G: datePartGetterFactory(nameCondition('era', 1)),
|
'G': datePartGetterFactory(nameCondition('era', 1)),
|
||||||
GG: datePartGetterFactory(nameCondition('era', 2)),
|
'GG': datePartGetterFactory(nameCondition('era', 2)),
|
||||||
GGG: datePartGetterFactory(nameCondition('era', 3)),
|
'GGG': datePartGetterFactory(nameCondition('era', 3)),
|
||||||
GGGG: datePartGetterFactory(nameCondition('era', 4))
|
'GGGG': datePartGetterFactory(nameCondition('era', 4))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -184,7 +187,8 @@ function dateFormatter(format: string, date: Date, locale: string): string {
|
|||||||
|
|
||||||
if (fn) return fn(date, locale);
|
if (fn) return fn(date, locale);
|
||||||
|
|
||||||
let parts = DATE_FORMATTER_CACHE.get(format);
|
const cacheKey = format;
|
||||||
|
let parts = DATE_FORMATTER_CACHE.get(cacheKey);
|
||||||
|
|
||||||
if (!parts) {
|
if (!parts) {
|
||||||
parts = [];
|
parts = [];
|
||||||
@ -202,7 +206,7 @@ function dateFormatter(format: string, date: Date, locale: string): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DATE_FORMATTER_CACHE.set(format, parts);
|
DATE_FORMATTER_CACHE.set(cacheKey, parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parts.reduce((text, part) => {
|
return parts.reduce((text, part) => {
|
@ -8,9 +8,9 @@
|
|||||||
|
|
||||||
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
|
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
|
||||||
|
|
||||||
import {NumberFormatStyle, NumberFormatter} from '../facade/intl';
|
|
||||||
import {NumberWrapper, isBlank, isPresent} from '../facade/lang';
|
import {NumberWrapper, isBlank, isPresent} from '../facade/lang';
|
||||||
|
|
||||||
|
import {NumberFormatStyle, NumberFormatter} from './intl';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
|
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
|
||||||
|
19
modules/@angular/common/src/version.ts
Normal file
19
modules/@angular/common/src/version.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module
|
||||||
|
* @description
|
||||||
|
* Entry point for all public APIs of the common package.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Version} from '@angular/core';
|
||||||
|
/**
|
||||||
|
* @stable
|
||||||
|
*/
|
||||||
|
export const VERSION = new Version('0.0.0-PLACEHOLDER');
|
@ -7,13 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {LOCALE_ID} from '@angular/core';
|
import {LOCALE_ID} from '@angular/core';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed, inject} from '@angular/core/testing';
|
||||||
import {beforeEach, describe, inject, it} from '@angular/core/testing/testing_internal';
|
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
|
||||||
|
|
||||||
import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../src/localization';
|
import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../src/localization';
|
||||||
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('l10n', () => {
|
describe('l10n', () => {
|
||||||
|
|
||||||
@ -141,6 +138,23 @@ export function main() {
|
|||||||
expect(getPluralCategory(5, ['one', 'other', '=5'], l10n)).toEqual('=5');
|
expect(getPluralCategory(5, ['one', 'other', '=5'], l10n)).toEqual('=5');
|
||||||
expect(getPluralCategory(6, ['one', 'other', '=5'], l10n)).toEqual('other');
|
expect(getPluralCategory(6, ['one', 'other', '=5'], l10n)).toEqual('other');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fallback to other when the case is not present', () => {
|
||||||
|
const l10n = new NgLocaleLocalization('ro');
|
||||||
|
expect(getPluralCategory(1, ['one', 'other'], l10n)).toEqual('one');
|
||||||
|
// 2 -> 'few'
|
||||||
|
expect(getPluralCategory(2, ['one', 'other'], l10n)).toEqual('other');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('errors', () => {
|
||||||
|
it('should report an error when the "other" category is not present', () => {
|
||||||
|
expect(() => {
|
||||||
|
const l10n = new NgLocaleLocalization('ro');
|
||||||
|
// 2 -> 'few'
|
||||||
|
getPluralCategory(2, ['one'], l10n);
|
||||||
|
}).toThrowError('No plural message found for value "2"');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -22,6 +22,7 @@
|
|||||||
"../../../node_modules/zone.js/dist/zone.js.d.ts"
|
"../../../node_modules/zone.js/dist/zone.js.d.ts"
|
||||||
],
|
],
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true
|
"strictMetadataEmit": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,13 @@
|
|||||||
* 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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {AotCompilerHost, AotCompilerHost as StaticReflectorHost, StaticReflector, StaticSymbol} from '@angular/compiler';
|
export {AotCompilerHost, AotCompilerHost as StaticReflectorHost, StaticReflector, StaticSymbol} from '@angular/compiler';
|
||||||
export {CodeGenerator} from './src/codegen';
|
export {CodeGenerator} from './src/codegen';
|
||||||
export {CompilerHost, CompilerHostContext, NodeCompilerHostContext} from './src/compiler_host';
|
export {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter, NodeCompilerHostContext} from './src/compiler_host';
|
||||||
export {Extractor} from './src/extractor';
|
export {Extractor} from './src/extractor';
|
||||||
|
|
||||||
export * from '@angular/tsc-wrapped';
|
export * from '@angular/tsc-wrapped';
|
||||||
|
export {VERSION} from './src/version';
|
||||||
|
|
||||||
|
|
||||||
|
// TODO(hansl): moving to Angular 4 need to update this API.
|
||||||
|
export {NgTools_InternalApi_NG_2 as __NGTOOLS_PRIVATE_API_2} from './src/ngtools_api'
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
:host {
|
||||||
|
background-color: blue;
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
<div>
|
||||||
|
<h1>hello world</h1>
|
||||||
|
<a [routerLink]="['lazy']">lazy</a>
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-root',
|
||||||
|
templateUrl: 'app.component.html',
|
||||||
|
styleUrls: ['app.component.css'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
|
import {RouterModule} from '@angular/router';
|
||||||
|
|
||||||
|
import {AppComponent} from './app.component';
|
||||||
|
|
||||||
|
@Component({selector: 'home-view', template: 'home!'})
|
||||||
|
export class HomeView {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [AppComponent, HomeView],
|
||||||
|
imports: [
|
||||||
|
BrowserModule, RouterModule.forRoot([
|
||||||
|
{path: 'lazy', loadChildren: './lazy.module#LazyModule'},
|
||||||
|
{path: 'feature2', loadChildren: 'feature2/feature2.module#Feature2Module'},
|
||||||
|
{path: '', component: HomeView}
|
||||||
|
])
|
||||||
|
],
|
||||||
|
bootstrap: [AppComponent]
|
||||||
|
})
|
||||||
|
export class AppModule {
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
import {RouterModule} from '@angular/router';
|
||||||
|
|
||||||
|
@Component({selector: 'feature-component', template: 'foo.html'})
|
||||||
|
export class FeatureComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [FeatureComponent],
|
||||||
|
imports: [RouterModule.forChild([{path: '', component: FeatureComponent}])]
|
||||||
|
})
|
||||||
|
export class FeatureModule {
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
import {RouterModule} from '@angular/router';
|
||||||
|
|
||||||
|
@Component({selector: 'lazy-feature-comp', template: 'lazy feature!'})
|
||||||
|
export class LazyFeatureComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild([
|
||||||
|
{path: '', component: LazyFeatureComponent, pathMatch: 'full'},
|
||||||
|
{path: 'feature', loadChildren: './feature.module#FeatureModule'}
|
||||||
|
])],
|
||||||
|
declarations: [LazyFeatureComponent]
|
||||||
|
})
|
||||||
|
export class LazyFeatureModule {
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
import {RouterModule} from '@angular/router';
|
||||||
|
|
||||||
|
@Component({selector: 'feature-component', template: 'foo.html'})
|
||||||
|
export class FeatureComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [FeatureComponent],
|
||||||
|
imports: [RouterModule.forChild([
|
||||||
|
{path: '', component: FeatureComponent},
|
||||||
|
])]
|
||||||
|
})
|
||||||
|
export default class DefaultModule {
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
import {RouterModule} from '@angular/router';
|
||||||
|
|
||||||
|
@Component({selector: 'feature-component', template: 'foo.html'})
|
||||||
|
export class FeatureComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [FeatureComponent],
|
||||||
|
imports: [RouterModule.forChild([
|
||||||
|
{path: '', component: FeatureComponent}, {path: 'd', loadChildren: './default.module'} {
|
||||||
|
path: 'e',
|
||||||
|
loadChildren: 'feature/feature.module#FeatureModule'
|
||||||
|
}
|
||||||
|
])]
|
||||||
|
})
|
||||||
|
export class Feature2Module {
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
import {RouterModule} from '@angular/router';
|
||||||
|
|
||||||
|
@Component({selector: 'lazy-comp', template: 'lazy!'})
|
||||||
|
export class LazyComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild([
|
||||||
|
{path: '', component: LazyComponent, pathMatch: 'full'},
|
||||||
|
{path: 'feature', loadChildren: './feature/feature.module#FeatureModule'},
|
||||||
|
{path: 'lazy-feature', loadChildren: './feature/lazy-feature.module#LazyFeatureModule'}
|
||||||
|
])],
|
||||||
|
declarations: [LazyComponent]
|
||||||
|
})
|
||||||
|
export class LazyModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SecondModule {}
|
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
// For TypeScript 1.8, we have to lay out generated files
|
||||||
|
// in the same source directory with your code.
|
||||||
|
"genDir": ".",
|
||||||
|
"debug": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "",
|
||||||
|
"declaration": true,
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"baseUrl": "."
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE translationbundle [<!ELEMENT translationbundle (translation)*>
|
|
||||||
<!ATTLIST translationbundle lang CDATA #REQUIRED>
|
|
||||||
<!ELEMENT translation (#PCDATA|ph)*>
|
|
||||||
<!ATTLIST translation id CDATA #REQUIRED>
|
|
||||||
<!ELEMENT ph EMPTY>
|
|
||||||
<!ATTLIST ph name CDATA #REQUIRED>
|
|
||||||
]>
|
|
||||||
<translationbundle>
|
|
||||||
<translation id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4">käännä teksti</translation>
|
|
||||||
<translation id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">tervetuloa</translation>
|
|
||||||
<translation id="63a85808f03b8181e36a952e0fa38202c2304862">other-3rdP-component</translation>
|
|
||||||
</translationbundle>
|
|
@ -11,7 +11,9 @@ import {FormsModule} from '@angular/forms';
|
|||||||
import {ServerModule} from '@angular/platform-server';
|
import {ServerModule} from '@angular/platform-server';
|
||||||
import {MdButtonModule} from '@angular2-material/button';
|
import {MdButtonModule} from '@angular2-material/button';
|
||||||
|
|
||||||
import {ThirdpartyModule} from '../third_party_src/module';
|
// Note: don't refer to third_party_src as we want to test that
|
||||||
|
// we can compile components from node_modules!
|
||||||
|
import {ThirdpartyModule} from 'third_party/module';
|
||||||
|
|
||||||
import {MultipleComponentsMyComp, NextComp} from './a/multiple_components';
|
import {MultipleComponentsMyComp, NextComp} from './a/multiple_components';
|
||||||
import {AnimateCmp} from './animate';
|
import {AnimateCmp} from './animate';
|
||||||
@ -19,7 +21,7 @@ import {BasicComp} from './basic';
|
|||||||
import {ComponentUsingThirdParty} from './comp_using_3rdp';
|
import {ComponentUsingThirdParty} from './comp_using_3rdp';
|
||||||
import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components';
|
import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components';
|
||||||
import {CompConsumingEvents, CompUsingPipes, CompWithProviders, CompWithReferences, DirPublishingEvents, ModuleUsingCustomElements} from './features';
|
import {CompConsumingEvents, CompUsingPipes, CompWithProviders, CompWithReferences, DirPublishingEvents, ModuleUsingCustomElements} from './features';
|
||||||
import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomePipeInRootModule, SomeService, someLibModuleWithProviders} from './module_fixtures';
|
import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomeLibModule, SomePipeInRootModule, SomeService} from './module_fixtures';
|
||||||
import {CompWithNgContent, ProjectingComp} from './projection';
|
import {CompWithNgContent, ProjectingComp} from './projection';
|
||||||
import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, DirectiveForQuery} from './queries';
|
import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, DirectiveForQuery} from './queries';
|
||||||
|
|
||||||
@ -52,7 +54,7 @@ import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, Directive
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
MdButtonModule,
|
MdButtonModule,
|
||||||
ModuleUsingCustomElements,
|
ModuleUsingCustomElements,
|
||||||
someLibModuleWithProviders(),
|
SomeLibModule.withProviders(),
|
||||||
ThirdpartyModule,
|
ThirdpartyModule,
|
||||||
],
|
],
|
||||||
providers: [SomeService],
|
providers: [SomeService],
|
||||||
|
@ -63,17 +63,13 @@ export function provideValueWithEntryComponents(value: any) {
|
|||||||
entryComponents: [CompUsingLibModuleDirectiveAndPipe],
|
entryComponents: [CompUsingLibModuleDirectiveAndPipe],
|
||||||
})
|
})
|
||||||
export class SomeLibModule {
|
export class SomeLibModule {
|
||||||
}
|
static withProviders() {
|
||||||
|
|
||||||
// TODO(tbosch): Make this a static method in `SomeLibModule` once
|
|
||||||
// our static reflector supports it.
|
|
||||||
// See https://github.com/angular/angular/issues/10266.
|
|
||||||
export function someLibModuleWithProviders(): ModuleWithProviders {
|
|
||||||
return {
|
return {
|
||||||
ngModule: SomeLibModule,
|
ngModule: SomeLibModule,
|
||||||
providers: [
|
providers: [
|
||||||
ServiceUsingLibModule,
|
ServiceUsingLibModule, provideValueWithEntryComponents(
|
||||||
provideValueWithEntryComponents([{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}])
|
[{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}])
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,9 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
|
|||||||
<!ELEMENT ex (#PCDATA)>
|
<!ELEMENT ex (#PCDATA)>
|
||||||
]>
|
]>
|
||||||
<messagebundle>
|
<messagebundle>
|
||||||
<msg id="63a85808f03b8181e36a952e0fa38202c2304862">other-3rdP-component</msg>
|
<msg id="3772663375917578720">other-3rdP-component</msg>
|
||||||
<msg id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" desc="desc" meaning="meaning">translate me</msg>
|
<msg id="8136548302122759730" desc="desc" meaning="meaning">translate me</msg>
|
||||||
<msg id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">Welcome</msg>
|
<msg id="3492007542396725315">Welcome</msg>
|
||||||
</messagebundle>
|
</messagebundle>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -79,5 +79,4 @@ describe('template i18n extraction output', () => {
|
|||||||
const xlf = fs.readFileSync(xlfOutput, {encoding: 'utf-8'});
|
const xlf = fs.readFileSync(xlfOutput, {encoding: 'utf-8'});
|
||||||
expect(xlf).toEqual(EXPECTED_XLIFF);
|
expect(xlf).toEqual(EXPECTED_XLIFF);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,158 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
/* tslint:disable:no-console */
|
||||||
|
|
||||||
|
// Must be imported first, because angular2 decorators throws on load.
|
||||||
|
import 'reflect-metadata';
|
||||||
|
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
import * as assert from 'assert';
|
||||||
|
import {tsc} from '@angular/tsc-wrapped/src/tsc';
|
||||||
|
import {AngularCompilerOptions, CodeGenerator, CompilerHostContext, NodeCompilerHostContext, __NGTOOLS_PRIVATE_API_2} from '@angular/compiler-cli';
|
||||||
|
|
||||||
|
const glob = require('glob');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main method.
|
||||||
|
* Standalone program that executes codegen using the ngtools API and tests that files were
|
||||||
|
* properly read and wrote.
|
||||||
|
*/
|
||||||
|
function main() {
|
||||||
|
console.log(`testing ngtools API...`);
|
||||||
|
|
||||||
|
Promise.resolve()
|
||||||
|
.then(() => codeGenTest())
|
||||||
|
.then(() => lazyRoutesTest())
|
||||||
|
.then(() => {
|
||||||
|
console.log('All done!');
|
||||||
|
process.exit(0);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err.stack);
|
||||||
|
console.error('Test failed');
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function codeGenTest() {
|
||||||
|
const basePath = path.join(__dirname, '../ngtools_src');
|
||||||
|
const project = path.join(basePath, 'tsconfig-build.json');
|
||||||
|
const readResources: string[] = [];
|
||||||
|
const wroteFiles: string[] = [];
|
||||||
|
|
||||||
|
const config = tsc.readConfiguration(project, basePath);
|
||||||
|
const hostContext = new NodeCompilerHostContext();
|
||||||
|
const delegateHost = ts.createCompilerHost(config.parsed.options, true);
|
||||||
|
const host: ts.CompilerHost = Object.assign({}, delegateHost, {
|
||||||
|
writeFile: (fileName: string, ...rest: any[]) => {
|
||||||
|
wroteFiles.push(fileName);
|
||||||
|
return delegateHost.writeFile.call(delegateHost, fileName, ...rest);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
|
||||||
|
|
||||||
|
config.ngOptions.basePath = basePath;
|
||||||
|
|
||||||
|
console.log(`>>> running codegen for ${project}`);
|
||||||
|
return __NGTOOLS_PRIVATE_API_2
|
||||||
|
.codeGen({
|
||||||
|
basePath,
|
||||||
|
compilerOptions: config.parsed.options, program, host,
|
||||||
|
|
||||||
|
angularCompilerOptions: config.ngOptions,
|
||||||
|
|
||||||
|
// i18n options.
|
||||||
|
i18nFormat: null,
|
||||||
|
i18nFile: null,
|
||||||
|
locale: null,
|
||||||
|
|
||||||
|
readResource: (fileName: string) => {
|
||||||
|
readResources.push(fileName);
|
||||||
|
return hostContext.readResource(fileName);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
console.log(`>>> codegen done, asserting read and wrote files`);
|
||||||
|
|
||||||
|
// Assert for each file that it has been read and each `ts` has a written file associated.
|
||||||
|
const allFiles = glob.sync(path.join(basePath, '**/*'), {nodir: true});
|
||||||
|
|
||||||
|
allFiles.forEach((fileName: string) => {
|
||||||
|
// Skip tsconfig.
|
||||||
|
if (fileName.match(/tsconfig-build.json$/)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that file was read.
|
||||||
|
if (fileName.match(/\.module\.ts$/)) {
|
||||||
|
const factory = fileName.replace(/\.module\.ts$/, '.module.ngfactory.ts');
|
||||||
|
assert(wroteFiles.indexOf(factory) != -1, `Expected file "${factory}" to be written.`);
|
||||||
|
} else if (fileName.match(/\.css$/) || fileName.match(/\.html$/)) {
|
||||||
|
assert(
|
||||||
|
readResources.indexOf(fileName) != -1,
|
||||||
|
`Expected resource "${fileName}" to be read.`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`done, no errors.`);
|
||||||
|
})
|
||||||
|
.catch((e: any) => {
|
||||||
|
console.error(e.stack);
|
||||||
|
console.error('Compilation failed');
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function lazyRoutesTest() {
|
||||||
|
const basePath = path.join(__dirname, '../ngtools_src');
|
||||||
|
const project = path.join(basePath, 'tsconfig-build.json');
|
||||||
|
|
||||||
|
const config = tsc.readConfiguration(project, basePath);
|
||||||
|
const host = ts.createCompilerHost(config.parsed.options, true);
|
||||||
|
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
|
||||||
|
|
||||||
|
config.ngOptions.basePath = basePath;
|
||||||
|
|
||||||
|
const lazyRoutes = __NGTOOLS_PRIVATE_API_2.listLazyRoutes({
|
||||||
|
program,
|
||||||
|
host,
|
||||||
|
angularCompilerOptions: config.ngOptions,
|
||||||
|
entryModule: 'app.module#AppModule'
|
||||||
|
});
|
||||||
|
|
||||||
|
const expectations: {[route: string]: string} = {
|
||||||
|
'./lazy.module#LazyModule': 'lazy.module.ts',
|
||||||
|
'./feature/feature.module#FeatureModule': 'feature/feature.module.ts',
|
||||||
|
'./feature/lazy-feature.module#LazyFeatureModule': 'feature/lazy-feature.module.ts',
|
||||||
|
'feature2/feature2.module#Feature2Module': 'feature2/feature2.module.ts',
|
||||||
|
'./default.module': 'feature2/default.module.ts',
|
||||||
|
'feature/feature.module#FeatureModule': 'feature/feature.module.ts'
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(lazyRoutes).forEach((route: string) => {
|
||||||
|
assert(route in expectations, `Found a route that was not expected: "${route}".`);
|
||||||
|
assert(
|
||||||
|
lazyRoutes[route] == path.join(basePath, expectations[route]),
|
||||||
|
`Route "${route}" does not point to the expected absolute path ` +
|
||||||
|
`"${path.join(basePath, expectations[route])}". It points to "${lazyRoutes[route]}"`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify that all expectations were met.
|
||||||
|
assert.deepEqual(
|
||||||
|
Object.keys(lazyRoutes), Object.keys(expectations), `Expected routes listed to be: \n` +
|
||||||
|
` ${JSON.stringify(Object.keys(expectations))}\n` +
|
||||||
|
`Actual:\n` +
|
||||||
|
` ${JSON.stringify(Object.keys(lazyRoutes))}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
@ -0,0 +1,100 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
/* tslint:disable:no-console */
|
||||||
|
|
||||||
|
// Must be imported first, because angular2 decorators throws on load.
|
||||||
|
import 'reflect-metadata';
|
||||||
|
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
import * as assert from 'assert';
|
||||||
|
import {tsc} from '@angular/tsc-wrapped/src/tsc';
|
||||||
|
import {AngularCompilerOptions, CodeGenerator, CompilerHostContext, NodeCompilerHostContext} from '@angular/compiler-cli';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main method.
|
||||||
|
* Standalone program that executes the real codegen and tests that
|
||||||
|
* ngsummary.json files are used for libraries.
|
||||||
|
*/
|
||||||
|
function main() {
|
||||||
|
console.log(`testing usage of ngsummary.json files in libraries...`);
|
||||||
|
const basePath = path.resolve(__dirname, '..');
|
||||||
|
const project = path.resolve(basePath, 'tsconfig-build.json');
|
||||||
|
const readFiles: string[] = [];
|
||||||
|
|
||||||
|
class AssertingHostContext extends NodeCompilerHostContext {
|
||||||
|
readFile(fileName: string): string {
|
||||||
|
readFiles.push(path.relative(basePath, fileName));
|
||||||
|
return super.readFile(fileName);
|
||||||
|
}
|
||||||
|
readResource(s: string): Promise<string> {
|
||||||
|
readFiles.push(path.relative(basePath, s));
|
||||||
|
return super.readResource(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = tsc.readConfiguration(project, basePath);
|
||||||
|
config.ngOptions.basePath = basePath;
|
||||||
|
// This flag tells ngc do not recompile libraries.
|
||||||
|
config.ngOptions.generateCodeForLibraries = false;
|
||||||
|
|
||||||
|
console.log(`>>> running codegen for ${project}`);
|
||||||
|
codegen(config, (host) => new AssertingHostContext())
|
||||||
|
.then((exitCode: any) => {
|
||||||
|
console.log(`>>> codegen done, asserting read files`);
|
||||||
|
assertSomeFileMatch(readFiles, /^node_modules\/.*\.ngsummary\.json$/);
|
||||||
|
assertNoFileMatch(readFiles, /^node_modules\/.*\.html$/);
|
||||||
|
assertNoFileMatch(readFiles, /^node_modules\/.*\.css$/);
|
||||||
|
|
||||||
|
assertNoFileMatch(readFiles, /^src\/.*\.ngsummary\.json$/);
|
||||||
|
assertSomeFileMatch(readFiles, /^src\/.*\.html$/);
|
||||||
|
assertSomeFileMatch(readFiles, /^src\/.*\.css$/);
|
||||||
|
console.log(`done, no errors.`);
|
||||||
|
process.exit(exitCode);
|
||||||
|
})
|
||||||
|
.catch((e: any) => {
|
||||||
|
console.error(e.stack);
|
||||||
|
console.error('Compilation failed');
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple adaption of tsc-wrapped main to just run codegen with a CompilerHostContext
|
||||||
|
*/
|
||||||
|
function codegen(
|
||||||
|
config: {parsed: ts.ParsedCommandLine, ngOptions: AngularCompilerOptions},
|
||||||
|
hostContextFactory: (host: ts.CompilerHost) => CompilerHostContext) {
|
||||||
|
const host = ts.createCompilerHost(config.parsed.options, true);
|
||||||
|
|
||||||
|
// HACK: patch the realpath to solve symlink issue here:
|
||||||
|
// https://github.com/Microsoft/TypeScript/issues/9552
|
||||||
|
// todo(misko): remove once facade symlinks are removed
|
||||||
|
host.realpath = (path) => path;
|
||||||
|
|
||||||
|
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
|
||||||
|
|
||||||
|
return CodeGenerator.create(config.ngOptions, {
|
||||||
|
} as any, program, host, hostContextFactory(host)).codegen();
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertSomeFileMatch(fileNames: string[], pattern: RegExp) {
|
||||||
|
assert(
|
||||||
|
fileNames.some(fileName => pattern.test(fileName)),
|
||||||
|
`Expected some read files match ${pattern}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertNoFileMatch(fileNames: string[], pattern: RegExp) {
|
||||||
|
const matches = fileNames.filter(fileName => pattern.test(fileName));
|
||||||
|
assert(
|
||||||
|
matches.length === 0,
|
||||||
|
`Expected no read files match ${pattern}, but found: \n${matches.join('\n')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
@ -21,6 +21,8 @@
|
|||||||
"src/module",
|
"src/module",
|
||||||
"src/bootstrap",
|
"src/bootstrap",
|
||||||
"test/all_spec",
|
"test/all_spec",
|
||||||
|
"test/test_ngtools_api",
|
||||||
|
"test/test_summaries",
|
||||||
"benchmarks/src/tree/ng2/index_aot.ts",
|
"benchmarks/src/tree/ng2/index_aot.ts",
|
||||||
"benchmarks/src/tree/ng2_switch/index_aot.ts",
|
"benchmarks/src/tree/ng2_switch/index_aot.ts",
|
||||||
"benchmarks/src/largetable/ng2/index_aot.ts",
|
"benchmarks/src/largetable/ng2/index_aot.ts",
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"ng-xi18n": "./src/extract_i18n.js"
|
"ng-xi18n": "./src/extract_i18n.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/tsc-wrapped": "^0.4.0",
|
"@angular/tsc-wrapped": "0.5.0",
|
||||||
"reflect-metadata": "^0.1.2",
|
"reflect-metadata": "^0.1.2",
|
||||||
"minimist": "^1.2.0"
|
"minimist": "^1.2.0"
|
||||||
},
|
},
|
||||||
|
@ -17,12 +17,13 @@ import {readFileSync} from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {CompilerHost, CompilerHostContext} from './compiler_host';
|
import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './compiler_host';
|
||||||
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
|
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
|
||||||
import {Console} from './private_import_core';
|
import {Console} from './private_import_core';
|
||||||
|
|
||||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
const GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$/;
|
||||||
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
const GENERATED_META_FILES = /\.json$/;
|
||||||
|
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.ngstyle\.ts$/;
|
||||||
|
|
||||||
const PREAMBLE = `/**
|
const PREAMBLE = `/**
|
||||||
* @fileoverview This file is generated by the Angular 2 template compiler.
|
* @fileoverview This file is generated by the Angular 2 template compiler.
|
||||||
@ -44,7 +45,7 @@ export class CodeGenerator {
|
|||||||
let root = this.options.basePath;
|
let root = this.options.basePath;
|
||||||
for (const eachRootDir of this.options.rootDirs || []) {
|
for (const eachRootDir of this.options.rootDirs || []) {
|
||||||
if (this.options.trace) {
|
if (this.options.trace) {
|
||||||
console.log(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
console.error(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
||||||
}
|
}
|
||||||
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
|
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
|
||||||
root = eachRootDir;
|
root = eachRootDir;
|
||||||
@ -68,10 +69,11 @@ export class CodeGenerator {
|
|||||||
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName)))
|
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName)))
|
||||||
.then(generatedModules => {
|
.then(generatedModules => {
|
||||||
generatedModules.forEach(generatedModule => {
|
generatedModules.forEach(generatedModule => {
|
||||||
const sourceFile = this.program.getSourceFile(generatedModule.fileUrl);
|
const sourceFile = this.program.getSourceFile(generatedModule.srcFileUrl);
|
||||||
const emitPath = this.calculateEmitPath(generatedModule.moduleUrl);
|
const emitPath = this.calculateEmitPath(generatedModule.genFileUrl);
|
||||||
this.host.writeFile(
|
const source = GENERATED_META_FILES.test(emitPath) ? generatedModule.source :
|
||||||
emitPath, PREAMBLE + generatedModule.source, false, () => {}, [sourceFile]);
|
PREAMBLE + generatedModule.source;
|
||||||
|
this.host.writeFile(emitPath, source, false, () => {}, [sourceFile]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -82,9 +84,9 @@ export class CodeGenerator {
|
|||||||
ngCompilerHost?: CompilerHost): CodeGenerator {
|
ngCompilerHost?: CompilerHost): CodeGenerator {
|
||||||
if (!ngCompilerHost) {
|
if (!ngCompilerHost) {
|
||||||
const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0;
|
const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0;
|
||||||
ngCompilerHost = usePathMapping ?
|
const context = compilerHostContext || new ModuleResolutionHostAdapter(tsCompilerHost);
|
||||||
new PathMappedCompilerHost(program, tsCompilerHost, options, compilerHostContext) :
|
ngCompilerHost = usePathMapping ? new PathMappedCompilerHost(program, options, context) :
|
||||||
new CompilerHost(program, tsCompilerHost, options, compilerHostContext);
|
new CompilerHost(program, options, context);
|
||||||
}
|
}
|
||||||
const transFile = cliOptions.i18nFile;
|
const transFile = cliOptions.i18nFile;
|
||||||
const locale = cliOptions.locale;
|
const locale = cliOptions.locale;
|
||||||
|
@ -15,32 +15,27 @@ import * as ts from 'typescript';
|
|||||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||||
const DTS = /\.d\.ts$/;
|
const DTS = /\.d\.ts$/;
|
||||||
const NODE_MODULES = '/node_modules/';
|
const NODE_MODULES = '/node_modules/';
|
||||||
const IS_GENERATED = /\.(ngfactory|css(\.shim)?)$/;
|
const IS_GENERATED = /\.(ngfactory|ngstyle)$/;
|
||||||
|
|
||||||
export interface CompilerHostContext {
|
export interface CompilerHostContext extends ts.ModuleResolutionHost {
|
||||||
fileExists(fileName: string): boolean;
|
|
||||||
directoryExists(directoryName: string): boolean;
|
|
||||||
readFile(fileName: string): string;
|
|
||||||
readResource(fileName: string): Promise<string>;
|
readResource(fileName: string): Promise<string>;
|
||||||
assumeFileExists(fileName: string): void;
|
assumeFileExists(fileName: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompilerHost implements AotCompilerHost {
|
export class CompilerHost implements AotCompilerHost {
|
||||||
protected metadataCollector = new MetadataCollector();
|
protected metadataCollector = new MetadataCollector();
|
||||||
protected context: CompilerHostContext;
|
|
||||||
private isGenDirChildOfRootDir: boolean;
|
private isGenDirChildOfRootDir: boolean;
|
||||||
protected basePath: string;
|
protected basePath: string;
|
||||||
private genDir: string;
|
private genDir: string;
|
||||||
private resolverCache = new Map<string, ModuleMetadata[]>();
|
private resolverCache = new Map<string, ModuleMetadata[]>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected program: ts.Program, protected compilerHost: ts.CompilerHost,
|
protected program: ts.Program, protected options: AngularCompilerOptions,
|
||||||
protected options: AngularCompilerOptions, context?: CompilerHostContext) {
|
protected context: CompilerHostContext) {
|
||||||
// normalize the path so that it never ends with '/'.
|
// normalize the path so that it never ends with '/'.
|
||||||
this.basePath = path.normalize(path.join(this.options.basePath, '.')).replace(/\\/g, '/');
|
this.basePath = path.normalize(path.join(this.options.basePath, '.')).replace(/\\/g, '/');
|
||||||
this.genDir = path.normalize(path.join(this.options.genDir, '.')).replace(/\\/g, '/');
|
this.genDir = path.normalize(path.join(this.options.genDir, '.')).replace(/\\/g, '/');
|
||||||
|
|
||||||
this.context = context || new NodeCompilerHostContext(compilerHost);
|
|
||||||
const genPath: string = path.relative(this.basePath, this.genDir);
|
const genPath: string = path.relative(this.basePath, this.genDir);
|
||||||
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
|
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
|
||||||
}
|
}
|
||||||
@ -81,7 +76,7 @@ export class CompilerHost implements AotCompilerHost {
|
|||||||
fileNameToModuleName(importedFile: string, containingFile: string): string {
|
fileNameToModuleName(importedFile: string, containingFile: string): string {
|
||||||
// If a file does not yet exist (because we compile it later), we still need to
|
// If a file does not yet exist (because we compile it later), we still need to
|
||||||
// assume it exists it so that the `resolve` method works!
|
// assume it exists it so that the `resolve` method works!
|
||||||
if (!this.compilerHost.fileExists(importedFile)) {
|
if (!this.context.fileExists(importedFile)) {
|
||||||
this.context.assumeFileExists(importedFile);
|
this.context.assumeFileExists(importedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,29 +176,32 @@ export class CompilerHost implements AotCompilerHost {
|
|||||||
const metadatas = metadataOrMetadatas ?
|
const metadatas = metadataOrMetadatas ?
|
||||||
(Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) :
|
(Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) :
|
||||||
[];
|
[];
|
||||||
const v1Metadata = metadatas.find(m => m['version'] === 1);
|
const v1Metadata = metadatas.find((m: any) => m['version'] === 1);
|
||||||
let v2Metadata = metadatas.find(m => m['version'] === 2);
|
let v3Metadata = metadatas.find((m: any) => m['version'] === 3);
|
||||||
if (!v2Metadata && v1Metadata) {
|
if (!v3Metadata && v1Metadata) {
|
||||||
// patch up v1 to v2 by merging the metadata with metadata collected from the d.ts file
|
// patch up v1 to v3 by merging the metadata with metadata collected from the d.ts file
|
||||||
// as the only difference between the versions is whether all exports are contained in
|
// as the only difference between the versions is whether all exports are contained in
|
||||||
// the metadata
|
// the metadata and the `extends` clause.
|
||||||
v2Metadata = {'__symbolic': 'module', 'version': 2, 'metadata': {}};
|
v3Metadata = {'__symbolic': 'module', 'version': 3, 'metadata': {}};
|
||||||
if (v1Metadata.exports) {
|
if (v1Metadata.exports) {
|
||||||
v2Metadata.exports = v1Metadata.exports;
|
v3Metadata.exports = v1Metadata.exports;
|
||||||
}
|
}
|
||||||
for (let prop in v1Metadata.metadata) {
|
for (let prop in v1Metadata.metadata) {
|
||||||
v2Metadata.metadata[prop] = v1Metadata.metadata[prop];
|
v3Metadata.metadata[prop] = v1Metadata.metadata[prop];
|
||||||
}
|
}
|
||||||
const sourceText = this.context.readFile(dtsFilePath);
|
|
||||||
const exports = this.metadataCollector.getMetadata(this.getSourceFile(dtsFilePath));
|
const exports = this.metadataCollector.getMetadata(this.getSourceFile(dtsFilePath));
|
||||||
if (exports) {
|
if (exports) {
|
||||||
for (let prop in exports.metadata) {
|
for (let prop in exports.metadata) {
|
||||||
if (!v2Metadata.metadata[prop]) {
|
if (!v3Metadata.metadata[prop]) {
|
||||||
v2Metadata.metadata[prop] = exports.metadata[prop];
|
v3Metadata.metadata[prop] = exports.metadata[prop];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (exports.exports) {
|
||||||
|
v3Metadata.exports = exports.exports;
|
||||||
}
|
}
|
||||||
metadatas.push(v2Metadata);
|
}
|
||||||
|
metadatas.push(v3Metadata);
|
||||||
}
|
}
|
||||||
this.resolverCache.set(filePath, metadatas);
|
this.resolverCache.set(filePath, metadatas);
|
||||||
return metadatas;
|
return metadatas;
|
||||||
@ -214,17 +212,52 @@ export class CompilerHost implements AotCompilerHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadResource(filePath: string): Promise<string> { return this.context.readResource(filePath); }
|
loadResource(filePath: string): Promise<string> { return this.context.readResource(filePath); }
|
||||||
|
|
||||||
|
loadSummary(filePath: string): string { return this.context.readFile(filePath); }
|
||||||
|
|
||||||
|
getOutputFileName(sourceFilePath: string): string {
|
||||||
|
return sourceFilePath.replace(EXT, '') + '.d.ts';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NodeCompilerHostContext implements CompilerHostContext {
|
export class CompilerHostContextAdapter {
|
||||||
constructor(private host: ts.CompilerHost) {}
|
protected assumedExists: {[fileName: string]: boolean} = {};
|
||||||
|
|
||||||
private assumedExists: {[fileName: string]: boolean} = {};
|
assumeFileExists(fileName: string): void { this.assumedExists[fileName] = true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ModuleResolutionHostAdapter extends CompilerHostContextAdapter implements
|
||||||
|
CompilerHostContext {
|
||||||
|
public directoryExists: ((directoryName: string) => boolean)|undefined;
|
||||||
|
|
||||||
|
constructor(private host: ts.ModuleResolutionHost) {
|
||||||
|
super();
|
||||||
|
if (host.directoryExists) {
|
||||||
|
this.directoryExists = (directoryName: string) => host.directoryExists(directoryName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fileExists(fileName: string): boolean {
|
fileExists(fileName: string): boolean {
|
||||||
return this.assumedExists[fileName] || this.host.fileExists(fileName);
|
return this.assumedExists[fileName] || this.host.fileExists(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readFile(fileName: string): string { return this.host.readFile(fileName); }
|
||||||
|
|
||||||
|
readResource(s: string) {
|
||||||
|
if (!this.host.fileExists(s)) {
|
||||||
|
// TODO: We should really have a test for error cases like this!
|
||||||
|
throw new Error(`Compilation failed. Resource file not found: ${s}`);
|
||||||
|
}
|
||||||
|
return Promise.resolve(this.host.readFile(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NodeCompilerHostContext extends CompilerHostContextAdapter implements
|
||||||
|
CompilerHostContext {
|
||||||
|
fileExists(fileName: string): boolean {
|
||||||
|
return this.assumedExists[fileName] || fs.existsSync(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
directoryExists(directoryName: string): boolean {
|
directoryExists(directoryName: string): boolean {
|
||||||
try {
|
try {
|
||||||
return fs.statSync(directoryName).isDirectory();
|
return fs.statSync(directoryName).isDirectory();
|
||||||
@ -236,12 +269,10 @@ export class NodeCompilerHostContext implements CompilerHostContext {
|
|||||||
readFile(fileName: string): string { return fs.readFileSync(fileName, 'utf8'); }
|
readFile(fileName: string): string { return fs.readFileSync(fileName, 'utf8'); }
|
||||||
|
|
||||||
readResource(s: string) {
|
readResource(s: string) {
|
||||||
if (!this.host.fileExists(s)) {
|
if (!this.fileExists(s)) {
|
||||||
// TODO: We should really have a test for error cases like this!
|
// TODO: We should really have a test for error cases like this!
|
||||||
throw new Error(`Compilation failed. Resource file not found: ${s}`);
|
throw new Error(`Compilation failed. Resource file not found: ${s}`);
|
||||||
}
|
}
|
||||||
return Promise.resolve(this.host.readFile(s));
|
return Promise.resolve(this.readFile(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
assumeFileExists(fileName: string): void { this.assumedExists[fileName] = true; }
|
|
||||||
}
|
}
|
||||||
|
@ -41,9 +41,8 @@ function extract(
|
|||||||
case 'xliff':
|
case 'xliff':
|
||||||
case 'xlf':
|
case 'xlf':
|
||||||
default:
|
default:
|
||||||
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
|
|
||||||
ext = 'xlf';
|
ext = 'xlf';
|
||||||
serializer = new compiler.Xliff(htmlParser, compiler.DEFAULT_INTERPOLATION_CONFIG);
|
serializer = new compiler.Xliff();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import * as tsc from '@angular/tsc-wrapped';
|
|||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {excludeFilePattern} from './codegen';
|
import {excludeFilePattern} from './codegen';
|
||||||
import {CompilerHost} from './compiler_host';
|
import {CompilerHost, ModuleResolutionHostAdapter} from './compiler_host';
|
||||||
|
|
||||||
export class Extractor {
|
export class Extractor {
|
||||||
constructor(
|
constructor(
|
||||||
@ -32,8 +32,10 @@ export class Extractor {
|
|||||||
|
|
||||||
static create(
|
static create(
|
||||||
options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program,
|
options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program,
|
||||||
tsCompilerHost: ts.CompilerHost, ngCompilerHost?: CompilerHost): Extractor {
|
moduleResolverHost: ts.ModuleResolutionHost, ngCompilerHost?: CompilerHost): Extractor {
|
||||||
if (!ngCompilerHost) ngCompilerHost = new CompilerHost(program, tsCompilerHost, options);
|
if (!ngCompilerHost)
|
||||||
|
ngCompilerHost =
|
||||||
|
new CompilerHost(program, options, new ModuleResolutionHostAdapter(moduleResolverHost));
|
||||||
const {extractor: ngExtractor} = compiler.Extractor.create(
|
const {extractor: ngExtractor} = compiler.Extractor.create(
|
||||||
ngCompilerHost, {excludeFilePattern: excludeFilePattern(options)});
|
ngCompilerHost, {excludeFilePattern: excludeFilePattern(options)});
|
||||||
return new Extractor(ngExtractor, ngCompilerHost, program);
|
return new Extractor(ngExtractor, ngCompilerHost, program);
|
||||||
|
@ -22,14 +22,25 @@ function codegen(
|
|||||||
return CodeGenerator.create(ngOptions, cliOptions, program, host).codegen();
|
return CodeGenerator.create(ngOptions, cliOptions, program, host).codegen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function main(
|
||||||
|
args: any, consoleError: (s: string) => void = console.error): Promise<number> {
|
||||||
|
const project = args.p || args.project || '.';
|
||||||
|
const cliOptions = new tsc.NgcCliOptions(args);
|
||||||
|
|
||||||
|
return tsc.main(project, cliOptions, codegen).then(() => 0).catch(e => {
|
||||||
|
if (e instanceof tsc.UserError) {
|
||||||
|
consoleError(e.message);
|
||||||
|
return Promise.resolve(1);
|
||||||
|
} else {
|
||||||
|
consoleError(e.stack);
|
||||||
|
consoleError('Compilation failed');
|
||||||
|
return Promise.resolve(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// CLI entry point
|
// CLI entry point
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
const args = require('minimist')(process.argv.slice(2));
|
const args = require('minimist')(process.argv.slice(2));
|
||||||
const project = args.p || args.project || '.';
|
main(args).then((exitCode: number) => process.exit(exitCode));
|
||||||
const cliOptions = new tsc.NgcCliOptions(args);
|
|
||||||
tsc.main(project, cliOptions, codegen).then(exitCode => process.exit(exitCode)).catch(e => {
|
|
||||||
console.error(e.stack);
|
|
||||||
console.error('Compilation failed');
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
124
modules/@angular/compiler-cli/src/ngtools_api.ts
Normal file
124
modules/@angular/compiler-cli/src/ngtools_api.ts
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a private API for the ngtools toolkit.
|
||||||
|
*
|
||||||
|
* This API should be stable for NG 2. It can be removed in NG 4..., but should be replaced by
|
||||||
|
* something else.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {AotCompilerHost, StaticReflector} from '@angular/compiler';
|
||||||
|
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
import {CodeGenerator} from './codegen';
|
||||||
|
import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './compiler_host';
|
||||||
|
import {listLazyRoutesOfModule} from './ngtools_impl';
|
||||||
|
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
|
||||||
|
|
||||||
|
|
||||||
|
export interface NgTools_InternalApi_NG2_CodeGen_Options {
|
||||||
|
basePath: string;
|
||||||
|
compilerOptions: ts.CompilerOptions;
|
||||||
|
program: ts.Program;
|
||||||
|
host: ts.CompilerHost;
|
||||||
|
|
||||||
|
angularCompilerOptions: AngularCompilerOptions;
|
||||||
|
|
||||||
|
// i18n options.
|
||||||
|
i18nFormat: string;
|
||||||
|
i18nFile: string;
|
||||||
|
locale: string;
|
||||||
|
|
||||||
|
readResource: (fileName: string) => Promise<string>;
|
||||||
|
|
||||||
|
// Every new property under this line should be optional.
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NgTools_InternalApi_NG2_ListLazyRoutes_Options {
|
||||||
|
program: ts.Program;
|
||||||
|
host: ts.CompilerHost;
|
||||||
|
angularCompilerOptions: AngularCompilerOptions;
|
||||||
|
entryModule: string;
|
||||||
|
|
||||||
|
// Every new property under this line should be optional.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface NgTools_InternalApi_NG_2_LazyRouteMap { [route: string]: string; }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ModuleResolutionHostAdapter that overrides the readResource() method with the one
|
||||||
|
* passed in the interface.
|
||||||
|
*/
|
||||||
|
class CustomLoaderModuleResolutionHostAdapter extends ModuleResolutionHostAdapter {
|
||||||
|
constructor(
|
||||||
|
private _readResource: (path: string) => Promise<string>, host: ts.ModuleResolutionHost) {
|
||||||
|
super(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
readResource(path: string) { return this._readResource(path); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export class NgTools_InternalApi_NG_2 {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static codeGen(options: NgTools_InternalApi_NG2_CodeGen_Options): Promise<void> {
|
||||||
|
const hostContext: CompilerHostContext =
|
||||||
|
new CustomLoaderModuleResolutionHostAdapter(options.readResource, options.host);
|
||||||
|
const cliOptions: NgcCliOptions = {
|
||||||
|
i18nFormat: options.i18nFormat,
|
||||||
|
i18nFile: options.i18nFile,
|
||||||
|
locale: options.locale,
|
||||||
|
basePath: options.basePath
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the Code Generator.
|
||||||
|
const codeGenerator = CodeGenerator.create(
|
||||||
|
options.angularCompilerOptions, cliOptions, options.program, options.host, hostContext);
|
||||||
|
|
||||||
|
return codeGenerator.codegen();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static listLazyRoutes(options: NgTools_InternalApi_NG2_ListLazyRoutes_Options):
|
||||||
|
NgTools_InternalApi_NG_2_LazyRouteMap {
|
||||||
|
const angularCompilerOptions = options.angularCompilerOptions;
|
||||||
|
const program = options.program;
|
||||||
|
|
||||||
|
const moduleResolutionHost = new ModuleResolutionHostAdapter(options.host);
|
||||||
|
const usePathMapping =
|
||||||
|
!!angularCompilerOptions.rootDirs && angularCompilerOptions.rootDirs.length > 0;
|
||||||
|
const ngCompilerHost: AotCompilerHost = usePathMapping ?
|
||||||
|
new PathMappedCompilerHost(program, angularCompilerOptions, moduleResolutionHost) :
|
||||||
|
new CompilerHost(program, angularCompilerOptions, moduleResolutionHost);
|
||||||
|
|
||||||
|
const staticReflector = new StaticReflector(ngCompilerHost);
|
||||||
|
const routeMap = listLazyRoutesOfModule(options.entryModule, ngCompilerHost, staticReflector);
|
||||||
|
|
||||||
|
return Object.keys(routeMap).reduce(
|
||||||
|
(acc: NgTools_InternalApi_NG_2_LazyRouteMap, route: string) => {
|
||||||
|
acc[route] = routeMap[route].absoluteFilePath;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{});
|
||||||
|
}
|
||||||
|
}
|
205
modules/@angular/compiler-cli/src/ngtools_impl.ts
Normal file
205
modules/@angular/compiler-cli/src/ngtools_impl.ts
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a private API for the ngtools toolkit.
|
||||||
|
*
|
||||||
|
* This API should be stable for NG 2. It can be removed in NG 4..., but should be replaced by
|
||||||
|
* something else.
|
||||||
|
*/
|
||||||
|
import {AotCompilerHost, StaticReflector, StaticSymbol} from '@angular/compiler';
|
||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// We cannot depend directly to @angular/router.
|
||||||
|
type Route = any;
|
||||||
|
const ROUTER_MODULE_PATH = '@angular/router/src/router_config_loader';
|
||||||
|
const ROUTER_ROUTES_SYMBOL_NAME = 'ROUTES';
|
||||||
|
|
||||||
|
|
||||||
|
// LazyRoute information between the extractors.
|
||||||
|
export interface LazyRoute {
|
||||||
|
routeDef: RouteDef;
|
||||||
|
absoluteFilePath: string;
|
||||||
|
}
|
||||||
|
export type LazyRouteMap = {
|
||||||
|
[route: string]: LazyRoute
|
||||||
|
};
|
||||||
|
|
||||||
|
// A route definition. Normally the short form 'path/to/module#ModuleClassName' is used by
|
||||||
|
// the user, and this is a helper class to extract information from it.
|
||||||
|
export class RouteDef {
|
||||||
|
private constructor(public readonly path: string, public readonly className: string = null) {}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return (this.className === null || this.className == 'default') ?
|
||||||
|
this.path :
|
||||||
|
`${this.path}#${this.className}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromString(entry: string): RouteDef {
|
||||||
|
const split = entry.split('#');
|
||||||
|
return new RouteDef(split[0], split[1] || null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {LazyRouteMap}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export function listLazyRoutesOfModule(
|
||||||
|
entryModule: string, host: AotCompilerHost, reflector: StaticReflector): LazyRouteMap {
|
||||||
|
const entryRouteDef = RouteDef.fromString(entryModule);
|
||||||
|
const containingFile = _resolveModule(entryRouteDef.path, entryRouteDef.path, host);
|
||||||
|
const modulePath = `./${containingFile.replace(/^(.*)\//, '')}`;
|
||||||
|
const className = entryRouteDef.className;
|
||||||
|
|
||||||
|
// List loadChildren of this single module.
|
||||||
|
const staticSymbol = reflector.findDeclaration(modulePath, className, containingFile);
|
||||||
|
const ROUTES = reflector.findDeclaration(ROUTER_MODULE_PATH, ROUTER_ROUTES_SYMBOL_NAME);
|
||||||
|
const lazyRoutes: LazyRoute[] =
|
||||||
|
_extractLazyRoutesFromStaticModule(staticSymbol, reflector, host, ROUTES);
|
||||||
|
const routes: LazyRouteMap = {};
|
||||||
|
|
||||||
|
lazyRoutes.forEach((lazyRoute: LazyRoute) => {
|
||||||
|
const route: string = lazyRoute.routeDef.toString();
|
||||||
|
_assertRoute(routes, lazyRoute);
|
||||||
|
routes[route] = lazyRoute;
|
||||||
|
|
||||||
|
const lazyModuleSymbol = reflector.findDeclaration(
|
||||||
|
lazyRoute.absoluteFilePath, lazyRoute.routeDef.className || 'default');
|
||||||
|
const subRoutes = _extractLazyRoutesFromStaticModule(lazyModuleSymbol, reflector, host, ROUTES);
|
||||||
|
|
||||||
|
// Populate the map using the routes we just found.
|
||||||
|
subRoutes.forEach(subRoute => {
|
||||||
|
_assertRoute(routes, subRoute);
|
||||||
|
routes[subRoute.routeDef.toString()] = subRoute;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to resolve a module, and returns its absolute path.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function _resolveModule(modulePath: string, containingFile: string, host: AotCompilerHost) {
|
||||||
|
const result = host.moduleNameToFileName(modulePath, containingFile);
|
||||||
|
if (!result) {
|
||||||
|
throw new Error(`Could not resolve "${modulePath}" from "${containingFile}".`);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throw an exception if a route is in a route map, but does not point to the same module.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function _assertRoute(map: LazyRouteMap, route: LazyRoute) {
|
||||||
|
const r = route.routeDef.toString();
|
||||||
|
if (map[r] && map[r].absoluteFilePath != route.absoluteFilePath) {
|
||||||
|
throw new Error(
|
||||||
|
`Duplicated path in loadChildren detected: "${r}" is used in 2 loadChildren, ` +
|
||||||
|
`but they point to different modules "(${map[r].absoluteFilePath} and ` +
|
||||||
|
`"${route.absoluteFilePath}"). Webpack cannot distinguish on context and would fail to ` +
|
||||||
|
'load the proper one.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract all the LazyRoutes from a module. This extracts all `loadChildren` keys from this
|
||||||
|
* module and all statically referred modules.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function _extractLazyRoutesFromStaticModule(
|
||||||
|
staticSymbol: StaticSymbol, reflector: StaticReflector, host: AotCompilerHost,
|
||||||
|
ROUTES: StaticSymbol): LazyRoute[] {
|
||||||
|
const moduleMetadata = _getNgModuleMetadata(staticSymbol, reflector);
|
||||||
|
const allRoutes: any =
|
||||||
|
(moduleMetadata.imports || [])
|
||||||
|
.filter(i => 'providers' in i)
|
||||||
|
.reduce((mem: Route[], m: any) => {
|
||||||
|
return mem.concat(_collectRoutes(m.providers || [], reflector, ROUTES));
|
||||||
|
}, _collectRoutes(moduleMetadata.providers || [], reflector, ROUTES));
|
||||||
|
|
||||||
|
const lazyRoutes: LazyRoute[] =
|
||||||
|
_collectLoadChildren(allRoutes).reduce((acc: LazyRoute[], route: string) => {
|
||||||
|
const routeDef = RouteDef.fromString(route);
|
||||||
|
const absoluteFilePath = _resolveModule(routeDef.path, staticSymbol.filePath, host);
|
||||||
|
acc.push({routeDef, absoluteFilePath});
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const importedSymbols = ((moduleMetadata.imports || []) as any[])
|
||||||
|
.filter(i => i instanceof StaticSymbol) as StaticSymbol[];
|
||||||
|
|
||||||
|
return importedSymbols
|
||||||
|
.reduce(
|
||||||
|
(acc: LazyRoute[], i: StaticSymbol) => {
|
||||||
|
return acc.concat(_extractLazyRoutesFromStaticModule(i, reflector, host, ROUTES));
|
||||||
|
},
|
||||||
|
[])
|
||||||
|
.concat(lazyRoutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the NgModule Metadata of a symbol.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function _getNgModuleMetadata(staticSymbol: StaticSymbol, reflector: StaticReflector): NgModule {
|
||||||
|
const ngModules = reflector.annotations(staticSymbol).filter((s: any) => s instanceof NgModule);
|
||||||
|
if (ngModules.length === 0) {
|
||||||
|
throw new Error(`${staticSymbol.name} is not an NgModule`);
|
||||||
|
}
|
||||||
|
return ngModules[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the routes from the provider list.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function _collectRoutes(
|
||||||
|
providers: any[], reflector: StaticReflector, ROUTES: StaticSymbol): Route[] {
|
||||||
|
return providers.reduce((routeList: Route[], p: any) => {
|
||||||
|
if (p.provide === ROUTES) {
|
||||||
|
return routeList.concat(p.useValue);
|
||||||
|
} else if (Array.isArray(p)) {
|
||||||
|
return routeList.concat(_collectRoutes(p, reflector, ROUTES));
|
||||||
|
} else {
|
||||||
|
return routeList;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the loadChildren values of a list of Route.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function _collectLoadChildren(routes: Route[]): string[] {
|
||||||
|
return routes.reduce((m, r) => {
|
||||||
|
if (r.loadChildren) {
|
||||||
|
return m.concat(r.loadChildren);
|
||||||
|
} else if (Array.isArray(r)) {
|
||||||
|
return m.concat(_collectLoadChildren(r));
|
||||||
|
} else if (r.children) {
|
||||||
|
return m.concat(_collectLoadChildren(r.children));
|
||||||
|
} else {
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
}
|
@ -25,10 +25,8 @@ const DTS = /\.d\.ts$/;
|
|||||||
* loader what to do.
|
* loader what to do.
|
||||||
*/
|
*/
|
||||||
export class PathMappedCompilerHost extends CompilerHost {
|
export class PathMappedCompilerHost extends CompilerHost {
|
||||||
constructor(
|
constructor(program: ts.Program, options: AngularCompilerOptions, context: CompilerHostContext) {
|
||||||
program: ts.Program, compilerHost: ts.CompilerHost, options: AngularCompilerOptions,
|
super(program, options, context);
|
||||||
context?: CompilerHostContext) {
|
|
||||||
super(program, compilerHost, options, context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCanonicalFileName(fileName: string): string {
|
getCanonicalFileName(fileName: string): string {
|
||||||
@ -56,7 +54,7 @@ export class PathMappedCompilerHost extends CompilerHost {
|
|||||||
ts.resolveModuleName(m, rootedContainingFile, this.options, this.context).resolvedModule;
|
ts.resolveModuleName(m, rootedContainingFile, this.options, this.context).resolvedModule;
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
if (this.options.traceResolution) {
|
if (this.options.traceResolution) {
|
||||||
console.log('resolve', m, containingFile, '=>', resolved.resolvedFileName);
|
console.error('resolve', m, containingFile, '=>', resolved.resolvedFileName);
|
||||||
}
|
}
|
||||||
return this.getCanonicalFileName(resolved.resolvedFileName);
|
return this.getCanonicalFileName(resolved.resolvedFileName);
|
||||||
}
|
}
|
||||||
@ -71,7 +69,7 @@ export class PathMappedCompilerHost extends CompilerHost {
|
|||||||
*/
|
*/
|
||||||
fileNameToModuleName(importedFile: string, containingFile: string): string {
|
fileNameToModuleName(importedFile: string, containingFile: string): string {
|
||||||
if (this.options.traceResolution) {
|
if (this.options.traceResolution) {
|
||||||
console.log(
|
console.error(
|
||||||
'getImportPath from containingFile', containingFile, 'to importedFile', importedFile);
|
'getImportPath from containingFile', containingFile, 'to importedFile', importedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +117,7 @@ export class PathMappedCompilerHost extends CompilerHost {
|
|||||||
getMetadataFor(filePath: string): ModuleMetadata[] {
|
getMetadataFor(filePath: string): ModuleMetadata[] {
|
||||||
for (const root of this.options.rootDirs || []) {
|
for (const root of this.options.rootDirs || []) {
|
||||||
const rootedPath = path.join(root, filePath);
|
const rootedPath = path.join(root, filePath);
|
||||||
if (!this.compilerHost.fileExists(rootedPath)) {
|
if (!this.context.fileExists(rootedPath)) {
|
||||||
// If the file doesn't exists then we cannot return metadata for the file.
|
// If the file doesn't exists then we cannot return metadata for the file.
|
||||||
// This will occur if the user refernced a declared module for which no file
|
// This will occur if the user refernced a declared module for which no file
|
||||||
// exists for the module (i.e. jQuery or angularjs).
|
// exists for the module (i.e. jQuery or angularjs).
|
||||||
|
19
modules/@angular/compiler-cli/src/version.ts
Normal file
19
modules/@angular/compiler-cli/src/version.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module
|
||||||
|
* @description
|
||||||
|
* Entry point for all public APIs of the common package.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Version} from '@angular/core';
|
||||||
|
/**
|
||||||
|
* @stable
|
||||||
|
*/
|
||||||
|
export const VERSION = new Version('0.0.0-PLACEHOLDER');
|
@ -14,14 +14,13 @@ import {Directory, Entry, MockAotContext, MockCompilerHost} from './mocks';
|
|||||||
|
|
||||||
describe('CompilerHost', () => {
|
describe('CompilerHost', () => {
|
||||||
let context: MockAotContext;
|
let context: MockAotContext;
|
||||||
let host: ts.CompilerHost;
|
|
||||||
let program: ts.Program;
|
let program: ts.Program;
|
||||||
let hostNestedGenDir: CompilerHost;
|
let hostNestedGenDir: CompilerHost;
|
||||||
let hostSiblingGenDir: CompilerHost;
|
let hostSiblingGenDir: CompilerHost;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
context = new MockAotContext('/tmp/src', clone(FILES));
|
context = new MockAotContext('/tmp/src', clone(FILES));
|
||||||
host = new MockCompilerHost(context);
|
const host = new MockCompilerHost(context);
|
||||||
program = ts.createProgram(
|
program = ts.createProgram(
|
||||||
['main.ts'], {
|
['main.ts'], {
|
||||||
module: ts.ModuleKind.CommonJS,
|
module: ts.ModuleKind.CommonJS,
|
||||||
@ -33,7 +32,7 @@ describe('CompilerHost', () => {
|
|||||||
throw new Error('Expected no errors');
|
throw new Error('Expected no errors');
|
||||||
}
|
}
|
||||||
hostNestedGenDir = new CompilerHost(
|
hostNestedGenDir = new CompilerHost(
|
||||||
program, host, {
|
program, {
|
||||||
genDir: '/tmp/project/src/gen/',
|
genDir: '/tmp/project/src/gen/',
|
||||||
basePath: '/tmp/project/src',
|
basePath: '/tmp/project/src',
|
||||||
skipMetadataEmit: false,
|
skipMetadataEmit: false,
|
||||||
@ -43,7 +42,7 @@ describe('CompilerHost', () => {
|
|||||||
},
|
},
|
||||||
context);
|
context);
|
||||||
hostSiblingGenDir = new CompilerHost(
|
hostSiblingGenDir = new CompilerHost(
|
||||||
program, host, {
|
program, {
|
||||||
genDir: '/tmp/project/gen',
|
genDir: '/tmp/project/gen',
|
||||||
basePath: '/tmp/project/src/',
|
basePath: '/tmp/project/src/',
|
||||||
skipMetadataEmit: false,
|
skipMetadataEmit: false,
|
||||||
@ -67,11 +66,14 @@ describe('CompilerHost', () => {
|
|||||||
'/tmp/project/src/my.other.ngfactory.ts', '/tmp/project/src/my.ngfactory.ts'))
|
'/tmp/project/src/my.other.ngfactory.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||||
.toEqual('./my.other.ngfactory');
|
.toEqual('./my.other.ngfactory');
|
||||||
expect(hostNestedGenDir.fileNameToModuleName(
|
expect(hostNestedGenDir.fileNameToModuleName(
|
||||||
'/tmp/project/src/my.other.css.ts', '/tmp/project/src/a/my.ngfactory.ts'))
|
'/tmp/project/src/my.other.css.ngstyle.ts', '/tmp/project/src/a/my.ngfactory.ts'))
|
||||||
.toEqual('../my.other.css');
|
.toEqual('../my.other.css.ngstyle');
|
||||||
expect(hostNestedGenDir.fileNameToModuleName(
|
expect(hostNestedGenDir.fileNameToModuleName(
|
||||||
'/tmp/project/src/a/my.other.css.shim.ts', '/tmp/project/src/my.ngfactory.ts'))
|
'/tmp/project/src/a/my.other.shim.ngstyle.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||||
.toEqual('./a/my.other.css.shim');
|
.toEqual('./a/my.other.shim.ngstyle');
|
||||||
|
expect(hostNestedGenDir.fileNameToModuleName(
|
||||||
|
'/tmp/project/src/my.other.sass.ngstyle.ts', '/tmp/project/src/a/my.ngfactory.ts'))
|
||||||
|
.toEqual('../my.other.sass.ngstyle');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should import application from factory', () => {
|
it('should import application from factory', () => {
|
||||||
@ -84,6 +86,12 @@ describe('CompilerHost', () => {
|
|||||||
expect(hostNestedGenDir.fileNameToModuleName(
|
expect(hostNestedGenDir.fileNameToModuleName(
|
||||||
'/tmp/project/src/a/my.other.ts', '/tmp/project/src/my.ngfactory.ts'))
|
'/tmp/project/src/a/my.other.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||||
.toEqual('../a/my.other');
|
.toEqual('../a/my.other');
|
||||||
|
expect(hostNestedGenDir.fileNameToModuleName(
|
||||||
|
'/tmp/project/src/a/my.other.css.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||||
|
.toEqual('../a/my.other.css');
|
||||||
|
expect(hostNestedGenDir.fileNameToModuleName(
|
||||||
|
'/tmp/project/src/a/my.other.css.shim.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||||
|
.toEqual('../a/my.other.css.shim');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -158,12 +166,18 @@ describe('CompilerHost', () => {
|
|||||||
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/missing.d.ts')).toBeUndefined();
|
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/missing.d.ts')).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add missing v2 metadata from v1 metadata and .d.ts files', () => {
|
it('should add missing v3 metadata from v1 metadata and .d.ts files', () => {
|
||||||
expect(hostNestedGenDir.getMetadataFor('metadata_versions/v1.d.ts')).toEqual([
|
expect(hostNestedGenDir.getMetadataFor('metadata_versions/v1.d.ts')).toEqual([
|
||||||
{__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}}, {
|
{__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}}, {
|
||||||
__symbolic: 'module',
|
__symbolic: 'module',
|
||||||
version: 2,
|
version: 3,
|
||||||
metadata: {foo: {__symbolic: 'class'}, bar: {__symbolic: 'class'}}
|
metadata: {
|
||||||
|
foo: {__symbolic: 'class'},
|
||||||
|
Bar: {__symbolic: 'class', members: {ngOnInit: [{__symbolic: 'method'}]}},
|
||||||
|
BarChild: {__symbolic: 'class', extends: {__symbolic: 'reference', name: 'Bar'}},
|
||||||
|
ReExport: {__symbolic: 'reference', module: './lib/utils2', name: 'ReExport'},
|
||||||
|
},
|
||||||
|
exports: [{from: './lib/utils2', export: ['Export']}],
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@ -198,7 +212,17 @@ const FILES: Entry = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'metadata_versions': {
|
'metadata_versions': {
|
||||||
'v1.d.ts': 'export declare class bar {}',
|
'v1.d.ts': `
|
||||||
|
import {ReExport} from './lib/utils2';
|
||||||
|
export {ReExport};
|
||||||
|
|
||||||
|
export {Export} from './lib/utils2';
|
||||||
|
|
||||||
|
export declare class Bar {
|
||||||
|
ngOnInit() {}
|
||||||
|
}
|
||||||
|
export declare class BarChild extends Bar {}
|
||||||
|
`,
|
||||||
'v1.metadata.json':
|
'v1.metadata.json':
|
||||||
`{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
`{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
||||||
}
|
}
|
||||||
|
177
modules/@angular/compiler-cli/test/main_spec.ts
Normal file
177
modules/@angular/compiler-cli/test/main_spec.ts
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
/**
|
||||||
|
* @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 {makeTempDir} from '@angular/tsc-wrapped/test/test_support';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
import {main} from '../src/main';
|
||||||
|
import {ReflectionCapabilities, reflector} from './private_import_core';
|
||||||
|
|
||||||
|
|
||||||
|
describe('compiler-cli', () => {
|
||||||
|
let basePath: string;
|
||||||
|
let write: (fileName: string, content: string) => void;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
basePath = makeTempDir();
|
||||||
|
write = (fileName: string, content: string) => {
|
||||||
|
fs.writeFileSync(path.join(basePath, fileName), content, {encoding: 'utf-8'});
|
||||||
|
};
|
||||||
|
write('tsconfig.json', `{
|
||||||
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"types": [],
|
||||||
|
"outDir": "built",
|
||||||
|
"declaration": true,
|
||||||
|
"module": "es2015"
|
||||||
|
},
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"annotateForClosureCompiler": true
|
||||||
|
},
|
||||||
|
"files": ["test.ts"]
|
||||||
|
}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Restore reflector since AoT compiler will update it with a new static reflector
|
||||||
|
afterEach(() => { reflector.updateCapabilities(new ReflectionCapabilities()); });
|
||||||
|
|
||||||
|
it('should compile without errors', (done) => {
|
||||||
|
write('test.ts', 'export const A = 1;');
|
||||||
|
|
||||||
|
const mockConsole = {error: (s: string) => {}};
|
||||||
|
|
||||||
|
spyOn(mockConsole, 'error');
|
||||||
|
|
||||||
|
main({p: basePath}, mockConsole.error)
|
||||||
|
.then((exitCode) => {
|
||||||
|
expect(mockConsole.error).not.toHaveBeenCalled();
|
||||||
|
expect(exitCode).toEqual(0);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(e => done.fail(e));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not print the stack trace if user input file does not exist', (done) => {
|
||||||
|
const mockConsole = {error: (s: string) => {}};
|
||||||
|
|
||||||
|
spyOn(mockConsole, 'error');
|
||||||
|
|
||||||
|
main({p: basePath}, mockConsole.error)
|
||||||
|
.then((exitCode) => {
|
||||||
|
expect(mockConsole.error)
|
||||||
|
.toHaveBeenCalledWith(
|
||||||
|
`Error File '` + path.join(basePath, 'test.ts') + `' not found.`);
|
||||||
|
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
|
||||||
|
expect(exitCode).toEqual(1);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(e => done.fail(e));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not print the stack trace if user input file is malformed', (done) => {
|
||||||
|
write('test.ts', 'foo;');
|
||||||
|
|
||||||
|
const mockConsole = {error: (s: string) => {}};
|
||||||
|
|
||||||
|
spyOn(mockConsole, 'error');
|
||||||
|
|
||||||
|
main({p: basePath}, mockConsole.error)
|
||||||
|
.then((exitCode) => {
|
||||||
|
expect(mockConsole.error)
|
||||||
|
.toHaveBeenCalledWith(
|
||||||
|
'Error at ' + path.join(basePath, 'test.ts') + `:1:1: Cannot find name 'foo'.`);
|
||||||
|
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
|
||||||
|
expect(exitCode).toEqual(1);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(e => done.fail(e));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not print the stack trace if cannot find the imported module', (done) => {
|
||||||
|
write('test.ts', `import {MyClass} from './not-exist-deps';`);
|
||||||
|
|
||||||
|
const mockConsole = {error: (s: string) => {}};
|
||||||
|
|
||||||
|
spyOn(mockConsole, 'error');
|
||||||
|
|
||||||
|
main({p: basePath}, mockConsole.error)
|
||||||
|
.then((exitCode) => {
|
||||||
|
expect(mockConsole.error)
|
||||||
|
.toHaveBeenCalledWith(
|
||||||
|
'Error at ' + path.join(basePath, 'test.ts') +
|
||||||
|
`:1:23: Cannot find module './not-exist-deps'.`);
|
||||||
|
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
|
||||||
|
expect(exitCode).toEqual(1);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(e => done.fail(e));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not print the stack trace if cannot import', (done) => {
|
||||||
|
write('empty-deps.ts', 'export const A = 1;');
|
||||||
|
write('test.ts', `import {MyClass} from './empty-deps';`);
|
||||||
|
|
||||||
|
const mockConsole = {error: (s: string) => {}};
|
||||||
|
|
||||||
|
spyOn(mockConsole, 'error');
|
||||||
|
|
||||||
|
main({p: basePath}, mockConsole.error)
|
||||||
|
.then((exitCode) => {
|
||||||
|
expect(mockConsole.error)
|
||||||
|
.toHaveBeenCalledWith(
|
||||||
|
'Error at ' + path.join(basePath, 'test.ts') + `:1:9: Module '"` +
|
||||||
|
path.join(basePath, 'empty-deps') + `"' has no exported member 'MyClass'.`);
|
||||||
|
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
|
||||||
|
expect(exitCode).toEqual(1);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(e => done.fail(e));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not print the stack trace if type mismatches', (done) => {
|
||||||
|
write('empty-deps.ts', 'export const A = "abc";');
|
||||||
|
write('test.ts', `
|
||||||
|
import {A} from './empty-deps';
|
||||||
|
A();
|
||||||
|
`);
|
||||||
|
|
||||||
|
const mockConsole = {error: (s: string) => {}};
|
||||||
|
|
||||||
|
spyOn(mockConsole, 'error');
|
||||||
|
|
||||||
|
main({p: basePath}, mockConsole.error)
|
||||||
|
.then((exitCode) => {
|
||||||
|
expect(mockConsole.error)
|
||||||
|
.toHaveBeenCalledWith(
|
||||||
|
'Error at ' + path.join(basePath, 'test.ts') +
|
||||||
|
':3:7: Cannot invoke an expression whose type lacks a call signature.');
|
||||||
|
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
|
||||||
|
expect(exitCode).toEqual(1);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(e => done.fail(e));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should print the stack trace on compiler internal errors', (done) => {
|
||||||
|
write('test.ts', 'export const A = 1;');
|
||||||
|
|
||||||
|
const mockConsole = {error: (s: string) => {}};
|
||||||
|
|
||||||
|
spyOn(mockConsole, 'error');
|
||||||
|
|
||||||
|
main({p: 'not-exist'}, mockConsole.error)
|
||||||
|
.then((exitCode) => {
|
||||||
|
expect(mockConsole.error).toHaveBeenCalled();
|
||||||
|
expect(mockConsole.error).toHaveBeenCalledWith('Compilation failed');
|
||||||
|
expect(exitCode).toEqual(1);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(e => done.fail(e));
|
||||||
|
});
|
||||||
|
});
|
13
modules/@angular/compiler-cli/test/private_import_core.ts
Normal file
13
modules/@angular/compiler-cli/test/private_import_core.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* @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 {__core_private__ as r} from '@angular/core';
|
||||||
|
|
||||||
|
export type ReflectionCapabilities = typeof r._ReflectionCapabilities;
|
||||||
|
export var ReflectionCapabilities: typeof r.ReflectionCapabilities = r.ReflectionCapabilities;
|
||||||
|
export var reflector: typeof r.reflector = r.reflector;
|
34
modules/@angular/compiler-cli/tsconfig-2015.json
Normal file
34
modules/@angular/compiler-cli/tsconfig-2015.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"declaration": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"module": "es2015",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"outDir": "../../../dist/esm/compiler-cli",
|
||||||
|
"paths": {
|
||||||
|
"@angular/core": ["../../../dist/packages-dist/core"],
|
||||||
|
"@angular/common": ["../../../dist/packages-dist/common"],
|
||||||
|
"@angular/compiler": ["../../../dist/packages-dist/compiler"],
|
||||||
|
"@angular/platform-server": ["../../../dist/packages-dist/platform-server"],
|
||||||
|
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
||||||
|
"@angular/tsc-wrapped": ["../../../dist/tools/@angular/tsc-wrapped"]
|
||||||
|
},
|
||||||
|
"rootDir": ".",
|
||||||
|
"sourceMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"exclude": ["integrationtest"],
|
||||||
|
"files": [
|
||||||
|
"index.ts",
|
||||||
|
"src/main.ts",
|
||||||
|
"src/extract_i18n.ts",
|
||||||
|
"../../../node_modules/@types/node/index.d.ts",
|
||||||
|
"../../../node_modules/@types/jasmine/index.d.ts",
|
||||||
|
"../../../node_modules/zone.js/dist/zone.js.d.ts"
|
||||||
|
]
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "commonjs",
|
|
||||||
"target": "es5",
|
|
||||||
"lib": ["es6", "dom"],
|
|
||||||
"noImplicitAny": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
|
"declaration": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"module": "commonjs",
|
||||||
|
"outDir": "../../../dist/packages-dist/compiler-cli",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@angular/core": ["../../../dist/packages-dist/core"],
|
"@angular/core": ["../../../dist/packages-dist/core"],
|
||||||
"@angular/common": ["../../../dist/packages-dist/common"],
|
"@angular/common": ["../../../dist/packages-dist/common"],
|
||||||
@ -14,11 +14,11 @@
|
|||||||
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
||||||
"@angular/tsc-wrapped": ["../../../dist/tools/@angular/tsc-wrapped"]
|
"@angular/tsc-wrapped": ["../../../dist/tools/@angular/tsc-wrapped"]
|
||||||
},
|
},
|
||||||
"experimentalDecorators": true,
|
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"sourceRoot": ".",
|
"sourceMap": true,
|
||||||
"outDir": "../../../dist/packages-dist/compiler-cli",
|
"inlineSources": true,
|
||||||
"declaration": true,
|
"target": "es5",
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
"skipLibCheck": true
|
"skipLibCheck": true
|
||||||
},
|
},
|
||||||
"exclude": ["integrationtest"],
|
"exclude": ["integrationtest"],
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
* </p>
|
* </p>
|
||||||
* </div>
|
* </div>
|
||||||
*/
|
*/
|
||||||
|
export {VERSION} from './src/version';
|
||||||
export * from './src/template_parser/template_ast';
|
export * from './src/template_parser/template_ast';
|
||||||
export {TEMPLATE_TRANSFORMS} from './src/template_parser/template_parser';
|
export {TEMPLATE_TRANSFORMS} from './src/template_parser/template_parser';
|
||||||
export {CompilerConfig, RenderTypes} from './src/config';
|
export {CompilerConfig, RenderTypes} from './src/config';
|
||||||
@ -31,6 +32,7 @@ export * from './src/aot/compiler_host';
|
|||||||
export * from './src/aot/static_reflector';
|
export * from './src/aot/static_reflector';
|
||||||
export * from './src/aot/static_reflection_capabilities';
|
export * from './src/aot/static_reflection_capabilities';
|
||||||
export * from './src/aot/static_symbol';
|
export * from './src/aot/static_symbol';
|
||||||
|
export * from './src/aot/summary_resolver';
|
||||||
export {JitCompiler} from './src/jit/compiler';
|
export {JitCompiler} from './src/jit/compiler';
|
||||||
export * from './src/jit/compiler_factory';
|
export * from './src/jit/compiler_factory';
|
||||||
export * from './src/url_resolver';
|
export * from './src/url_resolver';
|
||||||
@ -58,5 +60,4 @@ export * from './src/style_compiler';
|
|||||||
export * from './src/template_parser/template_parser';
|
export * from './src/template_parser/template_parser';
|
||||||
export {ViewCompiler} from './src/view_compiler/view_compiler';
|
export {ViewCompiler} from './src/view_compiler/view_compiler';
|
||||||
export {AnimationParser} from './src/animation/animation_parser';
|
export {AnimationParser} from './src/animation/animation_parser';
|
||||||
|
|
||||||
// This file only reexports content of the `src` folder. Keep it that way.
|
// This file only reexports content of the `src` folder. Keep it that way.
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
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';
|
||||||
|
|
||||||
@ -69,8 +69,8 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
stylesArr.push(o.literalMap(entries));
|
stylesArr.push(o.literalMap(entries));
|
||||||
});
|
});
|
||||||
|
|
||||||
return o.importExpr(resolveIdentifier(Identifiers.AnimationStyles)).instantiate([
|
return o.importExpr(createIdentifier(Identifiers.AnimationStyles)).instantiate([
|
||||||
o.importExpr(resolveIdentifier(Identifiers.collectAndResolveStyles)).callFn([
|
o.importExpr(createIdentifier(Identifiers.collectAndResolveStyles)).callFn([
|
||||||
_ANIMATION_COLLECTED_STYLES, o.literalArr(stylesArr)
|
_ANIMATION_COLLECTED_STYLES, o.literalArr(stylesArr)
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
@ -78,7 +78,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
|
|
||||||
visitAnimationKeyframe(ast: AnimationKeyframeAst, context: _AnimationBuilderContext):
|
visitAnimationKeyframe(ast: AnimationKeyframeAst, context: _AnimationBuilderContext):
|
||||||
o.Expression {
|
o.Expression {
|
||||||
return o.importExpr(resolveIdentifier(Identifiers.AnimationKeyframe)).instantiate([
|
return o.importExpr(createIdentifier(Identifiers.AnimationKeyframe)).instantiate([
|
||||||
o.literal(ast.offset), ast.styles.visit(this, context)
|
o.literal(ast.offset), ast.styles.visit(this, context)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
const startingStylesExpr = ast.startingStyles.visit(this, context);
|
const startingStylesExpr = ast.startingStyles.visit(this, context);
|
||||||
const keyframeExpressions = ast.keyframes.map(keyframe => keyframe.visit(this, context));
|
const keyframeExpressions = ast.keyframes.map(keyframe => keyframe.visit(this, context));
|
||||||
const keyframesExpr =
|
const keyframesExpr =
|
||||||
o.importExpr(resolveIdentifier(Identifiers.balanceAnimationKeyframes)).callFn([
|
o.importExpr(createIdentifier(Identifiers.balanceAnimationKeyframes)).callFn([
|
||||||
_ANIMATION_COLLECTED_STYLES, _ANIMATION_END_STATE_STYLES_VAR,
|
_ANIMATION_COLLECTED_STYLES, _ANIMATION_END_STATE_STYLES_VAR,
|
||||||
o.literalArr(keyframeExpressions)
|
o.literalArr(keyframeExpressions)
|
||||||
]);
|
]);
|
||||||
@ -127,14 +127,14 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
visitAnimationSequence(ast: AnimationSequenceAst, context: _AnimationBuilderContext):
|
visitAnimationSequence(ast: AnimationSequenceAst, context: _AnimationBuilderContext):
|
||||||
o.Expression {
|
o.Expression {
|
||||||
const playerExprs = ast.steps.map(step => step.visit(this, context));
|
const playerExprs = ast.steps.map(step => step.visit(this, context));
|
||||||
return o.importExpr(resolveIdentifier(Identifiers.AnimationSequencePlayer)).instantiate([
|
return o.importExpr(createIdentifier(Identifiers.AnimationSequencePlayer)).instantiate([
|
||||||
o.literalArr(playerExprs)
|
o.literalArr(playerExprs)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitAnimationGroup(ast: AnimationGroupAst, context: _AnimationBuilderContext): o.Expression {
|
visitAnimationGroup(ast: AnimationGroupAst, context: _AnimationBuilderContext): o.Expression {
|
||||||
const playerExprs = ast.steps.map(step => step.visit(this, context));
|
const playerExprs = ast.steps.map(step => step.visit(this, context));
|
||||||
return o.importExpr(resolveIdentifier(Identifiers.AnimationGroupPlayer)).instantiate([
|
return o.importExpr(createIdentifier(Identifiers.AnimationGroupPlayer)).instantiate([
|
||||||
o.literalArr(playerExprs)
|
o.literalArr(playerExprs)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -199,8 +199,9 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
.set(_ANIMATION_FACTORY_VIEW_CONTEXT.callMethod(
|
.set(_ANIMATION_FACTORY_VIEW_CONTEXT.callMethod(
|
||||||
'getAnimationPlayers',
|
'getAnimationPlayers',
|
||||||
[
|
[
|
||||||
_ANIMATION_FACTORY_ELEMENT_VAR, o.literal(this.animationName),
|
_ANIMATION_FACTORY_ELEMENT_VAR,
|
||||||
_ANIMATION_NEXT_STATE_VAR.equals(o.literal(EMPTY_STATE))
|
_ANIMATION_NEXT_STATE_VAR.equals(o.literal(EMPTY_STATE))
|
||||||
|
.conditional(o.NULL_EXPR, o.literal(this.animationName))
|
||||||
]))
|
]))
|
||||||
.toDeclStmt());
|
.toDeclStmt());
|
||||||
|
|
||||||
@ -228,7 +229,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
_ANIMATION_END_STATE_STYLES_VAR.equals(o.NULL_EXPR),
|
_ANIMATION_END_STATE_STYLES_VAR.equals(o.NULL_EXPR),
|
||||||
[_ANIMATION_END_STATE_STYLES_VAR.set(_ANIMATION_DEFAULT_STATE_VAR).toStmt()]));
|
[_ANIMATION_END_STATE_STYLES_VAR.set(_ANIMATION_DEFAULT_STATE_VAR).toStmt()]));
|
||||||
|
|
||||||
const RENDER_STYLES_FN = o.importExpr(resolveIdentifier(Identifiers.renderStyles));
|
const RENDER_STYLES_FN = o.importExpr(createIdentifier(Identifiers.renderStyles));
|
||||||
|
|
||||||
ast.stateTransitions.forEach(transAst => statements.push(transAst.visit(this, context)));
|
ast.stateTransitions.forEach(transAst => statements.push(transAst.visit(this, context)));
|
||||||
|
|
||||||
@ -237,7 +238,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
statements.push(new o.IfStmt(
|
statements.push(new o.IfStmt(
|
||||||
_ANIMATION_PLAYER_VAR.equals(o.NULL_EXPR),
|
_ANIMATION_PLAYER_VAR.equals(o.NULL_EXPR),
|
||||||
[_ANIMATION_PLAYER_VAR
|
[_ANIMATION_PLAYER_VAR
|
||||||
.set(o.importExpr(resolveIdentifier(Identifiers.NoOpAnimationPlayer)).instantiate([]))
|
.set(o.importExpr(createIdentifier(Identifiers.NoOpAnimationPlayer)).instantiate([]))
|
||||||
.toStmt()]));
|
.toStmt()]));
|
||||||
|
|
||||||
// once complete we want to apply the styles on the element
|
// once complete we want to apply the styles on the element
|
||||||
@ -255,8 +256,9 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
.callFn([
|
.callFn([
|
||||||
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
|
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
|
||||||
o.importExpr(
|
o.importExpr(
|
||||||
resolveIdentifier(Identifiers.prepareFinalAnimationStyles))
|
createIdentifier(Identifiers.prepareFinalAnimationStyles))
|
||||||
.callFn([
|
.callFn(
|
||||||
|
[
|
||||||
_ANIMATION_START_STATE_STYLES_VAR,
|
_ANIMATION_START_STATE_STYLES_VAR,
|
||||||
_ANIMATION_END_STATE_STYLES_VAR
|
_ANIMATION_END_STATE_STYLES_VAR
|
||||||
])
|
])
|
||||||
@ -265,7 +267,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
])])
|
])])
|
||||||
.toStmt());
|
.toStmt());
|
||||||
|
|
||||||
statements.push(o.importExpr(resolveIdentifier(Identifiers.AnimationSequencePlayer))
|
statements.push(o.importExpr(createIdentifier(Identifiers.AnimationSequencePlayer))
|
||||||
.instantiate([_PREVIOUS_ANIMATION_PLAYERS])
|
.instantiate([_PREVIOUS_ANIMATION_PLAYERS])
|
||||||
.callMethod('destroy', [])
|
.callMethod('destroy', [])
|
||||||
.toStmt());
|
.toStmt());
|
||||||
@ -276,7 +278,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
statements.push(RENDER_STYLES_FN
|
statements.push(RENDER_STYLES_FN
|
||||||
.callFn([
|
.callFn([
|
||||||
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
|
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
|
||||||
o.importExpr(resolveIdentifier(Identifiers.clearStyles))
|
o.importExpr(createIdentifier(Identifiers.clearStyles))
|
||||||
.callFn([_ANIMATION_START_STATE_STYLES_VAR])
|
.callFn([_ANIMATION_START_STATE_STYLES_VAR])
|
||||||
])
|
])
|
||||||
.toStmt());
|
.toStmt());
|
||||||
@ -291,7 +293,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
.toStmt());
|
.toStmt());
|
||||||
|
|
||||||
statements.push(new o.ReturnStatement(
|
statements.push(new o.ReturnStatement(
|
||||||
o.importExpr(resolveIdentifier(Identifiers.AnimationTransition)).instantiate([
|
o.importExpr(createIdentifier(Identifiers.AnimationTransition)).instantiate([
|
||||||
_ANIMATION_PLAYER_VAR, _ANIMATION_CURRENT_STATE_VAR, _ANIMATION_NEXT_STATE_VAR,
|
_ANIMATION_PLAYER_VAR, _ANIMATION_CURRENT_STATE_VAR, _ANIMATION_NEXT_STATE_VAR,
|
||||||
_ANIMATION_TIME_VAR
|
_ANIMATION_TIME_VAR
|
||||||
])));
|
])));
|
||||||
@ -300,12 +302,12 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
[
|
[
|
||||||
new o.FnParam(
|
new o.FnParam(
|
||||||
_ANIMATION_FACTORY_VIEW_VAR.name,
|
_ANIMATION_FACTORY_VIEW_VAR.name,
|
||||||
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
new o.FnParam(_ANIMATION_FACTORY_ELEMENT_VAR.name, o.DYNAMIC_TYPE),
|
new o.FnParam(_ANIMATION_FACTORY_ELEMENT_VAR.name, o.DYNAMIC_TYPE),
|
||||||
new o.FnParam(_ANIMATION_CURRENT_STATE_VAR.name, o.DYNAMIC_TYPE),
|
new o.FnParam(_ANIMATION_CURRENT_STATE_VAR.name, o.DYNAMIC_TYPE),
|
||||||
new o.FnParam(_ANIMATION_NEXT_STATE_VAR.name, o.DYNAMIC_TYPE)
|
new o.FnParam(_ANIMATION_NEXT_STATE_VAR.name, o.DYNAMIC_TYPE)
|
||||||
],
|
],
|
||||||
statements, o.importType(resolveIdentifier(Identifiers.AnimationTransition)));
|
statements, o.importType(createIdentifier(Identifiers.AnimationTransition)));
|
||||||
}
|
}
|
||||||
|
|
||||||
build(ast: AnimationAst): AnimationEntryCompileResult {
|
build(ast: AnimationAst): AnimationEntryCompileResult {
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata} 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';
|
||||||
import {ParseError} from '../parse_util';
|
import {ParseError} from '../parse_util';
|
||||||
@ -41,7 +41,7 @@ export class AnimationParser {
|
|||||||
|
|
||||||
parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] {
|
parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] {
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
const componentName = component.type.name;
|
const componentName = identifierName(component.type);
|
||||||
const animationTriggerNames = new Set<string>();
|
const animationTriggerNames = new Set<string>();
|
||||||
const asts = component.template.animations.map(entry => {
|
const asts = component.template.animations.map(entry => {
|
||||||
const result = this.parseEntry(entry);
|
const result = this.parseEntry(entry);
|
||||||
@ -174,6 +174,11 @@ function _normalizeStyleMetadata(
|
|||||||
entry: CompileAnimationStyleMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
entry: CompileAnimationStyleMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
||||||
schema: ElementSchemaRegistry, errors: AnimationParseError[],
|
schema: ElementSchemaRegistry, errors: AnimationParseError[],
|
||||||
permitStateReferences: boolean): {[key: string]: string | number}[] {
|
permitStateReferences: boolean): {[key: string]: string | number}[] {
|
||||||
|
const offset = entry.offset;
|
||||||
|
if (offset > 1 || offset < 0) {
|
||||||
|
errors.push(new AnimationParseError(`Offset values for animations must be between 0 and 1`));
|
||||||
|
}
|
||||||
|
|
||||||
const normalizedStyles: {[key: string]: string | number}[] = [];
|
const normalizedStyles: {[key: string]: string | number}[] = [];
|
||||||
entry.styles.forEach(styleEntry => {
|
entry.styles.forEach(styleEntry => {
|
||||||
if (typeof styleEntry === 'string') {
|
if (typeof styleEntry === 'string') {
|
||||||
|
@ -10,11 +10,11 @@ import {SchemaMetadata} from '@angular/core';
|
|||||||
|
|
||||||
import {AnimationCompiler} from '../animation/animation_compiler';
|
import {AnimationCompiler} from '../animation/animation_compiler';
|
||||||
import {AnimationParser} from '../animation/animation_parser';
|
import {AnimationParser} from '../animation/animation_parser';
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, createHostComponentMeta} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTypeSummary, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';
|
||||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||||
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||||
import {ListWrapper} from '../facade/collection';
|
import {ListWrapper} from '../facade/collection';
|
||||||
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
|
import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers';
|
||||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||||
import {NgModuleCompiler} from '../ng_module_compiler';
|
import {NgModuleCompiler} from '../ng_module_compiler';
|
||||||
import {OutputEmitter} from '../output/abstract_emitter';
|
import {OutputEmitter} from '../output/abstract_emitter';
|
||||||
@ -24,12 +24,11 @@ import {TemplateParser} from '../template_parser/template_parser';
|
|||||||
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
|
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
|
||||||
|
|
||||||
import {AotCompilerOptions} from './compiler_options';
|
import {AotCompilerOptions} from './compiler_options';
|
||||||
|
import {GeneratedFile} from './generated_file';
|
||||||
import {StaticReflector} from './static_reflector';
|
import {StaticReflector} from './static_reflector';
|
||||||
import {StaticSymbol} from './static_symbol';
|
import {StaticSymbol} from './static_symbol';
|
||||||
|
import {AotSummaryResolver} from './summary_resolver';
|
||||||
export class SourceModule {
|
import {filterFileByPatterns} from './utils';
|
||||||
constructor(public fileUrl: string, public moduleUrl: string, public source: string) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AotCompiler {
|
export class AotCompiler {
|
||||||
private _animationCompiler = new AnimationCompiler();
|
private _animationCompiler = new AnimationCompiler();
|
||||||
@ -39,31 +38,45 @@ export class AotCompiler {
|
|||||||
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
||||||
private _dirWrapperCompiler: DirectiveWrapperCompiler,
|
private _dirWrapperCompiler: DirectiveWrapperCompiler,
|
||||||
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
||||||
private _localeId: string, private _translationFormat: string,
|
private _summaryResolver: AotSummaryResolver, private _localeId: string,
|
||||||
private _animationParser: AnimationParser, private _staticReflector: StaticReflector,
|
private _translationFormat: string, private _animationParser: AnimationParser,
|
||||||
private _options: AotCompilerOptions) {}
|
private _staticReflector: StaticReflector, private _options: AotCompilerOptions) {}
|
||||||
|
|
||||||
clearCache() { this._metadataResolver.clearCache(); }
|
clearCache() { this._metadataResolver.clearCache(); }
|
||||||
|
|
||||||
compileAll(rootFiles: string[]): Promise<SourceModule[]> {
|
compileAll(rootFiles: string[]): Promise<GeneratedFile[]> {
|
||||||
const programSymbols = extractProgramSymbols(this._staticReflector, rootFiles, this._options);
|
const programSymbols = extractProgramSymbols(this._staticReflector, rootFiles, this._options);
|
||||||
const {ngModuleByPipeOrDirective, files, ngModules} =
|
const {ngModuleByPipeOrDirective, files, ngModules} =
|
||||||
analyzeAndValidateNgModules(programSymbols, this._options, this._metadataResolver);
|
analyzeAndValidateNgModules(programSymbols, this._options, this._metadataResolver);
|
||||||
return loadNgModuleDirectives(ngModules).then(() => {
|
return Promise
|
||||||
|
.all(ngModules.map(
|
||||||
|
ngModule => this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
||||||
|
ngModule.type.reference, false)))
|
||||||
|
.then(() => {
|
||||||
const sourceModules = files.map(
|
const sourceModules = files.map(
|
||||||
file => this._compileSrcFile(
|
file => this._compileSrcFile(
|
||||||
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.ngModules));
|
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes,
|
||||||
|
file.ngModules));
|
||||||
return ListWrapper.flatten(sourceModules);
|
return ListWrapper.flatten(sourceModules);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileSrcFile(
|
private _compileSrcFile(
|
||||||
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||||
directives: StaticSymbol[], ngModules: StaticSymbol[]): SourceModule[] {
|
directives: StaticSymbol[], pipes: StaticSymbol[],
|
||||||
|
ngModules: 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 outputSourceModules: SourceModule[] = [];
|
const generatedFiles: GeneratedFile[] = [];
|
||||||
|
|
||||||
|
// write summary files
|
||||||
|
const summaries: CompileTypeSummary[] = [
|
||||||
|
...ngModules.map(ref => this._metadataResolver.getNgModuleSummary(ref)),
|
||||||
|
...directives.map(ref => this._metadataResolver.getDirectiveSummary(ref)),
|
||||||
|
...pipes.map(ref => this._metadataResolver.getPipeSummary(ref))
|
||||||
|
];
|
||||||
|
generatedFiles.push(this._summaryResolver.serializeSummaries(srcFileUrl, summaries));
|
||||||
|
|
||||||
// compile all ng modules
|
// compile all ng modules
|
||||||
exportedVars.push(
|
exportedVars.push(
|
||||||
@ -82,7 +95,7 @@ export class AotCompiler {
|
|||||||
const ngModule = ngModuleByPipeOrDirective.get(dirType);
|
const ngModule = ngModuleByPipeOrDirective.get(dirType);
|
||||||
if (!ngModule) {
|
if (!ngModule) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Internal Error: cannot determine the module for component ${compMeta.type.name}!`);
|
`Internal Error: cannot determine the module for component ${identifierName(compMeta.type)}!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
_assertComponent(compMeta);
|
_assertComponent(compMeta);
|
||||||
@ -90,7 +103,7 @@ export class AotCompiler {
|
|||||||
// compile styles
|
// compile styles
|
||||||
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
|
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
|
||||||
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
|
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
|
||||||
outputSourceModules.push(this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
|
generatedFiles.push(this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
|
||||||
});
|
});
|
||||||
|
|
||||||
// compile components
|
// compile components
|
||||||
@ -103,9 +116,9 @@ 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, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
|
||||||
outputSourceModules.unshift(srcModule);
|
generatedFiles.unshift(srcModule);
|
||||||
}
|
}
|
||||||
return outputSourceModules;
|
return generatedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
|
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
|
||||||
@ -113,24 +126,24 @@ export class AotCompiler {
|
|||||||
const providers: CompileProviderMetadata[] = [];
|
const providers: CompileProviderMetadata[] = [];
|
||||||
|
|
||||||
if (this._localeId) {
|
if (this._localeId) {
|
||||||
providers.push(new CompileProviderMetadata({
|
providers.push({
|
||||||
token: resolveIdentifierToken(Identifiers.LOCALE_ID),
|
token: createIdentifierToken(Identifiers.LOCALE_ID),
|
||||||
useValue: this._localeId,
|
useValue: this._localeId,
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._translationFormat) {
|
if (this._translationFormat) {
|
||||||
providers.push(new CompileProviderMetadata({
|
providers.push({
|
||||||
token: resolveIdentifierToken(Identifiers.TRANSLATIONS_FORMAT),
|
token: createIdentifierToken(Identifiers.TRANSLATIONS_FORMAT),
|
||||||
useValue: this._translationFormat
|
useValue: this._translationFormat
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
|
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
|
||||||
|
|
||||||
appCompileResult.dependencies.forEach((dep) => {
|
appCompileResult.dependencies.forEach((dep) => {
|
||||||
dep.placeholder.name = _componentFactoryName(dep.comp);
|
dep.placeholder.reference = this._staticReflector.getStaticSymbol(
|
||||||
dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp.moduleUrl);
|
_ngfactoryModuleUrl(identifierModuleUrl(dep.comp)), _componentFactoryName(dep.comp));
|
||||||
});
|
});
|
||||||
|
|
||||||
targetStatements.push(...appCompileResult.statements);
|
targetStatements.push(...appCompileResult.statements);
|
||||||
@ -149,14 +162,17 @@ 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 hostMeta = createHostComponentMeta(compMeta);
|
const hostMeta = createHostComponentMeta(
|
||||||
|
this._staticReflector.getStaticSymbol(
|
||||||
|
identifierModuleUrl(compMeta.type), `${identifierName(compMeta.type)}_Host`),
|
||||||
|
compMeta);
|
||||||
const hostViewFactoryVar = this._compileComponent(
|
const hostViewFactoryVar = this._compileComponent(
|
||||||
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements);
|
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements);
|
||||||
const compFactoryVar = _componentFactoryName(compMeta.type);
|
const compFactoryVar = _componentFactoryName(compMeta.type);
|
||||||
targetStatements.push(
|
targetStatements.push(
|
||||||
o.variable(compFactoryVar)
|
o.variable(compFactoryVar)
|
||||||
.set(o.importExpr(resolveIdentifier(Identifiers.ComponentFactory), [o.importType(
|
.set(o.importExpr(
|
||||||
compMeta.type)])
|
createIdentifier(Identifiers.ComponentFactory), [o.importType(compMeta.type)])
|
||||||
.instantiate(
|
.instantiate(
|
||||||
[
|
[
|
||||||
o.literal(compMeta.selector),
|
o.literal(compMeta.selector),
|
||||||
@ -164,7 +180,7 @@ export class AotCompiler {
|
|||||||
o.importExpr(compMeta.type),
|
o.importExpr(compMeta.type),
|
||||||
],
|
],
|
||||||
o.importType(
|
o.importType(
|
||||||
resolveIdentifier(Identifiers.ComponentFactory),
|
createIdentifier(Identifiers.ComponentFactory),
|
||||||
[o.importType(compMeta.type)], [o.TypeModifier.Const])))
|
[o.importType(compMeta.type)], [o.TypeModifier.Const])))
|
||||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||||
return compFactoryVar;
|
return compFactoryVar;
|
||||||
@ -182,23 +198,24 @@ export class AotCompiler {
|
|||||||
|
|
||||||
const parsedTemplate = this._templateParser.parse(
|
const parsedTemplate = this._templateParser.parse(
|
||||||
compMeta, compMeta.template.template, directives, pipes, ngModule.schemas,
|
compMeta, compMeta.template.template, directives, pipes, ngModule.schemas,
|
||||||
compMeta.type.name);
|
identifierName(compMeta.type));
|
||||||
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
|
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
|
||||||
const compiledAnimations =
|
const compiledAnimations =
|
||||||
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
|
this._animationCompiler.compile(identifierName(compMeta.type), parsedAnimations);
|
||||||
const viewResult = this._viewCompiler.compileComponent(
|
const viewResult = this._viewCompiler.compileComponent(
|
||||||
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
|
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
|
||||||
if (componentStyles) {
|
if (componentStyles) {
|
||||||
targetStatements.push(..._resolveStyleStatements(componentStyles, fileSuffix));
|
targetStatements.push(
|
||||||
|
..._resolveStyleStatements(this._staticReflector, componentStyles, fileSuffix));
|
||||||
}
|
}
|
||||||
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
|
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
|
||||||
targetStatements.push(..._resolveViewStatements(viewResult));
|
targetStatements.push(..._resolveViewStatements(this._staticReflector, viewResult));
|
||||||
return viewResult.viewClassVar;
|
return viewResult.viewClassVar;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _codgenStyles(
|
private _codgenStyles(
|
||||||
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): SourceModule {
|
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): GeneratedFile {
|
||||||
_resolveStyleStatements(stylesCompileResult, fileSuffix);
|
_resolveStyleStatements(this._staticReflector, stylesCompileResult, fileSuffix);
|
||||||
return this._codegenSourceModule(
|
return this._codegenSourceModule(
|
||||||
fileUrl, _stylesModuleUrl(
|
fileUrl, _stylesModuleUrl(
|
||||||
stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix),
|
stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix),
|
||||||
@ -206,26 +223,29 @@ export class AotCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _codegenSourceModule(
|
private _codegenSourceModule(
|
||||||
fileUrl: string, moduleUrl: string, statements: o.Statement[],
|
srcFileUrl: string, genFileUrl: string, statements: o.Statement[],
|
||||||
exportedVars: string[]): SourceModule {
|
exportedVars: string[]): GeneratedFile {
|
||||||
return new SourceModule(
|
return new GeneratedFile(
|
||||||
fileUrl, moduleUrl,
|
srcFileUrl, genFileUrl,
|
||||||
this._outputEmitter.emitStatements(moduleUrl, statements, exportedVars));
|
this._outputEmitter.emitStatements(genFileUrl, statements, exportedVars));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[] {
|
function _resolveViewStatements(
|
||||||
|
reflector: StaticReflector, compileResult: ViewCompileResult): o.Statement[] {
|
||||||
compileResult.dependencies.forEach((dep) => {
|
compileResult.dependencies.forEach((dep) => {
|
||||||
if (dep instanceof ViewClassDependency) {
|
if (dep instanceof ViewClassDependency) {
|
||||||
const vfd = <ViewClassDependency>dep;
|
const vfd = <ViewClassDependency>dep;
|
||||||
vfd.placeholder.moduleUrl = _ngfactoryModuleUrl(vfd.comp.moduleUrl);
|
vfd.placeholder.reference =
|
||||||
|
reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(vfd.comp)), dep.name);
|
||||||
} else if (dep instanceof ComponentFactoryDependency) {
|
} else if (dep instanceof ComponentFactoryDependency) {
|
||||||
const cfd = <ComponentFactoryDependency>dep;
|
const cfd = <ComponentFactoryDependency>dep;
|
||||||
cfd.placeholder.name = _componentFactoryName(cfd.comp);
|
cfd.placeholder.reference = reflector.getStaticSymbol(
|
||||||
cfd.placeholder.moduleUrl = _ngfactoryModuleUrl(cfd.comp.moduleUrl);
|
_ngfactoryModuleUrl(identifierModuleUrl(cfd.comp)), _componentFactoryName(cfd.comp));
|
||||||
} else if (dep instanceof DirectiveWrapperDependency) {
|
} else if (dep instanceof DirectiveWrapperDependency) {
|
||||||
const dwd = <DirectiveWrapperDependency>dep;
|
const dwd = <DirectiveWrapperDependency>dep;
|
||||||
dwd.placeholder.moduleUrl = _ngfactoryModuleUrl(dwd.dir.moduleUrl);
|
dwd.placeholder.reference =
|
||||||
|
reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(dwd.dir)), dwd.name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return compileResult.statements;
|
return compileResult.statements;
|
||||||
@ -233,9 +253,11 @@ function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[]
|
|||||||
|
|
||||||
|
|
||||||
function _resolveStyleStatements(
|
function _resolveStyleStatements(
|
||||||
compileResult: CompiledStylesheet, fileSuffix: string): o.Statement[] {
|
reflector: StaticReflector, compileResult: CompiledStylesheet,
|
||||||
|
fileSuffix: string): o.Statement[] {
|
||||||
compileResult.dependencies.forEach((dep) => {
|
compileResult.dependencies.forEach((dep) => {
|
||||||
dep.valuePlaceholder.moduleUrl = _stylesModuleUrl(dep.moduleUrl, dep.isShimmed, fileSuffix);
|
dep.valuePlaceholder.reference = reflector.getStaticSymbol(
|
||||||
|
_stylesModuleUrl(dep.moduleUrl, dep.isShimmed, fileSuffix), dep.name);
|
||||||
});
|
});
|
||||||
return compileResult.statements;
|
return compileResult.statements;
|
||||||
}
|
}
|
||||||
@ -246,16 +268,17 @@ function _ngfactoryModuleUrl(dirUrl: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _componentFactoryName(comp: CompileIdentifierMetadata): string {
|
function _componentFactoryName(comp: CompileIdentifierMetadata): string {
|
||||||
return `${comp.name}NgFactory`;
|
return `${identifierName(comp)}NgFactory`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string {
|
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string {
|
||||||
return shim ? `${stylesheetUrl}.shim${suffix}` : `${stylesheetUrl}${suffix}`;
|
return `${stylesheetUrl}${shim ? '.shim' : ''}.ngstyle${suffix}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _assertComponent(meta: CompileDirectiveMetadata) {
|
function _assertComponent(meta: CompileDirectiveMetadata) {
|
||||||
if (!meta.isComponent) {
|
if (!meta.isComponent) {
|
||||||
throw new Error(`Could not compile '${meta.type.name}' because it is not a component.`);
|
throw new Error(
|
||||||
|
`Could not compile '${identifierName(meta.type)}' because it is not a component.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,7 +299,12 @@ function _splitTypescriptSuffix(path: string): string[] {
|
|||||||
export interface NgAnalyzedModules {
|
export interface NgAnalyzedModules {
|
||||||
ngModules: CompileNgModuleMetadata[];
|
ngModules: CompileNgModuleMetadata[];
|
||||||
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>;
|
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>;
|
||||||
files: Array<{srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}>;
|
files: Array<{
|
||||||
|
srcUrl: string,
|
||||||
|
directives: StaticSymbol[],
|
||||||
|
pipes: StaticSymbol[],
|
||||||
|
ngModules: StaticSymbol[]
|
||||||
|
}>;
|
||||||
symbolsMissingModule?: StaticSymbol[];
|
symbolsMissingModule?: StaticSymbol[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,14 +331,6 @@ export function analyzeAndValidateNgModules(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the directives in the given modules have been loaded
|
|
||||||
export function loadNgModuleDirectives(ngModules: CompileNgModuleMetadata[]) {
|
|
||||||
return Promise
|
|
||||||
.all(ListWrapper.flatten(ngModules.map(
|
|
||||||
(ngModule) => ngModule.transitiveModule.directiveLoaders.map(loader => loader()))))
|
|
||||||
.then(() => {});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _analyzeNgModules(
|
function _analyzeNgModules(
|
||||||
ngModuleMetas: CompileNgModuleMetadata[],
|
ngModuleMetas: CompileNgModuleMetadata[],
|
||||||
symbolsMissingModule: StaticSymbol[]): NgAnalyzedModules {
|
symbolsMissingModule: StaticSymbol[]): NgAnalyzedModules {
|
||||||
@ -319,11 +339,13 @@ function _analyzeNgModules(
|
|||||||
const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
|
const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
|
||||||
const ngModulesByFile = new Map<string, StaticSymbol[]>();
|
const ngModulesByFile = new Map<string, StaticSymbol[]>();
|
||||||
const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
|
const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
|
||||||
|
const ngPipesByFile = new Map<string, StaticSymbol[]>();
|
||||||
const filePaths = new Set<string>();
|
const filePaths = new Set<string>();
|
||||||
|
|
||||||
// Looping over all modules to construct:
|
// Looping over all modules to construct:
|
||||||
// - a map from file to modules `ngModulesByFile`,
|
// - a map from file to modules `ngModulesByFile`,
|
||||||
// - a map from file to directives `ngDirectivesByFile`,
|
// - a map from file to directives `ngDirectivesByFile`,
|
||||||
|
// - a map from file to pipes `ngPipesByFile`,
|
||||||
// - a map from directive/pipe to module `ngModuleByPipeOrDirective`.
|
// - a map from directive/pipe to module `ngModuleByPipeOrDirective`.
|
||||||
ngModuleMetas.forEach((ngModuleMeta) => {
|
ngModuleMetas.forEach((ngModuleMeta) => {
|
||||||
const srcFileUrl = ngModuleMeta.type.reference.filePath;
|
const srcFileUrl = ngModuleMeta.type.reference.filePath;
|
||||||
@ -341,16 +363,23 @@ function _analyzeNgModules(
|
|||||||
ngModuleMeta.declaredPipes.forEach((pipeIdentifier) => {
|
ngModuleMeta.declaredPipes.forEach((pipeIdentifier) => {
|
||||||
const fileUrl = pipeIdentifier.reference.filePath;
|
const fileUrl = pipeIdentifier.reference.filePath;
|
||||||
filePaths.add(fileUrl);
|
filePaths.add(fileUrl);
|
||||||
|
ngPipesByFile.set(
|
||||||
|
fileUrl, (ngPipesByFile.get(fileUrl) || []).concat(pipeIdentifier.reference));
|
||||||
ngModuleByPipeOrDirective.set(pipeIdentifier.reference, ngModuleMeta);
|
ngModuleByPipeOrDirective.set(pipeIdentifier.reference, ngModuleMeta);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const files: {srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}[] = [];
|
const files:
|
||||||
|
{srcUrl: string,
|
||||||
|
directives: StaticSymbol[],
|
||||||
|
pipes: StaticSymbol[],
|
||||||
|
ngModules: StaticSymbol[]}[] = [];
|
||||||
|
|
||||||
filePaths.forEach((srcUrl) => {
|
filePaths.forEach((srcUrl) => {
|
||||||
const directives = ngDirectivesByFile.get(srcUrl) || [];
|
const directives = ngDirectivesByFile.get(srcUrl) || [];
|
||||||
|
const pipes = ngPipesByFile.get(srcUrl) || [];
|
||||||
const ngModules = ngModulesByFile.get(srcUrl) || [];
|
const ngModules = ngModulesByFile.get(srcUrl) || [];
|
||||||
files.push({srcUrl, directives, ngModules});
|
files.push({srcUrl, directives, pipes, ngModules});
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -366,10 +395,10 @@ export function extractProgramSymbols(
|
|||||||
staticReflector: StaticReflector, files: string[],
|
staticReflector: StaticReflector, files: string[],
|
||||||
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}): StaticSymbol[] {
|
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}): StaticSymbol[] {
|
||||||
const staticSymbols: StaticSymbol[] = [];
|
const staticSymbols: StaticSymbol[] = [];
|
||||||
files.filter(fileName => _filterFileByPatterns(fileName, options)).forEach(sourceFile => {
|
files.filter(fileName => filterFileByPatterns(fileName, options)).forEach(sourceFile => {
|
||||||
const moduleMetadata = staticReflector.getModuleMetadata(sourceFile);
|
const moduleMetadata = staticReflector.getModuleMetadata(sourceFile);
|
||||||
if (!moduleMetadata) {
|
if (!moduleMetadata) {
|
||||||
console.log(`WARNING: no metadata found for ${sourceFile}`);
|
console.error(`WARNING: no metadata found for ${sourceFile}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,16 +433,16 @@ function _createNgModules(
|
|||||||
const ngModulePipesAndDirective = new Set<StaticSymbol>();
|
const ngModulePipesAndDirective = new Set<StaticSymbol>();
|
||||||
|
|
||||||
const addNgModule = (staticSymbol: any) => {
|
const addNgModule = (staticSymbol: any) => {
|
||||||
if (ngModules.has(staticSymbol) || !_filterFileByPatterns(staticSymbol.filePath, options)) {
|
if (ngModules.has(staticSymbol) || !filterFileByPatterns(staticSymbol.filePath, options)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const ngModule = metadataResolver.getUnloadedNgModuleMetadata(staticSymbol, false, false);
|
const ngModule = metadataResolver.getNgModuleMetadata(staticSymbol, false);
|
||||||
if (ngModule) {
|
if (ngModule) {
|
||||||
ngModules.set(ngModule.type.reference, ngModule);
|
ngModules.set(ngModule.type.reference, ngModule);
|
||||||
ngModule.declaredDirectives.forEach((dir) => ngModulePipesAndDirective.add(dir.reference));
|
ngModule.declaredDirectives.forEach((dir) => ngModulePipesAndDirective.add(dir.reference));
|
||||||
ngModule.declaredPipes.forEach((pipe) => ngModulePipesAndDirective.add(pipe.reference));
|
ngModule.declaredPipes.forEach((pipe) => ngModulePipesAndDirective.add(pipe.reference));
|
||||||
// For every input module add the list of transitively included modules
|
// For every input module add the list of transitively included modules
|
||||||
ngModule.transitiveModule.modules.forEach(modMeta => addNgModule(modMeta.type.reference));
|
ngModule.transitiveModule.modules.forEach(modMeta => addNgModule(modMeta.reference));
|
||||||
}
|
}
|
||||||
return !!ngModule;
|
return !!ngModule;
|
||||||
};
|
};
|
||||||
@ -430,15 +459,3 @@ function _createNgModules(
|
|||||||
|
|
||||||
return {ngModules: Array.from(ngModules.values()), symbolsMissingModule};
|
return {ngModules: Array.from(ngModules.values()), symbolsMissingModule};
|
||||||
}
|
}
|
||||||
|
|
||||||
function _filterFileByPatterns(
|
|
||||||
fileName: string, options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}) {
|
|
||||||
let match = true;
|
|
||||||
if (options.includeFilePattern) {
|
|
||||||
match = match && !!options.includeFilePattern.exec(fileName);
|
|
||||||
}
|
|
||||||
if (options.excludeFilePattern) {
|
|
||||||
match = match && !options.excludeFilePattern.exec(fileName);
|
|
||||||
}
|
|
||||||
return match;
|
|
||||||
}
|
|
@ -34,8 +34,7 @@ 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 {AotSummaryResolver} from './summary_resolver';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new AotCompiler based on options and a host.
|
* Creates a new AotCompiler based on options and a host.
|
||||||
@ -61,15 +60,17 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
|
|||||||
const console = new Console();
|
const console = new Console();
|
||||||
const tmplParser =
|
const tmplParser =
|
||||||
new TemplateParser(expressionParser, elementSchemaRegistry, htmlParser, console, []);
|
new TemplateParser(expressionParser, elementSchemaRegistry, htmlParser, console, []);
|
||||||
|
const summaryResolver = new AotSummaryResolver(compilerHost, staticReflector, options);
|
||||||
const resolver = new CompileMetadataResolver(
|
const resolver = new CompileMetadataResolver(
|
||||||
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
||||||
new PipeResolver(staticReflector), elementSchemaRegistry, normalizer, staticReflector);
|
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
||||||
|
staticReflector);
|
||||||
// TODO(vicb): do not pass options.i18nFormat here
|
// TODO(vicb): do not pass options.i18nFormat here
|
||||||
const compiler = new AotCompiler(
|
const compiler = new AotCompiler(
|
||||||
resolver, tmplParser, new StyleCompiler(urlResolver),
|
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), options.locale,
|
new NgModuleCompiler(), new TypeScriptEmitter(compilerHost), summaryResolver, options.locale,
|
||||||
options.i18nFormat, new AnimationParser(elementSchemaRegistry), staticReflector, options);
|
options.i18nFormat, new AnimationParser(elementSchemaRegistry), staticReflector, options);
|
||||||
return {compiler, reflector: staticReflector};
|
return {compiler, reflector: staticReflector};
|
||||||
}
|
}
|
||||||
|
@ -10,13 +10,14 @@ import {ImportResolver} from '../output/path_util';
|
|||||||
|
|
||||||
import {StaticReflectorHost} from './static_reflector';
|
import {StaticReflectorHost} from './static_reflector';
|
||||||
import {StaticSymbol} from './static_symbol';
|
import {StaticSymbol} from './static_symbol';
|
||||||
|
import {AotSummaryResolverHost} from './summary_resolver';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 StaticReflectorHost, ImportResolver {
|
export interface AotCompilerHost extends StaticReflectorHost, ImportResolver,
|
||||||
|
AotSummaryResolverHost {
|
||||||
/**
|
/**
|
||||||
* Loads a resource (e.g. html / css)
|
* Loads a resource (e.g. html / css)
|
||||||
*/
|
*/
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
export class GeneratedFile {
|
||||||
* @module
|
constructor(public srcFileUrl: string, public genFileUrl: string, public source: string) {}
|
||||||
* @description
|
}
|
||||||
* This module provides a set of common Pipes.
|
|
||||||
*/
|
|
@ -10,7 +10,7 @@ import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, Ho
|
|||||||
import {ReflectorReader} from '../private_import_core';
|
import {ReflectorReader} from '../private_import_core';
|
||||||
import {StaticSymbol} from './static_symbol';
|
import {StaticSymbol} from './static_symbol';
|
||||||
|
|
||||||
const SUPPORTED_SCHEMA_VERSION = 2;
|
const SUPPORTED_SCHEMA_VERSION = 3;
|
||||||
const ANGULAR_IMPORT_LOCATIONS = {
|
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',
|
||||||
@ -20,6 +20,8 @@ const ANGULAR_IMPORT_LOCATIONS = {
|
|||||||
provider: '@angular/core/src/di/provider'
|
provider: '@angular/core/src/di/provider'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const HIDDEN_KEY = /^\$.*\$$/;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The host of the StaticReflector disconnects the implementation from TypeScript / other language
|
* The host of the StaticReflector disconnects the implementation from TypeScript / other language
|
||||||
* services and from underlying file systems.
|
* services and from underlying file systems.
|
||||||
@ -70,16 +72,25 @@ export class StaticSymbolCache {
|
|||||||
export class StaticReflector implements ReflectorReader {
|
export class StaticReflector implements ReflectorReader {
|
||||||
private declarationCache = new Map<string, StaticSymbol>();
|
private declarationCache = new Map<string, StaticSymbol>();
|
||||||
private annotationCache = new Map<StaticSymbol, any[]>();
|
private annotationCache = new Map<StaticSymbol, any[]>();
|
||||||
private propertyCache = new Map<StaticSymbol, {[key: string]: any}>();
|
private propertyCache = new Map<StaticSymbol, {[key: string]: any[]}>();
|
||||||
private parameterCache = new Map<StaticSymbol, any[]>();
|
private parameterCache = new Map<StaticSymbol, any[]>();
|
||||||
|
private methodCache = new Map<StaticSymbol, {[key: string]: boolean}>();
|
||||||
private metadataCache = new Map<string, {[key: string]: any}>();
|
private metadataCache = new Map<string, {[key: string]: any}>();
|
||||||
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
|
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
|
||||||
private opaqueToken: StaticSymbol;
|
private opaqueToken: StaticSymbol;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private host: StaticReflectorHost,
|
private host: StaticReflectorHost,
|
||||||
private staticSymbolCache: StaticSymbolCache = new StaticSymbolCache()) {
|
private staticSymbolCache: StaticSymbolCache = new StaticSymbolCache(),
|
||||||
|
knownMetadataClasses: {name: string, filePath: string, ctor: any}[] = [],
|
||||||
|
knownMetadataFunctions: {name: string, filePath: string, fn: any}[] = [],
|
||||||
|
private errorRecorder?: (error: any, fileName: string) => void) {
|
||||||
this.initializeConversionMap();
|
this.initializeConversionMap();
|
||||||
|
knownMetadataClasses.forEach(
|
||||||
|
(kc) => this._registerDecoratorOrConstructor(
|
||||||
|
this.getStaticSymbol(kc.filePath, kc.name), kc.ctor));
|
||||||
|
knownMetadataFunctions.forEach(
|
||||||
|
(kf) => this._registerFunction(this.getStaticSymbol(kf.filePath, kf.name), kf.fn));
|
||||||
}
|
}
|
||||||
|
|
||||||
importUri(typeOrFunc: StaticSymbol): string {
|
importUri(typeOrFunc: StaticSymbol): string {
|
||||||
@ -99,29 +110,45 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
public annotations(type: StaticSymbol): any[] {
|
public annotations(type: StaticSymbol): any[] {
|
||||||
let annotations = this.annotationCache.get(type);
|
let annotations = this.annotationCache.get(type);
|
||||||
if (!annotations) {
|
if (!annotations) {
|
||||||
const classMetadata = this.getTypeMetadata(type);
|
|
||||||
if (classMetadata['decorators']) {
|
|
||||||
annotations = this.simplify(type, classMetadata['decorators']);
|
|
||||||
} else {
|
|
||||||
annotations = [];
|
annotations = [];
|
||||||
|
const classMetadata = this.getTypeMetadata(type);
|
||||||
|
if (classMetadata['extends']) {
|
||||||
|
const parentAnnotations = this.annotations(this.simplify(type, classMetadata['extends']));
|
||||||
|
annotations.push(...parentAnnotations);
|
||||||
|
}
|
||||||
|
if (classMetadata['decorators']) {
|
||||||
|
const ownAnnotations: any[] = this.simplify(type, classMetadata['decorators']);
|
||||||
|
annotations.push(...ownAnnotations);
|
||||||
}
|
}
|
||||||
this.annotationCache.set(type, annotations.filter(ann => !!ann));
|
this.annotationCache.set(type, annotations.filter(ann => !!ann));
|
||||||
}
|
}
|
||||||
return annotations;
|
return annotations;
|
||||||
}
|
}
|
||||||
|
|
||||||
public propMetadata(type: StaticSymbol): {[key: string]: any} {
|
public propMetadata(type: StaticSymbol): {[key: string]: any[]} {
|
||||||
let propMetadata = this.propertyCache.get(type);
|
let propMetadata = this.propertyCache.get(type);
|
||||||
if (!propMetadata) {
|
if (!propMetadata) {
|
||||||
const classMetadata = this.getTypeMetadata(type);
|
const classMetadata = this.getTypeMetadata(type) || {};
|
||||||
const members = classMetadata ? classMetadata['members'] : {};
|
propMetadata = {};
|
||||||
propMetadata = mapStringMap(members, (propData, propName) => {
|
if (classMetadata['extends']) {
|
||||||
|
const parentPropMetadata = this.propMetadata(this.simplify(type, classMetadata['extends']));
|
||||||
|
Object.keys(parentPropMetadata).forEach((parentProp) => {
|
||||||
|
propMetadata[parentProp] = parentPropMetadata[parentProp];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const members = classMetadata['members'] || {};
|
||||||
|
Object.keys(members).forEach((propName) => {
|
||||||
|
const propData = members[propName];
|
||||||
const prop = (<any[]>propData)
|
const prop = (<any[]>propData)
|
||||||
.find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method');
|
.find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method');
|
||||||
|
const decorators: any[] = [];
|
||||||
|
if (propMetadata[propName]) {
|
||||||
|
decorators.push(...propMetadata[propName]);
|
||||||
|
}
|
||||||
|
propMetadata[propName] = decorators;
|
||||||
if (prop && prop['decorators']) {
|
if (prop && prop['decorators']) {
|
||||||
return this.simplify(type, prop['decorators']);
|
decorators.push(...this.simplify(type, prop['decorators']));
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.propertyCache.set(type, propMetadata);
|
this.propertyCache.set(type, propMetadata);
|
||||||
@ -131,7 +158,10 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
|
|
||||||
public parameters(type: StaticSymbol): any[] {
|
public parameters(type: StaticSymbol): any[] {
|
||||||
if (!(type instanceof StaticSymbol)) {
|
if (!(type instanceof StaticSymbol)) {
|
||||||
throw new Error(`parameters received ${JSON.stringify(type)} which is not a StaticSymbol`);
|
this.reportError(
|
||||||
|
new Error(`parameters received ${JSON.stringify(type)} which is not a StaticSymbol`),
|
||||||
|
type);
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
let parameters = this.parameterCache.get(type);
|
let parameters = this.parameterCache.get(type);
|
||||||
@ -155,6 +185,8 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
}
|
}
|
||||||
parameters.push(nestedResult);
|
parameters.push(nestedResult);
|
||||||
});
|
});
|
||||||
|
} else if (classMetadata['extends']) {
|
||||||
|
parameters = this.parameters(this.simplify(type, classMetadata['extends']));
|
||||||
}
|
}
|
||||||
if (!parameters) {
|
if (!parameters) {
|
||||||
parameters = [];
|
parameters = [];
|
||||||
@ -163,28 +195,54 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
}
|
}
|
||||||
return parameters;
|
return parameters;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`Failed on type ${JSON.stringify(type)} with error ${e}`);
|
console.error(`Failed on type ${JSON.stringify(type)} with error ${e}`);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hasLifecycleHook(type: any, lcProperty: string): boolean {
|
private _methodNames(type: any): {[key: string]: boolean} {
|
||||||
if (!(type instanceof StaticSymbol)) {
|
let methodNames = this.methodCache.get(type);
|
||||||
throw new Error(
|
if (!methodNames) {
|
||||||
`hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`);
|
const classMetadata = this.getTypeMetadata(type) || {};
|
||||||
}
|
methodNames = {};
|
||||||
const classMetadata = this.getTypeMetadata(type);
|
if (classMetadata['extends']) {
|
||||||
const members = classMetadata ? classMetadata['members'] : null;
|
const parentMethodNames = this._methodNames(this.simplify(type, classMetadata['extends']));
|
||||||
const member: any[] =
|
Object.keys(parentMethodNames).forEach((parentProp) => {
|
||||||
members && members.hasOwnProperty(lcProperty) ? members[lcProperty] : null;
|
methodNames[parentProp] = parentMethodNames[parentProp];
|
||||||
return member ? member.some(a => a['__symbolic'] == 'method') : false;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void {
|
const members = classMetadata['members'] || {};
|
||||||
|
Object.keys(members).forEach((propName) => {
|
||||||
|
const propData = members[propName];
|
||||||
|
const isMethod = (<any[]>propData).some(a => a['__symbolic'] == 'method');
|
||||||
|
methodNames[propName] = methodNames[propName] || isMethod;
|
||||||
|
});
|
||||||
|
this.methodCache.set(type, methodNames);
|
||||||
|
}
|
||||||
|
return methodNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasLifecycleHook(type: any, lcProperty: string): boolean {
|
||||||
|
if (!(type instanceof StaticSymbol)) {
|
||||||
|
this.reportError(
|
||||||
|
new Error(
|
||||||
|
`hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`),
|
||||||
|
type);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return !!this._methodNames(type)[lcProperty];
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Failed on type ${JSON.stringify(type)} with error ${e}`);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void {
|
||||||
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => new ctor(...args));
|
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => new ctor(...args));
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerFunction(type: StaticSymbol, fn: any): void {
|
private _registerFunction(type: StaticSymbol, fn: any): void {
|
||||||
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => fn.apply(undefined, args));
|
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => fn.apply(undefined, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,50 +251,51 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
ANGULAR_IMPORT_LOCATIONS;
|
ANGULAR_IMPORT_LOCATIONS;
|
||||||
this.opaqueToken = this.findDeclaration(diOpaqueToken, 'OpaqueToken');
|
this.opaqueToken = this.findDeclaration(diOpaqueToken, 'OpaqueToken');
|
||||||
|
|
||||||
this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Host'), Host);
|
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Host'), Host);
|
||||||
this.registerDecoratorOrConstructor(
|
this._registerDecoratorOrConstructor(
|
||||||
this.findDeclaration(diDecorators, 'Injectable'), Injectable);
|
this.findDeclaration(diDecorators, 'Injectable'), Injectable);
|
||||||
this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Self'), Self);
|
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Self'), Self);
|
||||||
this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'SkipSelf'), SkipSelf);
|
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'SkipSelf'), SkipSelf);
|
||||||
this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Inject'), Inject);
|
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Inject'), Inject);
|
||||||
this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Optional'), Optional);
|
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Optional'), Optional);
|
||||||
this.registerDecoratorOrConstructor(
|
this._registerDecoratorOrConstructor(
|
||||||
this.findDeclaration(coreDecorators, 'Attribute'), Attribute);
|
this.findDeclaration(coreDecorators, 'Attribute'), Attribute);
|
||||||
this.registerDecoratorOrConstructor(
|
this._registerDecoratorOrConstructor(
|
||||||
this.findDeclaration(coreDecorators, 'ContentChild'), ContentChild);
|
this.findDeclaration(coreDecorators, 'ContentChild'), ContentChild);
|
||||||
this.registerDecoratorOrConstructor(
|
this._registerDecoratorOrConstructor(
|
||||||
this.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildren);
|
this.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildren);
|
||||||
this.registerDecoratorOrConstructor(
|
this._registerDecoratorOrConstructor(
|
||||||
this.findDeclaration(coreDecorators, 'ViewChild'), ViewChild);
|
this.findDeclaration(coreDecorators, 'ViewChild'), ViewChild);
|
||||||
this.registerDecoratorOrConstructor(
|
this._registerDecoratorOrConstructor(
|
||||||
this.findDeclaration(coreDecorators, 'ViewChildren'), ViewChildren);
|
this.findDeclaration(coreDecorators, 'ViewChildren'), ViewChildren);
|
||||||
this.registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Input'), Input);
|
this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Input'), Input);
|
||||||
this.registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Output'), Output);
|
this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Output'), Output);
|
||||||
this.registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Pipe'), Pipe);
|
this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Pipe'), Pipe);
|
||||||
this.registerDecoratorOrConstructor(
|
this._registerDecoratorOrConstructor(
|
||||||
this.findDeclaration(coreDecorators, 'HostBinding'), HostBinding);
|
this.findDeclaration(coreDecorators, 'HostBinding'), HostBinding);
|
||||||
this.registerDecoratorOrConstructor(
|
this._registerDecoratorOrConstructor(
|
||||||
this.findDeclaration(coreDecorators, 'HostListener'), HostListener);
|
this.findDeclaration(coreDecorators, 'HostListener'), HostListener);
|
||||||
this.registerDecoratorOrConstructor(
|
this._registerDecoratorOrConstructor(
|
||||||
this.findDeclaration(coreDecorators, 'Directive'), Directive);
|
this.findDeclaration(coreDecorators, 'Directive'), Directive);
|
||||||
this.registerDecoratorOrConstructor(
|
this._registerDecoratorOrConstructor(
|
||||||
this.findDeclaration(coreDecorators, 'Component'), Component);
|
this.findDeclaration(coreDecorators, 'Component'), Component);
|
||||||
this.registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'NgModule'), NgModule);
|
this._registerDecoratorOrConstructor(
|
||||||
|
this.findDeclaration(coreDecorators, 'NgModule'), NgModule);
|
||||||
|
|
||||||
// Note: Some metadata classes can be used directly with Provider.deps.
|
// Note: Some metadata classes can be used directly with Provider.deps.
|
||||||
this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Host'), Host);
|
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Host'), Host);
|
||||||
this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Self'), Self);
|
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Self'), Self);
|
||||||
this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'SkipSelf'), SkipSelf);
|
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'SkipSelf'), SkipSelf);
|
||||||
this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Optional'), Optional);
|
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Optional'), Optional);
|
||||||
|
|
||||||
this.registerFunction(this.findDeclaration(animationMetadata, 'trigger'), trigger);
|
this._registerFunction(this.findDeclaration(animationMetadata, 'trigger'), trigger);
|
||||||
this.registerFunction(this.findDeclaration(animationMetadata, 'state'), state);
|
this._registerFunction(this.findDeclaration(animationMetadata, 'state'), state);
|
||||||
this.registerFunction(this.findDeclaration(animationMetadata, 'transition'), transition);
|
this._registerFunction(this.findDeclaration(animationMetadata, 'transition'), transition);
|
||||||
this.registerFunction(this.findDeclaration(animationMetadata, 'style'), style);
|
this._registerFunction(this.findDeclaration(animationMetadata, 'style'), style);
|
||||||
this.registerFunction(this.findDeclaration(animationMetadata, 'animate'), animate);
|
this._registerFunction(this.findDeclaration(animationMetadata, 'animate'), animate);
|
||||||
this.registerFunction(this.findDeclaration(animationMetadata, 'keyframes'), keyframes);
|
this._registerFunction(this.findDeclaration(animationMetadata, 'keyframes'), keyframes);
|
||||||
this.registerFunction(this.findDeclaration(animationMetadata, 'sequence'), sequence);
|
this._registerFunction(this.findDeclaration(animationMetadata, 'sequence'), sequence);
|
||||||
this.registerFunction(this.findDeclaration(animationMetadata, 'group'), group);
|
this._registerFunction(this.findDeclaration(animationMetadata, 'group'), group);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -250,11 +309,21 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
return this.staticSymbolCache.get(declarationFile, name, members);
|
return this.staticSymbolCache.get(declarationFile, name, members);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private reportError(error: Error, context: StaticSymbol, path?: string) {
|
||||||
|
if (this.errorRecorder) {
|
||||||
|
this.errorRecorder(error, (context && context.filePath) || path);
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private resolveExportedSymbol(filePath: string, symbolName: string): StaticSymbol {
|
private resolveExportedSymbol(filePath: string, symbolName: string): StaticSymbol {
|
||||||
const resolveModule = (moduleName: string): string => {
|
const resolveModule = (moduleName: string): string => {
|
||||||
const resolvedModulePath = this.host.moduleNameToFileName(moduleName, filePath);
|
const resolvedModulePath = this.host.moduleNameToFileName(moduleName, filePath);
|
||||||
if (!resolvedModulePath) {
|
if (!resolvedModulePath) {
|
||||||
throw new Error(`Could not resolve module '${moduleName}' relative to file ${filePath}`);
|
this.reportError(
|
||||||
|
new Error(`Could not resolve module '${moduleName}' relative to file ${filePath}`),
|
||||||
|
null, filePath);
|
||||||
}
|
}
|
||||||
return resolvedModulePath;
|
return resolvedModulePath;
|
||||||
};
|
};
|
||||||
@ -287,7 +356,12 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
if (typeof exportSymbol !== 'string') {
|
if (typeof exportSymbol !== 'string') {
|
||||||
symName = exportSymbol.name;
|
symName = exportSymbol.name;
|
||||||
}
|
}
|
||||||
staticSymbol = this.resolveExportedSymbol(resolveModule(moduleExport.from), symName);
|
const resolvedModule = resolveModule(moduleExport.from);
|
||||||
|
if (resolvedModule) {
|
||||||
|
staticSymbol =
|
||||||
|
this.resolveExportedSymbol(resolveModule(moduleExport.from), symName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -297,6 +371,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
for (const moduleExport of metadata['exports']) {
|
for (const moduleExport of metadata['exports']) {
|
||||||
if (!moduleExport.export) {
|
if (!moduleExport.export) {
|
||||||
const resolvedModule = resolveModule(moduleExport.from);
|
const resolvedModule = resolveModule(moduleExport.from);
|
||||||
|
if (resolvedModule) {
|
||||||
const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName);
|
const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName);
|
||||||
if (candidateSymbol) {
|
if (candidateSymbol) {
|
||||||
staticSymbol = candidateSymbol;
|
staticSymbol = candidateSymbol;
|
||||||
@ -307,6 +382,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.declarationCache.set(cacheKey, staticSymbol);
|
this.declarationCache.set(cacheKey, staticSymbol);
|
||||||
return staticSymbol;
|
return staticSymbol;
|
||||||
}
|
}
|
||||||
@ -333,7 +409,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
public simplify(context: StaticSymbol, value: any): any {
|
public simplify(context: StaticSymbol, value: any): any {
|
||||||
const _this = this;
|
const self = this;
|
||||||
let scope = BindingScope.empty;
|
let scope = BindingScope.empty;
|
||||||
const calling = new Map<StaticSymbol, boolean>();
|
const calling = new Map<StaticSymbol, boolean>();
|
||||||
|
|
||||||
@ -342,15 +418,15 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
let staticSymbol: StaticSymbol;
|
let staticSymbol: StaticSymbol;
|
||||||
if (expression['module']) {
|
if (expression['module']) {
|
||||||
staticSymbol =
|
staticSymbol =
|
||||||
_this.findDeclaration(expression['module'], expression['name'], context.filePath);
|
self.findDeclaration(expression['module'], expression['name'], context.filePath);
|
||||||
} else {
|
} else {
|
||||||
staticSymbol = _this.getStaticSymbol(context.filePath, expression['name']);
|
staticSymbol = self.getStaticSymbol(context.filePath, expression['name']);
|
||||||
}
|
}
|
||||||
return staticSymbol;
|
return staticSymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveReferenceValue(staticSymbol: StaticSymbol): any {
|
function resolveReferenceValue(staticSymbol: StaticSymbol): any {
|
||||||
const moduleMetadata = _this.getModuleMetadata(staticSymbol.filePath);
|
const moduleMetadata = self.getModuleMetadata(staticSymbol.filePath);
|
||||||
const declarationValue =
|
const declarationValue =
|
||||||
moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null;
|
moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null;
|
||||||
return declarationValue;
|
return declarationValue;
|
||||||
@ -360,7 +436,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
if (value && value.__symbolic === 'new' && value.expression) {
|
if (value && value.__symbolic === 'new' && value.expression) {
|
||||||
const target = value.expression;
|
const target = value.expression;
|
||||||
if (target.__symbolic == 'reference') {
|
if (target.__symbolic == 'reference') {
|
||||||
return sameSymbol(resolveReference(context, target), _this.opaqueToken);
|
return sameSymbol(resolveReference(context, target), self.opaqueToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -542,24 +618,31 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
if (indexTarget && isPrimitive(index)) return indexTarget[index];
|
if (indexTarget && isPrimitive(index)) return indexTarget[index];
|
||||||
return null;
|
return null;
|
||||||
case 'select':
|
case 'select':
|
||||||
|
let selectContext = context;
|
||||||
let selectTarget = simplify(expression['expression']);
|
let selectTarget = simplify(expression['expression']);
|
||||||
if (selectTarget instanceof StaticSymbol) {
|
if (selectTarget instanceof StaticSymbol) {
|
||||||
// Access to a static instance variable
|
// Access to a static instance variable
|
||||||
const declarationValue = resolveReferenceValue(selectTarget);
|
|
||||||
if (declarationValue && declarationValue.statics) {
|
|
||||||
selectTarget = declarationValue.statics;
|
|
||||||
} else {
|
|
||||||
const member: string = expression['member'];
|
const member: string = expression['member'];
|
||||||
const members = selectTarget.members ?
|
const members = selectTarget.members ?
|
||||||
(selectTarget.members as string[]).concat(member) :
|
(selectTarget.members as string[]).concat(member) :
|
||||||
[member];
|
[member];
|
||||||
return _this.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
|
const declarationValue = resolveReferenceValue(selectTarget);
|
||||||
|
selectContext =
|
||||||
|
self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
|
||||||
|
if (declarationValue && declarationValue.statics) {
|
||||||
|
selectTarget = declarationValue.statics;
|
||||||
|
} else {
|
||||||
|
return selectContext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const member = simplify(expression['member']);
|
const member = simplifyInContext(selectContext, expression['member'], depth + 1);
|
||||||
if (selectTarget && isPrimitive(member)) return simplify(selectTarget[member]);
|
if (selectTarget && isPrimitive(member))
|
||||||
|
return simplifyInContext(selectContext, selectTarget[member], depth + 1);
|
||||||
return null;
|
return null;
|
||||||
case 'reference':
|
case 'reference':
|
||||||
|
if (!expression['name']) {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
if (!expression.module) {
|
if (!expression.module) {
|
||||||
const name: string = expression['name'];
|
const name: string = expression['name'];
|
||||||
const localValue = scope.resolve(name);
|
const localValue = scope.resolve(name);
|
||||||
@ -589,11 +672,11 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
let target = expression['expression'];
|
let target = expression['expression'];
|
||||||
if (target['module']) {
|
if (target['module']) {
|
||||||
staticSymbol =
|
staticSymbol =
|
||||||
_this.findDeclaration(target['module'], target['name'], context.filePath);
|
self.findDeclaration(target['module'], target['name'], context.filePath);
|
||||||
} else {
|
} else {
|
||||||
staticSymbol = _this.getStaticSymbol(context.filePath, target['name']);
|
staticSymbol = self.getStaticSymbol(context.filePath, target['name']);
|
||||||
}
|
}
|
||||||
let converter = _this.conversionMap.get(staticSymbol);
|
let converter = self.conversionMap.get(staticSymbol);
|
||||||
if (converter) {
|
if (converter) {
|
||||||
let args: any[] = expression['arguments'];
|
let args: any[] = expression['arguments'];
|
||||||
if (!args) {
|
if (!args) {
|
||||||
@ -634,7 +717,16 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = simplifyInContext(context, value, 0);
|
const recordedSimplifyInContext = (context: StaticSymbol, value: any, depth: number) => {
|
||||||
|
try {
|
||||||
|
return simplifyInContext(context, value, depth);
|
||||||
|
} catch (e) {
|
||||||
|
this.reportError(e, context);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = this.errorRecorder ? recordedSimplifyInContext(context, value, 0) :
|
||||||
|
simplifyInContext(context, value, 0);
|
||||||
if (shouldIgnore(result)) {
|
if (shouldIgnore(result)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -662,8 +754,10 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
{__symbolic: 'module', version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {}};
|
{__symbolic: 'module', version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {}};
|
||||||
}
|
}
|
||||||
if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) {
|
if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) {
|
||||||
throw new Error(
|
const errorMessage = moduleMetadata['version'] == 2 ?
|
||||||
`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`);
|
`Unsupported metadata version ${moduleMetadata['version']} for module ${module}. This module should be compiled with a newer version of ngc` :
|
||||||
|
`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`;
|
||||||
|
this.reportError(new Error(errorMessage), null);
|
||||||
}
|
}
|
||||||
this.metadataCache.set(module, moduleMetadata);
|
this.metadataCache.set(module, moduleMetadata);
|
||||||
}
|
}
|
||||||
@ -717,8 +811,12 @@ function mapStringMap(input: {[key: string]: any}, transform: (value: any, key:
|
|||||||
Object.keys(input).forEach((key) => {
|
Object.keys(input).forEach((key) => {
|
||||||
const value = transform(input[key], key);
|
const value = transform(input[key], key);
|
||||||
if (!shouldIgnore(value)) {
|
if (!shouldIgnore(value)) {
|
||||||
|
if (HIDDEN_KEY.test(key)) {
|
||||||
|
Object.defineProperty(result, key, {enumerable: false, configurable: true, value: value});
|
||||||
|
} else {
|
||||||
result[key] = value;
|
result[key] = value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,6 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function isStaticSymbol(value: any): value is StaticSymbol {
|
|
||||||
return typeof value === 'object' && value !== null && value['name'] && value['filePath'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A token representing the a reference to a static type.
|
* A token representing the a reference to a static type.
|
||||||
*
|
*
|
||||||
|
124
modules/@angular/compiler/src/aot/summary_resolver.ts
Normal file
124
modules/@angular/compiler/src/aot/summary_resolver.ts
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/**
|
||||||
|
* @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 {CompileDirectiveSummary, CompileIdentifierMetadata, CompileNgModuleSummary, CompilePipeSummary, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary, identifierModuleUrl, identifierName} from '../compile_metadata';
|
||||||
|
import {SummaryResolver} from '../summary_resolver';
|
||||||
|
|
||||||
|
import {GeneratedFile} from './generated_file';
|
||||||
|
import {StaticReflector} from './static_reflector';
|
||||||
|
import {StaticSymbol} from './static_symbol';
|
||||||
|
import {filterFileByPatterns} from './utils';
|
||||||
|
|
||||||
|
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||||
|
|
||||||
|
export interface AotSummaryResolverHost {
|
||||||
|
/**
|
||||||
|
* Loads an NgModule/Directive/Pipe summary file
|
||||||
|
*/
|
||||||
|
loadSummary(filePath: string): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the output file path of a source file.
|
||||||
|
* E.g.
|
||||||
|
* `some_file.ts` -> `some_file.d.ts`
|
||||||
|
*/
|
||||||
|
getOutputFileName(sourceFilePath: string): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AotSummaryResolverOptions {
|
||||||
|
includeFilePattern?: RegExp;
|
||||||
|
excludeFilePattern?: RegExp;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AotSummaryResolver implements SummaryResolver {
|
||||||
|
private summaryCache: {[cacheKey: string]: CompileTypeSummary} = {};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private host: AotSummaryResolverHost, private staticReflector: StaticReflector,
|
||||||
|
private options: AotSummaryResolverOptions) {}
|
||||||
|
|
||||||
|
serializeSummaries(srcFileUrl: string, summaries: CompileTypeSummary[]): GeneratedFile {
|
||||||
|
const jsonReplacer = (key: string, value: any) => {
|
||||||
|
if (value instanceof StaticSymbol) {
|
||||||
|
// We convert the source filenames into output filenames,
|
||||||
|
// as the generated summary file will be used when the current
|
||||||
|
// compilation unit is used as a library
|
||||||
|
return {
|
||||||
|
'__symbolic__': 'symbol',
|
||||||
|
'name': value.name,
|
||||||
|
'path': this.host.getOutputFileName(value.filePath),
|
||||||
|
'members': value.members
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
const allSummaries = summaries.slice();
|
||||||
|
summaries.forEach((summary) => {
|
||||||
|
if (summary.summaryKind === CompileSummaryKind.NgModule) {
|
||||||
|
const moduleMeta = <CompileNgModuleSummary>summary;
|
||||||
|
moduleMeta.exportedDirectives.concat(moduleMeta.exportedPipes).forEach((id) => {
|
||||||
|
if (!filterFileByPatterns(id.reference.filePath, this.options)) {
|
||||||
|
allSummaries.push(this.resolveSummary(id.reference));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return new GeneratedFile(
|
||||||
|
srcFileUrl, summaryFileName(srcFileUrl), JSON.stringify(allSummaries, jsonReplacer));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _cacheKey(symbol: StaticSymbol) { return `${symbol.filePath}|${symbol.name}`; }
|
||||||
|
|
||||||
|
resolveSummary(staticSymbol: StaticSymbol): any {
|
||||||
|
const filePath = staticSymbol.filePath;
|
||||||
|
const name = staticSymbol.name;
|
||||||
|
const cacheKey = this._cacheKey(staticSymbol);
|
||||||
|
if (!filterFileByPatterns(filePath, this.options)) {
|
||||||
|
let summary = this.summaryCache[cacheKey];
|
||||||
|
const summaryFilePath = summaryFileName(filePath);
|
||||||
|
if (!summary) {
|
||||||
|
try {
|
||||||
|
const jsonReviver = (key: string, value: any) => {
|
||||||
|
if (value && value['__symbolic__'] === 'symbol') {
|
||||||
|
// Note: We can't use staticReflector.findDeclaration here:
|
||||||
|
// Summary files can contain symbols of transitive compilation units
|
||||||
|
// (via the providers), and findDeclaration needs .metadata.json / .d.ts files,
|
||||||
|
// but we don't want to depend on these for transitive dependencies.
|
||||||
|
return this.staticReflector.getStaticSymbol(
|
||||||
|
value['path'], value['name'], value['members']);
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const readSummaries: CompileTypeSummary[] =
|
||||||
|
JSON.parse(this.host.loadSummary(summaryFilePath), jsonReviver);
|
||||||
|
readSummaries.forEach((summary) => {
|
||||||
|
const filePath = summary.type.reference.filePath;
|
||||||
|
this.summaryCache[this._cacheKey(summary.type.reference)] = summary;
|
||||||
|
});
|
||||||
|
summary = this.summaryCache[cacheKey];
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Error loading summary file ${summaryFilePath}`);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!summary) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not find the symbol ${name} in the summary file ${summaryFilePath}!`);
|
||||||
|
}
|
||||||
|
return summary;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function summaryFileName(fileName: string): string {
|
||||||
|
const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
|
||||||
|
return `${fileNameWithoutSuffix}.ngsummary.json`;
|
||||||
|
}
|
19
modules/@angular/compiler/src/aot/utils.ts
Normal file
19
modules/@angular/compiler/src/aot/utils.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function filterFileByPatterns(
|
||||||
|
fileName: string, options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}) {
|
||||||
|
let match = true;
|
||||||
|
if (options.includeFilePattern) {
|
||||||
|
match = match && !!options.includeFilePattern.exec(fileName);
|
||||||
|
}
|
||||||
|
if (options.excludeFilePattern) {
|
||||||
|
match = match && !options.excludeFilePattern.exec(fileName);
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
}
|
@ -8,11 +8,12 @@
|
|||||||
|
|
||||||
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
|
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
|
import {StaticSymbol} from './aot/static_symbol';
|
||||||
import {ListWrapper} from './facade/collection';
|
import {ListWrapper} from './facade/collection';
|
||||||
import {isPresent} from './facade/lang';
|
import {isPresent, stringify} from './facade/lang';
|
||||||
import {LifecycleHooks} from './private_import_core';
|
import {LifecycleHooks, reflector} from './private_import_core';
|
||||||
import {CssSelector} from './selector';
|
import {CssSelector} from './selector';
|
||||||
import {sanitizeIdentifier, splitAtColon} from './util';
|
import {splitAtColon} from './util';
|
||||||
|
|
||||||
function unimplemented(): any {
|
function unimplemented(): any {
|
||||||
throw new Error('unimplemented');
|
throw new Error('unimplemented');
|
||||||
@ -24,10 +25,6 @@ function unimplemented(): any {
|
|||||||
// group 3: "@trigger" from "@trigger"
|
// group 3: "@trigger" from "@trigger"
|
||||||
const HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/;
|
const HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/;
|
||||||
|
|
||||||
export abstract class CompileMetadataWithIdentifier {
|
|
||||||
get identifier(): CompileIdentifierMetadata { return <CompileIdentifierMetadata>unimplemented(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CompileAnimationEntryMetadata {
|
export class CompileAnimationEntryMetadata {
|
||||||
constructor(
|
constructor(
|
||||||
public name: string = null, public definitions: CompileAnimationStateMetadata[] = null) {}
|
public name: string = null, public definitions: CompileAnimationStateMetadata[] = null) {}
|
||||||
@ -78,24 +75,50 @@ export class CompileAnimationGroupMetadata extends CompileAnimationWithStepsMeta
|
|||||||
constructor(steps: CompileAnimationMetadata[] = null) { super(steps); }
|
constructor(steps: CompileAnimationMetadata[] = null) { super(steps); }
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier {
|
|
||||||
reference: any;
|
|
||||||
name: string;
|
|
||||||
prefix: string;
|
|
||||||
moduleUrl: string;
|
|
||||||
value: any;
|
|
||||||
|
|
||||||
constructor(
|
function _sanitizeIdentifier(name: string): string {
|
||||||
{reference, name, moduleUrl, prefix, value}:
|
return name.replace(/\W/g, '_');
|
||||||
{reference?: any, name?: string, moduleUrl?: string, prefix?: string, value?: any} = {}) {
|
}
|
||||||
this.reference = reference;
|
|
||||||
this.name = name;
|
let _anonymousTypeIndex = 0;
|
||||||
this.prefix = prefix;
|
|
||||||
this.moduleUrl = moduleUrl;
|
export function identifierName(compileIdentifier: CompileIdentifierMetadata): string {
|
||||||
this.value = value;
|
if (!compileIdentifier || !compileIdentifier.reference) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
const ref = compileIdentifier.reference;
|
||||||
|
if (ref instanceof StaticSymbol) {
|
||||||
|
return ref.name;
|
||||||
|
}
|
||||||
|
if (ref['__anonymousType']) {
|
||||||
|
return ref['__anonymousType'];
|
||||||
|
}
|
||||||
|
let identifier = stringify(ref);
|
||||||
|
if (identifier.indexOf('(') >= 0) {
|
||||||
|
// case: anonymous functions!
|
||||||
|
identifier = `anonymous_${_anonymousTypeIndex++}`;
|
||||||
|
ref['__anonymousType'] = identifier;
|
||||||
|
} else {
|
||||||
|
identifier = _sanitizeIdentifier(identifier);
|
||||||
|
}
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
get identifier(): CompileIdentifierMetadata { return this; }
|
export function identifierModuleUrl(compileIdentifier: CompileIdentifierMetadata): string {
|
||||||
|
const ref = compileIdentifier.reference;
|
||||||
|
if (ref instanceof StaticSymbol) {
|
||||||
|
return ref.filePath;
|
||||||
|
}
|
||||||
|
return reflector.importUri(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CompileIdentifierMetadata { reference: any; }
|
||||||
|
|
||||||
|
export enum CompileSummaryKind {
|
||||||
|
Template,
|
||||||
|
Pipe,
|
||||||
|
Directive,
|
||||||
|
NgModule
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,158 +126,69 @@ export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier
|
|||||||
* in other modules / components. However, this data is not enough to compile
|
* in other modules / components. However, this data is not enough to compile
|
||||||
* the directive / module itself.
|
* the directive / module itself.
|
||||||
*/
|
*/
|
||||||
export interface CompileSummary {
|
export interface CompileSummary { summaryKind: CompileSummaryKind; }
|
||||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
|
||||||
|
export interface CompileTypeSummary extends CompileSummary { type: CompileTypeMetadata; }
|
||||||
|
|
||||||
|
export interface CompileDiDependencyMetadata {
|
||||||
|
isAttribute?: boolean;
|
||||||
|
isSelf?: boolean;
|
||||||
|
isHost?: boolean;
|
||||||
|
isSkipSelf?: boolean;
|
||||||
|
isOptional?: boolean;
|
||||||
|
isValue?: boolean;
|
||||||
|
token?: CompileTokenMetadata;
|
||||||
|
value?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompileDiDependencyMetadata {
|
export interface CompileProviderMetadata {
|
||||||
isAttribute: boolean;
|
|
||||||
isSelf: boolean;
|
|
||||||
isHost: boolean;
|
|
||||||
isSkipSelf: boolean;
|
|
||||||
isOptional: boolean;
|
|
||||||
isValue: boolean;
|
|
||||||
token: CompileTokenMetadata;
|
token: CompileTokenMetadata;
|
||||||
value: any;
|
useClass?: CompileTypeMetadata;
|
||||||
|
useValue?: any;
|
||||||
constructor({isAttribute, isSelf, isHost, isSkipSelf, isOptional, isValue, token, value}: {
|
useExisting?: CompileTokenMetadata;
|
||||||
isAttribute?: boolean,
|
useFactory?: CompileFactoryMetadata;
|
||||||
isSelf?: boolean,
|
deps?: CompileDiDependencyMetadata[];
|
||||||
isHost?: boolean,
|
multi?: boolean;
|
||||||
isSkipSelf?: boolean,
|
|
||||||
isOptional?: boolean,
|
|
||||||
isValue?: boolean,
|
|
||||||
query?: CompileQueryMetadata,
|
|
||||||
viewQuery?: CompileQueryMetadata,
|
|
||||||
token?: CompileTokenMetadata,
|
|
||||||
value?: any
|
|
||||||
} = {}) {
|
|
||||||
this.isAttribute = !!isAttribute;
|
|
||||||
this.isSelf = !!isSelf;
|
|
||||||
this.isHost = !!isHost;
|
|
||||||
this.isSkipSelf = !!isSkipSelf;
|
|
||||||
this.isOptional = !!isOptional;
|
|
||||||
this.isValue = !!isValue;
|
|
||||||
this.token = token;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompileProviderMetadata {
|
export interface CompileFactoryMetadata extends CompileIdentifierMetadata {
|
||||||
token: CompileTokenMetadata;
|
|
||||||
useClass: CompileTypeMetadata;
|
|
||||||
useValue: any;
|
|
||||||
useExisting: CompileTokenMetadata;
|
|
||||||
useFactory: CompileFactoryMetadata;
|
|
||||||
deps: CompileDiDependencyMetadata[];
|
|
||||||
multi: boolean;
|
|
||||||
|
|
||||||
constructor({token, useClass, useValue, useExisting, useFactory, deps, multi}: {
|
|
||||||
token?: CompileTokenMetadata,
|
|
||||||
useClass?: CompileTypeMetadata,
|
|
||||||
useValue?: any,
|
|
||||||
useExisting?: CompileTokenMetadata,
|
|
||||||
useFactory?: CompileFactoryMetadata,
|
|
||||||
deps?: CompileDiDependencyMetadata[],
|
|
||||||
multi?: boolean
|
|
||||||
}) {
|
|
||||||
this.token = token;
|
|
||||||
this.useClass = useClass;
|
|
||||||
this.useValue = useValue;
|
|
||||||
this.useExisting = useExisting;
|
|
||||||
this.useFactory = useFactory;
|
|
||||||
this.deps = deps || null;
|
|
||||||
this.multi = !!multi;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CompileFactoryMetadata extends CompileIdentifierMetadata {
|
|
||||||
diDeps: CompileDiDependencyMetadata[];
|
diDeps: CompileDiDependencyMetadata[];
|
||||||
|
reference: any;
|
||||||
|
}
|
||||||
|
|
||||||
constructor({reference, name, moduleUrl, prefix, diDeps, value}: {
|
export function tokenName(token: CompileTokenMetadata) {
|
||||||
reference?: Function,
|
return isPresent(token.value) ? _sanitizeIdentifier(token.value) :
|
||||||
name?: string,
|
identifierName(token.identifier);
|
||||||
prefix?: string,
|
}
|
||||||
moduleUrl?: string,
|
|
||||||
value?: boolean,
|
export function tokenReference(token: CompileTokenMetadata) {
|
||||||
diDeps?: CompileDiDependencyMetadata[]
|
if (isPresent(token.identifier)) {
|
||||||
}) {
|
return token.identifier.reference;
|
||||||
super({reference: reference, name: name, prefix: prefix, moduleUrl: moduleUrl, value: value});
|
} else {
|
||||||
this.diDeps = _normalizeArray(diDeps);
|
return token.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompileTokenMetadata implements CompileMetadataWithIdentifier {
|
export interface CompileTokenMetadata {
|
||||||
value: any;
|
value?: any;
|
||||||
identifier: CompileIdentifierMetadata;
|
identifier?: CompileIdentifierMetadata|CompileTypeMetadata;
|
||||||
identifierIsInstance: boolean;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
{value, identifier, identifierIsInstance}:
|
|
||||||
{value?: any, identifier?: CompileIdentifierMetadata, identifierIsInstance?: boolean}) {
|
|
||||||
this.value = value;
|
|
||||||
this.identifier = identifier;
|
|
||||||
this.identifierIsInstance = !!identifierIsInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
get reference(): any {
|
|
||||||
if (isPresent(this.identifier)) {
|
|
||||||
return this.identifier.reference;
|
|
||||||
} else {
|
|
||||||
return this.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get name(): string {
|
|
||||||
return isPresent(this.value) ? sanitizeIdentifier(this.value) : this.identifier.name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metadata regarding compilation of a type.
|
* Metadata regarding compilation of a type.
|
||||||
*/
|
*/
|
||||||
export class CompileTypeMetadata extends CompileIdentifierMetadata {
|
export interface CompileTypeMetadata extends CompileIdentifierMetadata {
|
||||||
isHost: boolean;
|
|
||||||
diDeps: CompileDiDependencyMetadata[];
|
diDeps: CompileDiDependencyMetadata[];
|
||||||
lifecycleHooks: LifecycleHooks[];
|
lifecycleHooks: LifecycleHooks[];
|
||||||
|
reference: any;
|
||||||
constructor({reference, name, moduleUrl, prefix, isHost, value, diDeps, lifecycleHooks}: {
|
|
||||||
reference?: Type<any>,
|
|
||||||
name?: string,
|
|
||||||
moduleUrl?: string,
|
|
||||||
prefix?: string,
|
|
||||||
isHost?: boolean,
|
|
||||||
value?: any,
|
|
||||||
diDeps?: CompileDiDependencyMetadata[],
|
|
||||||
lifecycleHooks?: LifecycleHooks[];
|
|
||||||
} = {}) {
|
|
||||||
super({reference: reference, name: name, moduleUrl: moduleUrl, prefix: prefix, value: value});
|
|
||||||
this.isHost = !!isHost;
|
|
||||||
this.diDeps = _normalizeArray(diDeps);
|
|
||||||
this.lifecycleHooks = _normalizeArray(lifecycleHooks);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompileQueryMetadata {
|
export interface CompileQueryMetadata {
|
||||||
selectors: Array<CompileTokenMetadata>;
|
selectors: Array<CompileTokenMetadata>;
|
||||||
descendants: boolean;
|
descendants: boolean;
|
||||||
first: boolean;
|
first: boolean;
|
||||||
propertyName: string;
|
propertyName: string;
|
||||||
read: CompileTokenMetadata;
|
read: CompileTokenMetadata;
|
||||||
|
|
||||||
constructor({selectors, descendants, first, propertyName, read}: {
|
|
||||||
selectors?: Array<CompileTokenMetadata>,
|
|
||||||
descendants?: boolean,
|
|
||||||
first?: boolean,
|
|
||||||
propertyName?: string,
|
|
||||||
read?: CompileTokenMetadata
|
|
||||||
} = {}) {
|
|
||||||
this.selectors = selectors;
|
|
||||||
this.descendants = !!descendants;
|
|
||||||
this.first = !!first;
|
|
||||||
this.propertyName = propertyName;
|
|
||||||
this.read = read;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -277,7 +211,6 @@ export class CompileStylesheetMetadata {
|
|||||||
* Summary Metadata regarding compilation of a template.
|
* Summary Metadata regarding compilation of a template.
|
||||||
*/
|
*/
|
||||||
export interface CompileTemplateSummary extends CompileSummary {
|
export interface CompileTemplateSummary extends CompileSummary {
|
||||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
|
||||||
animations: string[];
|
animations: string[];
|
||||||
ngContentSelectors: string[];
|
ngContentSelectors: string[];
|
||||||
encapsulation: ViewEncapsulation;
|
encapsulation: ViewEncapsulation;
|
||||||
@ -325,7 +258,7 @@ export class CompileTemplateMetadata {
|
|||||||
|
|
||||||
toSummary(): CompileTemplateSummary {
|
toSummary(): CompileTemplateSummary {
|
||||||
return {
|
return {
|
||||||
isSummary: true,
|
summaryKind: CompileSummaryKind.Template,
|
||||||
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
|
||||||
@ -333,8 +266,9 @@ export class CompileTemplateMetadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompileDirectiveSummary extends CompileSummary {
|
// Note: This should only use interfaces as nested data types
|
||||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
// as we need to be able to serialize this from/to JSON!
|
||||||
|
export interface CompileDirectiveSummary extends CompileTypeSummary {
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
isComponent: boolean;
|
isComponent: boolean;
|
||||||
selector: string;
|
selector: string;
|
||||||
@ -355,10 +289,11 @@ export interface CompileDirectiveSummary extends CompileSummary {
|
|||||||
/**
|
/**
|
||||||
* Metadata regarding compilation of a directive.
|
* Metadata regarding compilation of a directive.
|
||||||
*/
|
*/
|
||||||
export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
export class CompileDirectiveMetadata {
|
||||||
static create(
|
static create(
|
||||||
{type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host, providers,
|
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host,
|
||||||
viewProviders, queries, viewQueries, entryComponents, template}: {
|
providers, viewProviders, queries, viewQueries, entryComponents, template}: {
|
||||||
|
isHost?: boolean,
|
||||||
type?: CompileTypeMetadata,
|
type?: CompileTypeMetadata,
|
||||||
isComponent?: boolean,
|
isComponent?: boolean,
|
||||||
selector?: string,
|
selector?: string,
|
||||||
@ -367,10 +302,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
|||||||
inputs?: string[],
|
inputs?: string[],
|
||||||
outputs?: string[],
|
outputs?: string[],
|
||||||
host?: {[key: string]: string},
|
host?: {[key: string]: string},
|
||||||
providers?:
|
providers?: CompileProviderMetadata[],
|
||||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
viewProviders?: CompileProviderMetadata[],
|
||||||
viewProviders?:
|
|
||||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
|
||||||
queries?: CompileQueryMetadata[],
|
queries?: CompileQueryMetadata[],
|
||||||
viewQueries?: CompileQueryMetadata[],
|
viewQueries?: CompileQueryMetadata[],
|
||||||
entryComponents?: CompileIdentifierMetadata[],
|
entryComponents?: CompileIdentifierMetadata[],
|
||||||
@ -412,6 +345,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new CompileDirectiveMetadata({
|
return new CompileDirectiveMetadata({
|
||||||
|
isHost,
|
||||||
type,
|
type,
|
||||||
isComponent: !!isComponent, selector, exportAs, changeDetection,
|
isComponent: !!isComponent, selector, exportAs, changeDetection,
|
||||||
inputs: inputsMap,
|
inputs: inputsMap,
|
||||||
@ -427,6 +361,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
|||||||
template,
|
template,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
isHost: boolean;
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
isComponent: boolean;
|
isComponent: boolean;
|
||||||
selector: string;
|
selector: string;
|
||||||
@ -446,9 +381,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
|||||||
template: CompileTemplateMetadata;
|
template: CompileTemplateMetadata;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{type, isComponent, selector, exportAs, changeDetection, inputs, outputs, hostListeners,
|
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs,
|
||||||
hostProperties, hostAttributes, providers, viewProviders, queries, viewQueries,
|
hostListeners, hostProperties, hostAttributes, providers, viewProviders, queries,
|
||||||
entryComponents, template}: {
|
viewQueries, entryComponents, template}: {
|
||||||
|
isHost?: boolean,
|
||||||
type?: CompileTypeMetadata,
|
type?: CompileTypeMetadata,
|
||||||
isComponent?: boolean,
|
isComponent?: boolean,
|
||||||
selector?: string,
|
selector?: string,
|
||||||
@ -459,15 +395,14 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
|||||||
hostListeners?: {[key: string]: string},
|
hostListeners?: {[key: string]: string},
|
||||||
hostProperties?: {[key: string]: string},
|
hostProperties?: {[key: string]: string},
|
||||||
hostAttributes?: {[key: string]: string},
|
hostAttributes?: {[key: string]: string},
|
||||||
providers?:
|
providers?: CompileProviderMetadata[],
|
||||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
viewProviders?: CompileProviderMetadata[],
|
||||||
viewProviders?:
|
|
||||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
|
||||||
queries?: CompileQueryMetadata[],
|
queries?: CompileQueryMetadata[],
|
||||||
viewQueries?: CompileQueryMetadata[],
|
viewQueries?: CompileQueryMetadata[],
|
||||||
entryComponents?: CompileIdentifierMetadata[],
|
entryComponents?: CompileIdentifierMetadata[],
|
||||||
template?: CompileTemplateMetadata,
|
template?: CompileTemplateMetadata,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
|
this.isHost = !!isHost;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.isComponent = isComponent;
|
this.isComponent = isComponent;
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
@ -487,11 +422,9 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
|||||||
this.template = template;
|
this.template = template;
|
||||||
}
|
}
|
||||||
|
|
||||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
|
||||||
|
|
||||||
toSummary(): CompileDirectiveSummary {
|
toSummary(): CompileDirectiveSummary {
|
||||||
return {
|
return {
|
||||||
isSummary: true,
|
summaryKind: CompileSummaryKind.Directive,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
isComponent: this.isComponent,
|
isComponent: this.isComponent,
|
||||||
selector: this.selector,
|
selector: this.selector,
|
||||||
@ -514,16 +447,12 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
|||||||
/**
|
/**
|
||||||
* Construct {@link CompileDirectiveMetadata} from {@link ComponentTypeMetadata} and a selector.
|
* Construct {@link CompileDirectiveMetadata} from {@link ComponentTypeMetadata} and a selector.
|
||||||
*/
|
*/
|
||||||
export function createHostComponentMeta(compMeta: CompileDirectiveMetadata):
|
export function createHostComponentMeta(
|
||||||
CompileDirectiveMetadata {
|
typeReference: any, compMeta: CompileDirectiveMetadata): CompileDirectiveMetadata {
|
||||||
const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
|
const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
|
||||||
return CompileDirectiveMetadata.create({
|
return CompileDirectiveMetadata.create({
|
||||||
type: new CompileTypeMetadata({
|
isHost: true,
|
||||||
reference: Object,
|
type: {reference: typeReference, diDeps: [], lifecycleHooks: []},
|
||||||
name: `${compMeta.type.name}_Host`,
|
|
||||||
moduleUrl: compMeta.type.moduleUrl,
|
|
||||||
isHost: true
|
|
||||||
}),
|
|
||||||
template: new CompileTemplateMetadata({
|
template: new CompileTemplateMetadata({
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
template: template,
|
template: template,
|
||||||
@ -546,14 +475,13 @@ export function createHostComponentMeta(compMeta: CompileDirectiveMetadata):
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompilePipeSummary extends CompileSummary {
|
export interface CompilePipeSummary extends CompileTypeSummary {
|
||||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
name: string;
|
name: string;
|
||||||
pure: boolean;
|
pure: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
|
export class CompilePipeMetadata {
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
name: string;
|
name: string;
|
||||||
pure: boolean;
|
pure: boolean;
|
||||||
@ -567,42 +495,44 @@ export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
this.pure = !!pure;
|
this.pure = !!pure;
|
||||||
}
|
}
|
||||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
|
||||||
|
|
||||||
toSummary(): CompilePipeSummary {
|
toSummary(): CompilePipeSummary {
|
||||||
return {isSummary: true, type: this.type, name: this.name, pure: this.pure};
|
return {
|
||||||
|
summaryKind: CompileSummaryKind.Pipe,
|
||||||
|
type: this.type,
|
||||||
|
name: this.name,
|
||||||
|
pure: this.pure
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompileNgModuleInjectorSummary extends CompileSummary {
|
// Note: This should only use interfaces as nested data types
|
||||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
// as we need to be able to serialize this from/to JSON!
|
||||||
|
export interface CompileNgModuleSummary extends CompileTypeSummary {
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
entryComponents: CompileIdentifierMetadata[];
|
|
||||||
providers: CompileProviderMetadata[];
|
|
||||||
importedModules: CompileNgModuleInjectorSummary[];
|
|
||||||
exportedModules: CompileNgModuleInjectorSummary[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CompileNgModuleDirectiveSummary extends CompileSummary {
|
// Note: This is transitive over the exported modules.
|
||||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
|
||||||
type: CompileTypeMetadata;
|
|
||||||
exportedDirectives: CompileIdentifierMetadata[];
|
exportedDirectives: CompileIdentifierMetadata[];
|
||||||
|
// Note: This is transitive over the exported modules.
|
||||||
exportedPipes: CompileIdentifierMetadata[];
|
exportedPipes: CompileIdentifierMetadata[];
|
||||||
exportedModules: CompileNgModuleDirectiveSummary[];
|
|
||||||
directiveLoaders: (() => Promise<void>)[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CompileNgModuleSummary =
|
// Note: This is transitive.
|
||||||
CompileNgModuleInjectorSummary & CompileNgModuleDirectiveSummary;
|
entryComponents: CompileIdentifierMetadata[];
|
||||||
|
// Note: This is transitive.
|
||||||
|
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[];
|
||||||
|
// Note: This is transitive.
|
||||||
|
modules: CompileTypeMetadata[];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metadata regarding compilation of a module.
|
* Metadata regarding compilation of a module.
|
||||||
*/
|
*/
|
||||||
export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
export class CompileNgModuleMetadata {
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
declaredDirectives: CompileIdentifierMetadata[];
|
declaredDirectives: CompileIdentifierMetadata[];
|
||||||
exportedDirectives: CompileIdentifierMetadata[];
|
exportedDirectives: CompileIdentifierMetadata[];
|
||||||
declaredPipes: CompileIdentifierMetadata[];
|
declaredPipes: CompileIdentifierMetadata[];
|
||||||
|
|
||||||
exportedPipes: CompileIdentifierMetadata[];
|
exportedPipes: CompileIdentifierMetadata[];
|
||||||
entryComponents: CompileIdentifierMetadata[];
|
entryComponents: CompileIdentifierMetadata[];
|
||||||
bootstrapComponents: CompileIdentifierMetadata[];
|
bootstrapComponents: CompileIdentifierMetadata[];
|
||||||
@ -620,8 +550,7 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
|||||||
entryComponents, bootstrapComponents, importedModules, exportedModules, schemas,
|
entryComponents, bootstrapComponents, importedModules, exportedModules, schemas,
|
||||||
transitiveModule, id}: {
|
transitiveModule, id}: {
|
||||||
type?: CompileTypeMetadata,
|
type?: CompileTypeMetadata,
|
||||||
providers?:
|
providers?: CompileProviderMetadata[],
|
||||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
|
||||||
declaredDirectives?: CompileIdentifierMetadata[],
|
declaredDirectives?: CompileIdentifierMetadata[],
|
||||||
exportedDirectives?: CompileIdentifierMetadata[],
|
exportedDirectives?: CompileIdentifierMetadata[],
|
||||||
declaredPipes?: CompileIdentifierMetadata[],
|
declaredPipes?: CompileIdentifierMetadata[],
|
||||||
@ -649,69 +578,75 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
|||||||
this.transitiveModule = transitiveModule;
|
this.transitiveModule = transitiveModule;
|
||||||
}
|
}
|
||||||
|
|
||||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
|
||||||
|
|
||||||
toSummary(): CompileNgModuleSummary {
|
toSummary(): CompileNgModuleSummary {
|
||||||
return {
|
return {
|
||||||
isSummary: true,
|
summaryKind: CompileSummaryKind.NgModule,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
entryComponents: this.entryComponents,
|
entryComponents: this.transitiveModule.entryComponents,
|
||||||
providers: this.providers,
|
providers: this.transitiveModule.providers,
|
||||||
importedModules: this.importedModules,
|
modules: this.transitiveModule.modules,
|
||||||
exportedModules: this.exportedModules,
|
exportedDirectives: this.transitiveModule.exportedDirectives,
|
||||||
exportedDirectives: this.exportedDirectives,
|
exportedPipes: this.transitiveModule.exportedPipes
|
||||||
exportedPipes: this.exportedPipes,
|
|
||||||
directiveLoaders: this.transitiveModule.directiveLoaders
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
toInjectorSummary(): CompileNgModuleInjectorSummary {
|
|
||||||
return {
|
|
||||||
isSummary: true,
|
|
||||||
type: this.type,
|
|
||||||
entryComponents: this.entryComponents,
|
|
||||||
providers: this.providers,
|
|
||||||
importedModules: this.importedModules,
|
|
||||||
exportedModules: this.exportedModules
|
|
||||||
};
|
|
||||||
}
|
|
||||||
toDirectiveSummary(): CompileNgModuleDirectiveSummary {
|
|
||||||
return {
|
|
||||||
isSummary: true,
|
|
||||||
type: this.type,
|
|
||||||
exportedDirectives: this.exportedDirectives,
|
|
||||||
exportedPipes: this.exportedPipes,
|
|
||||||
exportedModules: this.exportedModules,
|
|
||||||
directiveLoaders: this.transitiveModule.directiveLoaders
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TransitiveCompileNgModuleMetadata {
|
export class TransitiveCompileNgModuleMetadata {
|
||||||
directivesSet = new Set<any>();
|
directivesSet = new Set<any>();
|
||||||
|
directives: CompileIdentifierMetadata[] = [];
|
||||||
|
exportedDirectivesSet = new Set<any>();
|
||||||
|
exportedDirectives: CompileIdentifierMetadata[] = [];
|
||||||
pipesSet = new Set<any>();
|
pipesSet = new Set<any>();
|
||||||
|
pipes: CompileIdentifierMetadata[] = [];
|
||||||
|
exportedPipesSet = new Set<any>();
|
||||||
|
exportedPipes: CompileIdentifierMetadata[] = [];
|
||||||
|
modulesSet = new Set<any>();
|
||||||
|
modules: CompileTypeMetadata[] = [];
|
||||||
|
entryComponentsSet = new Set<any>();
|
||||||
|
entryComponents: CompileIdentifierMetadata[] = [];
|
||||||
|
|
||||||
constructor(
|
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[] = [];
|
||||||
public modules: CompileNgModuleInjectorSummary[], public providers: CompileProviderMetadata[],
|
|
||||||
public entryComponents: CompileIdentifierMetadata[],
|
addProvider(provider: CompileProviderMetadata, module: CompileIdentifierMetadata) {
|
||||||
public directives: CompileIdentifierMetadata[], public pipes: CompileIdentifierMetadata[],
|
this.providers.push({provider: provider, module: module});
|
||||||
public directiveLoaders: (() => Promise<void>)[]) {
|
|
||||||
directives.forEach(dir => this.directivesSet.add(dir.reference));
|
|
||||||
pipes.forEach(pipe => this.pipesSet.add(pipe.reference));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export function removeIdentifierDuplicates<T extends CompileMetadataWithIdentifier>(items: T[]):
|
addDirective(id: CompileIdentifierMetadata) {
|
||||||
T[] {
|
if (!this.directivesSet.has(id.reference)) {
|
||||||
const map = new Map<any, T>();
|
this.directivesSet.add(id.reference);
|
||||||
|
this.directives.push(id);
|
||||||
items.forEach((item) => {
|
}
|
||||||
if (!map.get(item.identifier.reference)) {
|
}
|
||||||
map.set(item.identifier.reference, item);
|
addExportedDirective(id: CompileIdentifierMetadata) {
|
||||||
|
if (!this.exportedDirectivesSet.has(id.reference)) {
|
||||||
|
this.exportedDirectivesSet.add(id.reference);
|
||||||
|
this.exportedDirectives.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addPipe(id: CompileIdentifierMetadata) {
|
||||||
|
if (!this.pipesSet.has(id.reference)) {
|
||||||
|
this.pipesSet.add(id.reference);
|
||||||
|
this.pipes.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addExportedPipe(id: CompileIdentifierMetadata) {
|
||||||
|
if (!this.exportedPipesSet.has(id.reference)) {
|
||||||
|
this.exportedPipesSet.add(id.reference);
|
||||||
|
this.exportedPipes.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addModule(id: CompileTypeMetadata) {
|
||||||
|
if (!this.modulesSet.has(id.reference)) {
|
||||||
|
this.modulesSet.add(id.reference);
|
||||||
|
this.modules.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addEntryComponent(id: CompileIdentifierMetadata) {
|
||||||
|
if (!this.entryComponentsSet.has(id.reference)) {
|
||||||
|
this.entryComponentsSet.add(id.reference);
|
||||||
|
this.entryComponents.push(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return Array.from(map.values());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _normalizeArray(obj: any[]): any[] {
|
function _normalizeArray(obj: any[]): any[] {
|
||||||
|
@ -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 {Identifiers, resolveIdentifier} from '../identifiers';
|
import {Identifiers, createIdentifier} from '../identifiers';
|
||||||
import {ClassBuilder} from '../output/class_builder';
|
import {ClassBuilder} from '../output/class_builder';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ export function createCheckBindingField(builder: ClassBuilder): CheckBindingFiel
|
|||||||
// 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.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED)))
|
.set(o.importExpr(createIdentifier(Identifiers.UNINITIALIZED)))
|
||||||
.toStmt());
|
.toStmt());
|
||||||
return new CheckBindingField(fieldExpr, bindingId);
|
return new CheckBindingField(fieldExpr, bindingId);
|
||||||
}
|
}
|
||||||
@ -30,7 +30,7 @@ export function createCheckBindingField(builder: ClassBuilder): CheckBindingFiel
|
|||||||
export function createCheckBindingStmt(
|
export function createCheckBindingStmt(
|
||||||
evalResult: ConvertPropertyBindingResult, fieldExpr: o.ReadPropExpr,
|
evalResult: ConvertPropertyBindingResult, fieldExpr: o.ReadPropExpr,
|
||||||
throwOnChangeVar: o.Expression, actions: o.Statement[]): o.Statement[] {
|
throwOnChangeVar: o.Expression, actions: o.Statement[]): o.Statement[] {
|
||||||
let condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([
|
let condition: o.Expression = o.importExpr(createIdentifier(Identifiers.checkBinding)).callFn([
|
||||||
throwOnChangeVar, fieldExpr, evalResult.currValExpr
|
throwOnChangeVar, fieldExpr, evalResult.currValExpr
|
||||||
]);
|
]);
|
||||||
if (evalResult.forceUpdate) {
|
if (evalResult.forceUpdate) {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import * as cdAst from '../expression_parser/ast';
|
import * as cdAst from '../expression_parser/ast';
|
||||||
import {isBlank, isPresent} from '../facade/lang';
|
import {isBlank, isPresent} from '../facade/lang';
|
||||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
import {Identifiers, createIdentifier} from '../identifiers';
|
||||||
import {ClassBuilder} from '../output/class_builder';
|
import {ClassBuilder} from '../output/class_builder';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ export function createSharedBindingVariablesIfNeeded(stmts: o.Statement[]): o.St
|
|||||||
if (readVars.has(VAL_UNWRAPPER_VAR.name)) {
|
if (readVars.has(VAL_UNWRAPPER_VAR.name)) {
|
||||||
unwrapperStmts.push(
|
unwrapperStmts.push(
|
||||||
VAL_UNWRAPPER_VAR
|
VAL_UNWRAPPER_VAR
|
||||||
.set(o.importExpr(resolveIdentifier(Identifiers.ValueUnwrapper)).instantiate([]))
|
.set(o.importExpr(createIdentifier(Identifiers.ValueUnwrapper)).instantiate([]))
|
||||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||||
}
|
}
|
||||||
return unwrapperStmts;
|
return unwrapperStmts;
|
||||||
@ -277,16 +277,21 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
args.push(o.literal(ast.strings[ast.strings.length - 1]));
|
args.push(o.literal(ast.strings[ast.strings.length - 1]));
|
||||||
|
|
||||||
return ast.expressions.length <= 9 ?
|
return ast.expressions.length <= 9 ?
|
||||||
o.importExpr(resolveIdentifier(Identifiers.inlineInterpolate)).callFn(args) :
|
o.importExpr(createIdentifier(Identifiers.inlineInterpolate)).callFn(args) :
|
||||||
o.importExpr(resolveIdentifier(Identifiers.interpolate)).callFn([
|
o.importExpr(createIdentifier(Identifiers.interpolate)).callFn([
|
||||||
args[0], o.literalArr(args.slice(1))
|
args[0], o.literalArr(args.slice(1))
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitKeyedRead(ast: cdAst.KeyedRead, mode: _Mode): any {
|
visitKeyedRead(ast: cdAst.KeyedRead, mode: _Mode): any {
|
||||||
|
const leftMostSafe = this.leftMostSafeNode(ast);
|
||||||
|
if (leftMostSafe) {
|
||||||
|
return this.convertSafeAccess(ast, leftMostSafe, mode);
|
||||||
|
} else {
|
||||||
return convertToStatementIfNeeded(
|
return convertToStatementIfNeeded(
|
||||||
mode, this.visit(ast.obj, _Mode.Expression).key(this.visit(ast.key, _Mode.Expression)));
|
mode, this.visit(ast.obj, _Mode.Expression).key(this.visit(ast.key, _Mode.Expression)));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
visitKeyedWrite(ast: cdAst.KeyedWrite, mode: _Mode): any {
|
visitKeyedWrite(ast: cdAst.KeyedWrite, mode: _Mode): any {
|
||||||
const obj: o.Expression = this.visit(ast.obj, _Mode.Expression);
|
const obj: o.Expression = this.visit(ast.obj, _Mode.Expression);
|
||||||
@ -578,7 +583,7 @@ function flattenStatements(arg: any, output: o.Statement[]) {
|
|||||||
|
|
||||||
function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[]): o.Expression {
|
function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[]): o.Expression {
|
||||||
if (values.length === 0) {
|
if (values.length === 0) {
|
||||||
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_ARRAY));
|
return o.importExpr(createIdentifier(Identifiers.EMPTY_ARRAY));
|
||||||
}
|
}
|
||||||
const proxyExpr = o.THIS_EXPR.prop(`_arr_${builder.fields.length}`);
|
const proxyExpr = o.THIS_EXPR.prop(`_arr_${builder.fields.length}`);
|
||||||
const proxyParams: o.FnParam[] = [];
|
const proxyParams: o.FnParam[] = [];
|
||||||
@ -599,7 +604,7 @@ function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[])
|
|||||||
function createCachedLiteralMap(
|
function createCachedLiteralMap(
|
||||||
builder: ClassBuilder, entries: [string, o.Expression][]): o.Expression {
|
builder: ClassBuilder, entries: [string, o.Expression][]): o.Expression {
|
||||||
if (entries.length === 0) {
|
if (entries.length === 0) {
|
||||||
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_MAP));
|
return o.importExpr(createIdentifier(Identifiers.EMPTY_MAP));
|
||||||
}
|
}
|
||||||
const proxyExpr = o.THIS_EXPR.prop(`_map_${builder.fields.length}`);
|
const proxyExpr = o.THIS_EXPR.prop(`_map_${builder.fields.length}`);
|
||||||
const proxyParams: o.FnParam[] = [];
|
const proxyParams: o.FnParam[] = [];
|
||||||
|
@ -8,15 +8,12 @@
|
|||||||
|
|
||||||
import {CompileTokenMetadata} from '../compile_metadata';
|
import {CompileTokenMetadata} from '../compile_metadata';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {IdentifierSpec, Identifiers, resolveEnumIdentifier, resolveIdentifier} from '../identifiers';
|
import {IdentifierSpec, Identifiers, createEnumIdentifier, createIdentifier} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
|
|
||||||
export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
|
export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
|
||||||
if (isPresent(token.value)) {
|
if (isPresent(token.value)) {
|
||||||
return o.literal(token.value);
|
return o.literal(token.value);
|
||||||
} else if (token.identifierIsInstance) {
|
|
||||||
return o.importExpr(token.identifier)
|
|
||||||
.instantiate([], o.importType(token.identifier, [], [o.TypeModifier.Const]));
|
|
||||||
} else {
|
} else {
|
||||||
return o.importExpr(token.identifier);
|
return o.importExpr(token.identifier);
|
||||||
}
|
}
|
||||||
@ -24,13 +21,13 @@ export function createDiTokenExpression(token: CompileTokenMetadata): o.Expressi
|
|||||||
|
|
||||||
export function createInlineArray(values: o.Expression[]): o.Expression {
|
export function createInlineArray(values: o.Expression[]): o.Expression {
|
||||||
if (values.length === 0) {
|
if (values.length === 0) {
|
||||||
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_INLINE_ARRAY));
|
return o.importExpr(createIdentifier(Identifiers.EMPTY_INLINE_ARRAY));
|
||||||
}
|
}
|
||||||
const log2 = Math.log(values.length) / Math.log(2);
|
const log2 = Math.log(values.length) / Math.log(2);
|
||||||
const index = Math.ceil(log2);
|
const index = Math.ceil(log2);
|
||||||
const identifierSpec = index < Identifiers.inlineArrays.length ? Identifiers.inlineArrays[index] :
|
const identifierSpec = index < Identifiers.inlineArrays.length ? Identifiers.inlineArrays[index] :
|
||||||
Identifiers.InlineArrayDynamic;
|
Identifiers.InlineArrayDynamic;
|
||||||
const identifier = resolveIdentifier(identifierSpec);
|
const identifier = createIdentifier(identifierSpec);
|
||||||
return o.importExpr(identifier).instantiate([
|
return o.importExpr(identifier).instantiate([
|
||||||
<o.Expression>o.literal(values.length)
|
<o.Expression>o.literal(values.length)
|
||||||
].concat(values));
|
].concat(values));
|
||||||
@ -46,7 +43,7 @@ export function createPureProxy(
|
|||||||
throw new Error(`Unsupported number of argument for pure functions: ${argCount}`);
|
throw new Error(`Unsupported number of argument for pure functions: ${argCount}`);
|
||||||
}
|
}
|
||||||
builder.ctorStmts.push(o.THIS_EXPR.prop(pureProxyProp.name)
|
builder.ctorStmts.push(o.THIS_EXPR.prop(pureProxyProp.name)
|
||||||
.set(o.importExpr(resolveIdentifier(pureProxyId)).callFn([fn]))
|
.set(o.importExpr(createIdentifier(pureProxyId)).callFn([fn]))
|
||||||
.toStmt());
|
.toStmt());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,5 +53,5 @@ export function createEnumExpression(enumType: IdentifierSpec, enumValue: any):
|
|||||||
if (!enumName) {
|
if (!enumName) {
|
||||||
throw new Error(`Unknown enum value ${enumValue} in ${enumType.name}`);
|
throw new Error(`Unknown enum value ${enumValue} in ${enumType.name}`);
|
||||||
}
|
}
|
||||||
return o.importExpr(resolveEnumIdentifier(resolveIdentifier(enumType), enumName));
|
return o.importExpr(createEnumIdentifier(enumType, enumName));
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
import {SecurityContext} from '@angular/core';
|
import {SecurityContext} from '@angular/core';
|
||||||
|
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {Identifiers, resolveIdentifier} 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';
|
||||||
@ -26,7 +26,7 @@ export function writeToRenderer(
|
|||||||
case PropertyBindingType.Property:
|
case PropertyBindingType.Property:
|
||||||
if (logBindingUpdate) {
|
if (logBindingUpdate) {
|
||||||
updateStmts.push(
|
updateStmts.push(
|
||||||
o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfo))
|
o.importExpr(createIdentifier(Identifiers.setBindingDebugInfo))
|
||||||
.callFn([renderer, renderElement, o.literal(boundProp.name), renderValue])
|
.callFn([renderer, renderElement, o.literal(boundProp.name), renderValue])
|
||||||
.toStmt());
|
.toStmt());
|
||||||
}
|
}
|
||||||
@ -91,8 +91,8 @@ function sanitizedValue(
|
|||||||
|
|
||||||
export function triggerAnimation(
|
export function triggerAnimation(
|
||||||
view: o.Expression, componentView: o.Expression, boundProp: BoundElementPropertyAst,
|
view: o.Expression, componentView: o.Expression, boundProp: BoundElementPropertyAst,
|
||||||
eventListener: o.Expression, renderElement: o.Expression, renderValue: o.Expression,
|
boundOutputs: BoundEventAst[], eventListener: o.Expression, renderElement: o.Expression,
|
||||||
lastRenderValue: o.Expression) {
|
renderValue: o.Expression, lastRenderValue: o.Expression) {
|
||||||
const detachStmts: o.Statement[] = [];
|
const detachStmts: o.Statement[] = [];
|
||||||
const updateStmts: o.Statement[] = [];
|
const updateStmts: o.Statement[] = [];
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ 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(resolveIdentifier(Identifiers.UNINITIALIZED));
|
const unitializedValue = o.importExpr(createIdentifier(Identifiers.UNINITIALIZED));
|
||||||
const animationTransitionVar = o.variable('animationTransition_' + animationName);
|
const animationTransitionVar = o.variable('animationTransition_' + animationName);
|
||||||
|
|
||||||
updateStmts.push(
|
updateStmts.push(
|
||||||
@ -121,23 +121,32 @@ export function triggerAnimation(
|
|||||||
.set(animationFnExpr.callFn([view, renderElement, lastRenderValue, emptyStateValue]))
|
.set(animationFnExpr.callFn([view, renderElement, lastRenderValue, emptyStateValue]))
|
||||||
.toDeclStmt());
|
.toDeclStmt());
|
||||||
|
|
||||||
const registerStmts = [
|
const registerStmts: o.Statement[] = [];
|
||||||
|
const animationStartMethodExists = boundOutputs.find(
|
||||||
|
event => event.isAnimation && event.name == animationName && event.phase == 'start');
|
||||||
|
if (animationStartMethodExists) {
|
||||||
|
registerStmts.push(
|
||||||
animationTransitionVar
|
animationTransitionVar
|
||||||
.callMethod(
|
.callMethod(
|
||||||
'onStart',
|
'onStart',
|
||||||
[eventListener.callMethod(
|
[eventListener.callMethod(
|
||||||
o.BuiltinMethod.Bind,
|
o.BuiltinMethod.Bind,
|
||||||
[view, o.literal(BoundEventAst.calcFullName(animationName, null, 'start'))])])
|
[view, o.literal(BoundEventAst.calcFullName(animationName, null, 'start'))])])
|
||||||
.toStmt(),
|
.toStmt());
|
||||||
|
}
|
||||||
|
|
||||||
|
const animationDoneMethodExists = boundOutputs.find(
|
||||||
|
event => event.isAnimation && event.name == animationName && event.phase == 'done');
|
||||||
|
if (animationDoneMethodExists) {
|
||||||
|
registerStmts.push(
|
||||||
animationTransitionVar
|
animationTransitionVar
|
||||||
.callMethod(
|
.callMethod(
|
||||||
'onDone',
|
'onDone',
|
||||||
[eventListener.callMethod(
|
[eventListener.callMethod(
|
||||||
o.BuiltinMethod.Bind,
|
o.BuiltinMethod.Bind,
|
||||||
[view, o.literal(BoundEventAst.calcFullName(animationName, null, 'done'))])])
|
[view, o.literal(BoundEventAst.calcFullName(animationName, null, 'done'))])])
|
||||||
.toStmt(),
|
.toStmt());
|
||||||
|
}
|
||||||
];
|
|
||||||
|
|
||||||
updateStmts.push(...registerStmts);
|
updateStmts.push(...registerStmts);
|
||||||
detachStmts.push(...registerStmts);
|
detachStmts.push(...registerStmts);
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {ViewEncapsulation, isDevMode} from '@angular/core';
|
import {ViewEncapsulation, isDevMode} from '@angular/core';
|
||||||
|
|
||||||
import {CompileIdentifierMetadata} from './compile_metadata';
|
import {CompileIdentifierMetadata} from './compile_metadata';
|
||||||
import {Identifiers, resolveIdentifier} from './identifiers';
|
import {Identifiers, createIdentifier} from './identifiers';
|
||||||
|
|
||||||
function unimplemented(): any {
|
function unimplemented(): any {
|
||||||
throw new Error('unimplemented');
|
throw new Error('unimplemented');
|
||||||
@ -61,7 +61,7 @@ export abstract class RenderTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class DefaultRenderTypes implements RenderTypes {
|
export class DefaultRenderTypes implements RenderTypes {
|
||||||
get renderer() { return resolveIdentifier(Identifiers.Renderer); };
|
get renderer() { return createIdentifier(Identifiers.Renderer); };
|
||||||
renderText: any = null;
|
renderText: any = null;
|
||||||
renderElement: any = null;
|
renderElement: any = null;
|
||||||
renderComment: any = null;
|
renderComment: any = null;
|
||||||
|
@ -8,11 +8,12 @@
|
|||||||
|
|
||||||
import {Component, Directive, HostBinding, HostListener, Injectable, Input, Output, Query, Type, resolveForwardRef} from '@angular/core';
|
import {Component, Directive, HostBinding, HostListener, Injectable, Input, Output, Query, Type, resolveForwardRef} from '@angular/core';
|
||||||
|
|
||||||
import {StringMapWrapper} from './facade/collection';
|
import {ListWrapper, StringMapWrapper} from './facade/collection';
|
||||||
import {stringify} from './facade/lang';
|
import {stringify} from './facade/lang';
|
||||||
import {ReflectorReader, reflector} from './private_import_core';
|
import {ReflectorReader, reflector} from './private_import_core';
|
||||||
import {splitAtColon} from './util';
|
import {splitAtColon} from './util';
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Resolve a `Type` for {@link Directive}.
|
* Resolve a `Type` for {@link Directive}.
|
||||||
*
|
*
|
||||||
@ -35,7 +36,7 @@ export class DirectiveResolver {
|
|||||||
resolve(type: Type<any>, throwIfNotFound = true): Directive {
|
resolve(type: Type<any>, throwIfNotFound = true): Directive {
|
||||||
const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
||||||
if (typeMetadata) {
|
if (typeMetadata) {
|
||||||
const metadata = typeMetadata.find(isDirectiveMetadata);
|
const metadata = ListWrapper.findLast(typeMetadata, isDirectiveMetadata);
|
||||||
if (metadata) {
|
if (metadata) {
|
||||||
const propertyMetadata = this._reflector.propMetadata(type);
|
const propertyMetadata = this._reflector.propMetadata(type);
|
||||||
return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type);
|
return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type);
|
||||||
@ -58,23 +59,24 @@ export class DirectiveResolver {
|
|||||||
const queries: {[key: string]: any} = {};
|
const queries: {[key: string]: any} = {};
|
||||||
|
|
||||||
Object.keys(propertyMetadata).forEach((propName: string) => {
|
Object.keys(propertyMetadata).forEach((propName: string) => {
|
||||||
|
const input = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Input);
|
||||||
propertyMetadata[propName].forEach(a => {
|
if (input) {
|
||||||
if (a instanceof Input) {
|
if (input.bindingPropertyName) {
|
||||||
if (a.bindingPropertyName) {
|
inputs.push(`${propName}: ${input.bindingPropertyName}`);
|
||||||
inputs.push(`${propName}: ${a.bindingPropertyName}`);
|
|
||||||
} else {
|
} else {
|
||||||
inputs.push(propName);
|
inputs.push(propName);
|
||||||
}
|
}
|
||||||
} else if (a instanceof Output) {
|
}
|
||||||
const output: Output = a;
|
const output = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Output);
|
||||||
|
if (output) {
|
||||||
if (output.bindingPropertyName) {
|
if (output.bindingPropertyName) {
|
||||||
outputs.push(`${propName}: ${output.bindingPropertyName}`);
|
outputs.push(`${propName}: ${output.bindingPropertyName}`);
|
||||||
} else {
|
} else {
|
||||||
outputs.push(propName);
|
outputs.push(propName);
|
||||||
}
|
}
|
||||||
} else if (a instanceof HostBinding) {
|
}
|
||||||
const hostBinding: HostBinding = a;
|
const hostBindings = propertyMetadata[propName].filter(a => a && a instanceof HostBinding);
|
||||||
|
hostBindings.forEach(hostBinding => {
|
||||||
if (hostBinding.hostPropertyName) {
|
if (hostBinding.hostPropertyName) {
|
||||||
const startWith = hostBinding.hostPropertyName[0];
|
const startWith = hostBinding.hostPropertyName[0];
|
||||||
if (startWith === '(') {
|
if (startWith === '(') {
|
||||||
@ -87,56 +89,44 @@ export class DirectiveResolver {
|
|||||||
} else {
|
} else {
|
||||||
host[`[${propName}]`] = propName;
|
host[`[${propName}]`] = propName;
|
||||||
}
|
}
|
||||||
} else if (a instanceof HostListener) {
|
});
|
||||||
const hostListener: HostListener = a;
|
const hostListeners = propertyMetadata[propName].filter(a => a && a instanceof HostListener);
|
||||||
|
hostListeners.forEach(hostListener => {
|
||||||
const args = hostListener.args || [];
|
const args = hostListener.args || [];
|
||||||
host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`;
|
host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`;
|
||||||
} else if (a instanceof Query) {
|
|
||||||
queries[propName] = a;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
const query = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Query);
|
||||||
|
if (query) {
|
||||||
|
queries[propName] = query;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return this._merge(dm, inputs, outputs, host, queries, directiveType);
|
return this._merge(dm, inputs, outputs, host, queries, directiveType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _extractPublicName(def: string) { return splitAtColon(def, [null, def])[1].trim(); }
|
private _extractPublicName(def: string) { return splitAtColon(def, [null, def])[1].trim(); }
|
||||||
|
|
||||||
|
private _dedupeBindings(bindings: string[]): string[] {
|
||||||
|
const names = new Set<string>();
|
||||||
|
const reversedResult: string[] = [];
|
||||||
|
// go last to first to allow later entries to overwrite previous entries
|
||||||
|
for (let i = bindings.length - 1; i >= 0; i--) {
|
||||||
|
const binding = bindings[i];
|
||||||
|
const name = this._extractPublicName(binding);
|
||||||
|
if (!names.has(name)) {
|
||||||
|
names.add(name);
|
||||||
|
reversedResult.push(binding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reversedResult.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
private _merge(
|
private _merge(
|
||||||
directive: Directive, inputs: string[], outputs: string[], host: {[key: string]: string},
|
directive: Directive, inputs: string[], outputs: string[], host: {[key: string]: string},
|
||||||
queries: {[key: string]: any}, directiveType: Type<any>): Directive {
|
queries: {[key: string]: any}, directiveType: Type<any>): Directive {
|
||||||
const mergedInputs: string[] = inputs;
|
const mergedInputs =
|
||||||
|
this._dedupeBindings(directive.inputs ? directive.inputs.concat(inputs) : inputs);
|
||||||
if (directive.inputs) {
|
const mergedOutputs =
|
||||||
const inputNames: string[] =
|
this._dedupeBindings(directive.outputs ? directive.outputs.concat(outputs) : outputs);
|
||||||
directive.inputs.map((def: string): string => this._extractPublicName(def));
|
|
||||||
|
|
||||||
inputs.forEach((inputDef: string) => {
|
|
||||||
const publicName = this._extractPublicName(inputDef);
|
|
||||||
if (inputNames.indexOf(publicName) > -1) {
|
|
||||||
throw new Error(
|
|
||||||
`Input '${publicName}' defined multiple times in '${stringify(directiveType)}'`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mergedInputs.unshift(...directive.inputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mergedOutputs: string[] = outputs;
|
|
||||||
|
|
||||||
if (directive.outputs) {
|
|
||||||
const outputNames: string[] =
|
|
||||||
directive.outputs.map((def: string): string => this._extractPublicName(def));
|
|
||||||
|
|
||||||
outputs.forEach((outputDef: string) => {
|
|
||||||
const publicName = this._extractPublicName(outputDef);
|
|
||||||
if (outputNames.indexOf(publicName) > -1) {
|
|
||||||
throw new Error(
|
|
||||||
`Output event '${publicName}' defined multiple times in '${stringify(directiveType)}'`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mergedOutputs.unshift(...directive.outputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mergedHost = directive.host ? StringMapWrapper.merge(directive.host, host) : host;
|
const mergedHost = directive.host ? StringMapWrapper.merge(directive.host, host) : host;
|
||||||
const mergedQueries =
|
const mergedQueries =
|
||||||
directive.queries ? StringMapWrapper.merge(directive.queries, queries) : queries;
|
directive.queries ? StringMapWrapper.merge(directive.queries, queries) : queries;
|
||||||
|
@ -8,13 +8,13 @@
|
|||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata} from './compile_metadata';
|
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
|
||||||
import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util';
|
import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util';
|
||||||
import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
|
import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
|
||||||
import {triggerAnimation, writeToRenderer} from './compiler_util/render_util';
|
import {triggerAnimation, writeToRenderer} from './compiler_util/render_util';
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
import {Parser} from './expression_parser/parser';
|
import {Parser} from './expression_parser/parser';
|
||||||
import {Identifiers, resolveIdentifier} from './identifiers';
|
import {Identifiers, createIdentifier} from './identifiers';
|
||||||
import {DEFAULT_INTERPOLATION_CONFIG} from './ml_parser/interpolation_config';
|
import {DEFAULT_INTERPOLATION_CONFIG} from './ml_parser/interpolation_config';
|
||||||
import {ClassBuilder, createClassStmt} from './output/class_builder';
|
import {ClassBuilder, createClassStmt} from './output/class_builder';
|
||||||
import * as o from './output/output_ast';
|
import * as o from './output/output_ast';
|
||||||
@ -53,7 +53,9 @@ const RESET_CHANGES_STMT = o.THIS_EXPR.prop(CHANGES_FIELD_NAME).set(o.literalMap
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DirectiveWrapperCompiler {
|
export class DirectiveWrapperCompiler {
|
||||||
static dirWrapperClassName(id: CompileIdentifierMetadata) { return `Wrapper_${id.name}`; }
|
static dirWrapperClassName(id: CompileIdentifierMetadata) {
|
||||||
|
return `Wrapper_${identifierName(id)}`;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private compilerConfig: CompilerConfig, private _exprParser: Parser,
|
private compilerConfig: CompilerConfig, private _exprParser: Parser,
|
||||||
@ -68,7 +70,7 @@ export class DirectiveWrapperCompiler {
|
|||||||
addCheckInputMethod(inputFieldName, builder);
|
addCheckInputMethod(inputFieldName, builder);
|
||||||
});
|
});
|
||||||
addNgDoCheckMethod(builder);
|
addNgDoCheckMethod(builder);
|
||||||
addCheckHostMethod(hostParseResult.hostProps, builder);
|
addCheckHostMethod(hostParseResult.hostProps, hostParseResult.hostListeners, builder);
|
||||||
addHandleEventMethod(hostParseResult.hostListeners, builder);
|
addHandleEventMethod(hostParseResult.hostListeners, builder);
|
||||||
addSubscribeMethod(dirMeta, builder);
|
addSubscribeMethod(dirMeta, builder);
|
||||||
|
|
||||||
@ -117,10 +119,10 @@ class DirectiveWrapperBuilder implements ClassBuilder {
|
|||||||
[
|
[
|
||||||
new o.FnParam(
|
new o.FnParam(
|
||||||
VIEW_VAR.name,
|
VIEW_VAR.name,
|
||||||
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
new o.FnParam(
|
new o.FnParam(
|
||||||
COMPONENT_VIEW_VAR.name,
|
COMPONENT_VIEW_VAR.name,
|
||||||
o.importType(resolveIdentifier(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),
|
||||||
],
|
],
|
||||||
this.detachStmts),
|
this.detachStmts),
|
||||||
@ -172,7 +174,7 @@ function addNgDoCheckMethod(builder: DirectiveWrapperBuilder) {
|
|||||||
}
|
}
|
||||||
if (builder.compilerConfig.logBindingUpdate) {
|
if (builder.compilerConfig.logBindingUpdate) {
|
||||||
onChangesStmts.push(
|
onChangesStmts.push(
|
||||||
o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfoForChanges))
|
o.importExpr(createIdentifier(Identifiers.setBindingDebugInfoForChanges))
|
||||||
.callFn(
|
.callFn(
|
||||||
[VIEW_VAR.prop('renderer'), RENDER_EL_VAR, o.THIS_EXPR.prop(CHANGES_FIELD_NAME)])
|
[VIEW_VAR.prop('renderer'), RENDER_EL_VAR, o.THIS_EXPR.prop(CHANGES_FIELD_NAME)])
|
||||||
.toStmt());
|
.toStmt());
|
||||||
@ -198,7 +200,7 @@ function addNgDoCheckMethod(builder: DirectiveWrapperBuilder) {
|
|||||||
'ngDoCheck',
|
'ngDoCheck',
|
||||||
[
|
[
|
||||||
new o.FnParam(
|
new o.FnParam(
|
||||||
VIEW_VAR.name, o.importType(resolveIdentifier(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),
|
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
|
||||||
],
|
],
|
||||||
@ -214,7 +216,7 @@ function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) {
|
|||||||
if (builder.genChanges) {
|
if (builder.genChanges) {
|
||||||
onChangeStatements.push(o.THIS_EXPR.prop(CHANGES_FIELD_NAME)
|
onChangeStatements.push(o.THIS_EXPR.prop(CHANGES_FIELD_NAME)
|
||||||
.key(o.literal(input))
|
.key(o.literal(input))
|
||||||
.set(o.importExpr(resolveIdentifier(Identifiers.SimpleChange))
|
.set(o.importExpr(createIdentifier(Identifiers.SimpleChange))
|
||||||
.instantiate([field.expression, CURR_VALUE_VAR]))
|
.instantiate([field.expression, CURR_VALUE_VAR]))
|
||||||
.toStmt());
|
.toStmt());
|
||||||
}
|
}
|
||||||
@ -233,14 +235,15 @@ function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addCheckHostMethod(
|
function addCheckHostMethod(
|
||||||
hostProps: BoundElementPropertyAst[], builder: DirectiveWrapperBuilder) {
|
hostProps: BoundElementPropertyAst[], hostEvents: BoundEventAst[],
|
||||||
|
builder: DirectiveWrapperBuilder) {
|
||||||
const stmts: o.Statement[] = [];
|
const stmts: o.Statement[] = [];
|
||||||
const methodParams: o.FnParam[] = [
|
const methodParams: o.FnParam[] = [
|
||||||
new o.FnParam(
|
new o.FnParam(
|
||||||
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
VIEW_VAR.name, o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
new o.FnParam(
|
new o.FnParam(
|
||||||
COMPONENT_VIEW_VAR.name,
|
COMPONENT_VIEW_VAR.name,
|
||||||
o.importType(resolveIdentifier(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),
|
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
|
||||||
];
|
];
|
||||||
@ -255,14 +258,14 @@ function addCheckHostMethod(
|
|||||||
if (hostProp.needsRuntimeSecurityContext) {
|
if (hostProp.needsRuntimeSecurityContext) {
|
||||||
securityContextExpr = o.variable(`secCtx_${methodParams.length}`);
|
securityContextExpr = o.variable(`secCtx_${methodParams.length}`);
|
||||||
methodParams.push(new o.FnParam(
|
methodParams.push(new o.FnParam(
|
||||||
securityContextExpr.name, o.importType(resolveIdentifier(Identifiers.SecurityContext))));
|
securityContextExpr.name, o.importType(createIdentifier(Identifiers.SecurityContext))));
|
||||||
}
|
}
|
||||||
let checkBindingStmts: o.Statement[];
|
let checkBindingStmts: o.Statement[];
|
||||||
if (hostProp.isAnimation) {
|
if (hostProp.isAnimation) {
|
||||||
const {updateStmts, detachStmts} = triggerAnimation(
|
const {updateStmts, detachStmts} = triggerAnimation(
|
||||||
VIEW_VAR, COMPONENT_VIEW_VAR, hostProp,
|
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(resolveIdentifier(Identifiers.noop))),
|
.or(o.importExpr(createIdentifier(Identifiers.noop))),
|
||||||
RENDER_EL_VAR, evalResult.currValExpr, field.expression);
|
RENDER_EL_VAR, evalResult.currValExpr, field.expression);
|
||||||
checkBindingStmts = updateStmts;
|
checkBindingStmts = updateStmts;
|
||||||
builder.detachStmts.push(...detachStmts);
|
builder.detachStmts.push(...detachStmts);
|
||||||
@ -306,7 +309,7 @@ function addHandleEventMethod(hostListeners: BoundEventAst[], builder: Directive
|
|||||||
function addSubscribeMethod(dirMeta: CompileDirectiveMetadata, builder: DirectiveWrapperBuilder) {
|
function addSubscribeMethod(dirMeta: CompileDirectiveMetadata, builder: DirectiveWrapperBuilder) {
|
||||||
const methodParams: o.FnParam[] = [
|
const methodParams: o.FnParam[] = [
|
||||||
new o.FnParam(
|
new o.FnParam(
|
||||||
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
VIEW_VAR.name, o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||||
new o.FnParam(EVENT_HANDLER_FIELD_NAME, o.DYNAMIC_TYPE)
|
new o.FnParam(EVENT_HANDLER_FIELD_NAME, o.DYNAMIC_TYPE)
|
||||||
];
|
];
|
||||||
const stmts: o.Statement[] = [
|
const stmts: o.Statement[] = [
|
||||||
@ -348,9 +351,10 @@ function parseHostBindings(
|
|||||||
const errors: ParseError[] = [];
|
const errors: ParseError[] = [];
|
||||||
const parser =
|
const parser =
|
||||||
new BindingParser(exprParser, DEFAULT_INTERPOLATION_CONFIG, schemaRegistry, [], errors);
|
new BindingParser(exprParser, DEFAULT_INTERPOLATION_CONFIG, schemaRegistry, [], errors);
|
||||||
const sourceFileName = dirMeta.type.moduleUrl ?
|
const moduleUrl = identifierModuleUrl(dirMeta.type);
|
||||||
`in Directive ${dirMeta.type.name} in ${dirMeta.type.moduleUrl}` :
|
const sourceFileName = moduleUrl ?
|
||||||
`in Directive ${dirMeta.type.name}`;
|
`in Directive ${identifierName(dirMeta.type)} in ${moduleUrl}` :
|
||||||
|
`in Directive ${identifierName(dirMeta.type)}`;
|
||||||
const sourceFile = new ParseSourceFile('', sourceFileName);
|
const sourceFile = new ParseSourceFile('', sourceFileName);
|
||||||
const sourceSpan = new ParseSourceSpan(
|
const sourceSpan = new ParseSourceSpan(
|
||||||
new ParseLocation(sourceFile, null, null, null),
|
new ParseLocation(sourceFile, null, null, null),
|
||||||
|
@ -344,7 +344,7 @@ export class _ParseAST {
|
|||||||
while (this.optionalCharacter(chars.$COLON)) {
|
while (this.optionalCharacter(chars.$COLON)) {
|
||||||
args.push(this.parseExpression());
|
args.push(this.parseExpression());
|
||||||
}
|
}
|
||||||
result = new BindingPipe(this.span(result.span.start - this.offset), result, name, args);
|
result = new BindingPipe(this.span(result.span.start), result, name, args);
|
||||||
} while (this.optionalOperator('|'));
|
} while (this.optionalOperator('|'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,10 +8,16 @@
|
|||||||
|
|
||||||
import * as i18n from './i18n_ast';
|
import * as i18n from './i18n_ast';
|
||||||
|
|
||||||
export function digestMessage(message: i18n.Message): string {
|
export function digest(message: i18n.Message): string {
|
||||||
return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
|
return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function decimalDigest(message: i18n.Message): string {
|
||||||
|
const visitor = new _SerializerIgnoreIcuExpVisitor();
|
||||||
|
const parts = message.nodes.map(a => a.visit(visitor, null));
|
||||||
|
return computeMsgId(parts.join(''), message.meaning);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialize the i18n ast to something xml-like in order to generate an UID.
|
* Serialize the i18n ast to something xml-like in order to generate an UID.
|
||||||
*
|
*
|
||||||
@ -39,7 +45,7 @@ class _SerializerVisitor implements i18n.Visitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitPlaceholder(ph: i18n.Placeholder, context: any): any {
|
visitPlaceholder(ph: i18n.Placeholder, context: any): any {
|
||||||
return `<ph name="${ph.name}">${ph.value}</ph>`;
|
return ph.value ? `<ph name="${ph.name}">${ph.value}</ph>` : `<ph name="${ph.name}"/>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any {
|
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any {
|
||||||
@ -53,6 +59,21 @@ export function serializeNodes(nodes: i18n.Node[]): string[] {
|
|||||||
return nodes.map(a => a.visit(serializerVisitor, null));
|
return nodes.map(a => a.visit(serializerVisitor, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize the i18n ast to something xml-like in order to generate an UID.
|
||||||
|
*
|
||||||
|
* Ignore the ICU expressions so that message IDs stays identical if only the expression changes.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class _SerializerIgnoreIcuExpVisitor extends _SerializerVisitor {
|
||||||
|
visitIcu(icu: i18n.Icu, context: any): any {
|
||||||
|
let strCases = Object.keys(icu.cases).map((k: string) => `${k} {${icu.cases[k].visit(this)}}`);
|
||||||
|
// Do not take the expression into account
|
||||||
|
return `{${icu.type}, ${strCases.join(', ')}}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the SHA1 of the given string
|
* Compute the SHA1 of the given string
|
||||||
*
|
*
|
||||||
@ -63,7 +84,7 @@ export function serializeNodes(nodes: i18n.Node[]): string[] {
|
|||||||
*/
|
*/
|
||||||
export function sha1(str: string): string {
|
export function sha1(str: string): string {
|
||||||
const utf8 = utf8Encode(str);
|
const utf8 = utf8Encode(str);
|
||||||
const words32 = stringToWords32(utf8);
|
const words32 = stringToWords32(utf8, Endian.Big);
|
||||||
const len = utf8.length * 8;
|
const len = utf8.length * 8;
|
||||||
|
|
||||||
const w = new Array(80);
|
const w = new Array(80);
|
||||||
@ -90,15 +111,99 @@ export function sha1(str: string): string {
|
|||||||
[a, b, c, d, e] = [add32(a, h0), add32(b, h1), add32(c, h2), add32(d, h3), add32(e, h4)];
|
[a, b, c, d, e] = [add32(a, h0), add32(b, h1), add32(c, h2), add32(d, h3), add32(e, h4)];
|
||||||
}
|
}
|
||||||
|
|
||||||
const sha1 = words32ToString([a, b, c, d, e]);
|
return byteStringToHexString(words32ToByteString([a, b, c, d, e]));
|
||||||
|
}
|
||||||
|
|
||||||
let hex: string = '';
|
function fk(index: number, b: number, c: number, d: number): [number, number] {
|
||||||
for (let i = 0; i < sha1.length; i++) {
|
if (index < 20) {
|
||||||
const b = sha1.charCodeAt(i);
|
return [(b & c) | (~b & d), 0x5a827999];
|
||||||
hex += (b >>> 4 & 0x0f).toString(16) + (b & 0x0f).toString(16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return hex.toLowerCase();
|
if (index < 40) {
|
||||||
|
return [b ^ c ^ d, 0x6ed9eba1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < 60) {
|
||||||
|
return [(b & c) | (b & d) | (c & d), 0x8f1bbcdc];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [b ^ c ^ d, 0xca62c1d6];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the fingerprint of the given string
|
||||||
|
*
|
||||||
|
* The output is 64 bit number encoded as a decimal string
|
||||||
|
*
|
||||||
|
* based on:
|
||||||
|
* https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java
|
||||||
|
*/
|
||||||
|
export function fingerprint(str: string): [number, number] {
|
||||||
|
const utf8 = utf8Encode(str);
|
||||||
|
|
||||||
|
let [hi, lo] = [hash32(utf8, 0), hash32(utf8, 102072)];
|
||||||
|
|
||||||
|
if (hi == 0 && (lo == 0 || lo == 1)) {
|
||||||
|
hi = hi ^ 0x130f9bef;
|
||||||
|
lo = lo ^ -0x6b5f56d8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [hi, lo];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function computeMsgId(msg: string, meaning: string): string {
|
||||||
|
let [hi, lo] = fingerprint(msg);
|
||||||
|
|
||||||
|
if (meaning) {
|
||||||
|
const [him, lom] = fingerprint(meaning);
|
||||||
|
[hi, lo] = add64(rol64([hi, lo], 1), [him, lom]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return byteStringToDecString(words32ToByteString([hi & 0x7fffffff, lo]));
|
||||||
|
}
|
||||||
|
|
||||||
|
function hash32(str: string, c: number): number {
|
||||||
|
let [a, b] = [0x9e3779b9, 0x9e3779b9];
|
||||||
|
let i: number;
|
||||||
|
|
||||||
|
const len = str.length;
|
||||||
|
|
||||||
|
for (i = 0; i + 12 <= len; i += 12) {
|
||||||
|
a = add32(a, wordAt(str, i, Endian.Little));
|
||||||
|
b = add32(b, wordAt(str, i + 4, Endian.Little));
|
||||||
|
c = add32(c, wordAt(str, i + 8, Endian.Little));
|
||||||
|
[a, b, c] = mix([a, b, c]);
|
||||||
|
}
|
||||||
|
|
||||||
|
a = add32(a, wordAt(str, i, Endian.Little));
|
||||||
|
b = add32(b, wordAt(str, i + 4, Endian.Little));
|
||||||
|
// the first byte of c is reserved for the length
|
||||||
|
c = add32(c, len);
|
||||||
|
c = add32(c, wordAt(str, i + 8, Endian.Little) << 8);
|
||||||
|
|
||||||
|
return mix([a, b, c])[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
function mix([a, b, c]: [number, number, number]): [number, number, number] {
|
||||||
|
a = sub32(a, b); a = sub32(a, c); a ^= c >>> 13;
|
||||||
|
b = sub32(b, c); b = sub32(b, a); b ^= a << 8;
|
||||||
|
c = sub32(c, a); c = sub32(c, b); c ^= b >>> 13;
|
||||||
|
a = sub32(a, b); a = sub32(a, c); a ^= c >>> 12;
|
||||||
|
b = sub32(b, c); b = sub32(b, a); b ^= a << 16;
|
||||||
|
c = sub32(c, a); c = sub32(c, b); c ^= b >>> 5;
|
||||||
|
a = sub32(a, b); a = sub32(a, c); a ^= c >>> 3;
|
||||||
|
b = sub32(b, c); b = sub32(b, a); b ^= a << 10;
|
||||||
|
c = sub32(c, a); c = sub32(c, b); c ^= b >>> 15;
|
||||||
|
return [a, b, c];
|
||||||
|
}
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
|
||||||
|
enum Endian {
|
||||||
|
Little,
|
||||||
|
Big,
|
||||||
}
|
}
|
||||||
|
|
||||||
function utf8Encode(str: string): string {
|
function utf8Encode(str: string): string {
|
||||||
@ -131,10 +236,9 @@ function decodeSurrogatePairs(str: string, index: number): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const high = str.charCodeAt(index);
|
const high = str.charCodeAt(index);
|
||||||
let low: number;
|
|
||||||
|
|
||||||
if (high >= 0xd800 && high <= 0xdfff && str.length > index + 1) {
|
if (high >= 0xd800 && high <= 0xdfff && str.length > index + 1) {
|
||||||
low = str.charCodeAt(index + 1);
|
const low = byteAt(str, index + 1);
|
||||||
if (low >= 0xdc00 && low <= 0xdfff) {
|
if (low >= 0xdc00 && low <= 0xdfff) {
|
||||||
return (high - 0xd800) * 0x400 + low - 0xdc00 + 0x10000;
|
return (high - 0xd800) * 0x400 + low - 0xdc00 + 0x10000;
|
||||||
}
|
}
|
||||||
@ -143,50 +247,126 @@ function decodeSurrogatePairs(str: string, index: number): number {
|
|||||||
return high;
|
return high;
|
||||||
}
|
}
|
||||||
|
|
||||||
function stringToWords32(str: string): number[] {
|
function add32(a: number, b: number): number {
|
||||||
const words32 = Array(str.length >>> 2);
|
return add32to64(a, b)[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function add32to64(a: number, b: number): [number, number] {
|
||||||
|
const low = (a & 0xffff) + (b & 0xffff);
|
||||||
|
const high = (a >>> 16) + (b >>> 16) + (low >>> 16);
|
||||||
|
return [high >>> 16, (high << 16) | (low & 0xffff)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function add64([ah, al]: [number, number], [bh, bl]: [number, number]): [number, number] {
|
||||||
|
const [carry, l] = add32to64(al, bl);
|
||||||
|
const h = add32(add32(ah, bh), carry);
|
||||||
|
return [h, l];
|
||||||
|
}
|
||||||
|
|
||||||
|
function sub32(a: number, b: number): number {
|
||||||
|
const low = (a & 0xffff) - (b & 0xffff);
|
||||||
|
const high = (a >> 16) - (b >> 16) + (low >> 16);
|
||||||
|
return (high << 16) | (low & 0xffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate a 32b number left `count` position
|
||||||
|
function rol32(a: number, count: number): number {
|
||||||
|
return (a << count) | (a >>> (32 - count));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate a 64b number left `count` position
|
||||||
|
function rol64([hi, lo]: [number, number], count: number): [number, number] {
|
||||||
|
const h = (hi << count) | (lo >>> (32 - count));
|
||||||
|
const l = (lo << count) | (hi >>> (32 - count));
|
||||||
|
return [h, l];
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringToWords32(str: string, endian: Endian): number[] {
|
||||||
|
const words32 = Array((str.length + 3) >>> 2);
|
||||||
|
|
||||||
for (let i = 0; i < words32.length; i++) {
|
for (let i = 0; i < words32.length; i++) {
|
||||||
words32[i] = 0;
|
words32[i] = wordAt(str, i * 4, endian);
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < str.length; i++) {
|
|
||||||
words32[i >>> 2] |= (str.charCodeAt(i) & 0xff) << 8 * (3 - i & 0x3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return words32;
|
return words32;
|
||||||
}
|
}
|
||||||
|
|
||||||
function words32ToString(words32: number[]): string {
|
function byteAt(str: string, index: number): number {
|
||||||
|
return index >= str.length ? 0 : str.charCodeAt(index) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wordAt(str: string, index: number, endian: Endian): number {
|
||||||
|
let word = 0;
|
||||||
|
if (endian === Endian.Big) {
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
word += byteAt(str, index + i) << (24 - 8 * i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
word += byteAt(str, index + i) << 8 * i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
|
||||||
|
function words32ToByteString(words32: number[]): string {
|
||||||
|
return words32.reduce((str, word) => str + word32ToByteString(word), '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function word32ToByteString(word: number): string {
|
||||||
let str = '';
|
let str = '';
|
||||||
for (let i = 0; i < words32.length * 4; i++) {
|
for (let i = 0; i < 4; i++) {
|
||||||
str += String.fromCharCode((words32[i >>> 2] >>> 8 * (3 - i & 0x3)) & 0xff);
|
str += String.fromCharCode((word >>> 8 * (3 - i)) & 0xff);
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
function fk(index: number, b: number, c: number, d: number): [number, number] {
|
function byteStringToHexString(str: string): string {
|
||||||
if (index < 20) {
|
let hex: string = '';
|
||||||
return [(b & c) | (~b & d), 0x5a827999];
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
const b = byteAt(str, i);
|
||||||
|
hex += (b >>> 4).toString(16) + (b & 0x0f).toString(16);
|
||||||
}
|
}
|
||||||
|
return hex.toLowerCase();
|
||||||
if (index < 40) {
|
|
||||||
return [b ^ c ^ d, 0x6ed9eba1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index < 60) {
|
|
||||||
return [(b & c) | (b & d) | (c & d), 0x8f1bbcdc];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [b ^ c ^ d, 0xca62c1d6];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function add32(a: number, b: number): number {
|
// based on http://www.danvk.org/hex2dec.html (JS can not handle more than 56b)
|
||||||
const low = (a & 0xffff) + (b & 0xffff);
|
function byteStringToDecString(str: string): string {
|
||||||
const high = (a >> 16) + (b >> 16) + (low >> 16);
|
let decimal = '';
|
||||||
return (high << 16) | (low & 0xffff);
|
let toThePower = '1';
|
||||||
|
|
||||||
|
for (let i = str.length - 1; i >= 0; i--) {
|
||||||
|
decimal = addBigInt(decimal, numberTimesBigInt(byteAt(str, i), toThePower));
|
||||||
|
toThePower = numberTimesBigInt(256, toThePower);
|
||||||
|
}
|
||||||
|
|
||||||
|
return decimal.split('').reverse().join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
function rol32(a: number, count: number): number {
|
// x and y decimal, lowest significant digit first
|
||||||
return (a << count) | (a >>> (32 - count));
|
function addBigInt(x: string, y: string): string {
|
||||||
|
let sum = '';
|
||||||
|
const len = Math.max(x.length, y.length);
|
||||||
|
for (let i = 0, carry = 0; i < len || carry; i++) {
|
||||||
|
const tmpSum = carry + +(x[i] || 0) + +(y[i] || 0);
|
||||||
|
if (tmpSum >= 10) {
|
||||||
|
carry = 1;
|
||||||
|
sum += tmpSum - 10;
|
||||||
|
} else {
|
||||||
|
carry = 0;
|
||||||
|
sum += tmpSum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
function numberTimesBigInt(num: number, b: string): string {
|
||||||
|
let product = '';
|
||||||
|
let bToThePower = b;
|
||||||
|
for (; num !== 0; num = num >>> 1) {
|
||||||
|
if (num & 1) product = addBigInt(product, bToThePower);
|
||||||
|
bToThePower = addBigInt(bToThePower, bToThePower);
|
||||||
|
}
|
||||||
|
return product;
|
||||||
}
|
}
|
@ -12,9 +12,10 @@
|
|||||||
*/
|
*/
|
||||||
import {ViewEncapsulation} from '@angular/core';
|
import {ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
import {analyzeAndValidateNgModules, extractProgramSymbols, loadNgModuleDirectives} from '../aot/compiler';
|
import {analyzeAndValidateNgModules, extractProgramSymbols} from '../aot/compiler';
|
||||||
import {StaticAndDynamicReflectionCapabilities} from '../aot/static_reflection_capabilities';
|
import {StaticAndDynamicReflectionCapabilities} from '../aot/static_reflection_capabilities';
|
||||||
import {StaticReflector, StaticReflectorHost} from '../aot/static_reflector';
|
import {StaticReflector, StaticReflectorHost} from '../aot/static_reflector';
|
||||||
|
import {AotSummaryResolver, AotSummaryResolverHost} from '../aot/summary_resolver';
|
||||||
import {CompileDirectiveMetadata} from '../compile_metadata';
|
import {CompileDirectiveMetadata} from '../compile_metadata';
|
||||||
import {CompilerConfig} from '../config';
|
import {CompilerConfig} from '../config';
|
||||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||||
@ -41,7 +42,7 @@ export interface ExtractorOptions {
|
|||||||
* The host of the Extractor disconnects the implementation from TypeScript / other language
|
* The host of the Extractor disconnects the implementation from TypeScript / other language
|
||||||
* services and from underlying file systems.
|
* services and from underlying file systems.
|
||||||
*/
|
*/
|
||||||
export interface ExtractorHost extends StaticReflectorHost {
|
export interface ExtractorHost extends StaticReflectorHost, AotSummaryResolverHost {
|
||||||
/**
|
/**
|
||||||
* Loads a resource (e.g. html / css)
|
* Loads a resource (e.g. html / css)
|
||||||
*/
|
*/
|
||||||
@ -58,7 +59,11 @@ export class Extractor {
|
|||||||
const programSymbols = extractProgramSymbols(this.staticReflector, rootFiles, this.options);
|
const programSymbols = extractProgramSymbols(this.staticReflector, rootFiles, this.options);
|
||||||
const {ngModuleByPipeOrDirective, files, ngModules} =
|
const {ngModuleByPipeOrDirective, files, ngModules} =
|
||||||
analyzeAndValidateNgModules(programSymbols, this.options, this.metadataResolver);
|
analyzeAndValidateNgModules(programSymbols, this.options, this.metadataResolver);
|
||||||
return loadNgModuleDirectives(ngModules).then(() => {
|
return Promise
|
||||||
|
.all(ngModules.map(
|
||||||
|
ngModule => this.metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
||||||
|
ngModule.type.reference, false)))
|
||||||
|
.then(() => {
|
||||||
const errors: ParseError[] = [];
|
const errors: ParseError[] = [];
|
||||||
|
|
||||||
files.forEach(file => {
|
files.forEach(file => {
|
||||||
@ -106,7 +111,8 @@ export class Extractor {
|
|||||||
const elementSchemaRegistry = new DomElementSchemaRegistry();
|
const elementSchemaRegistry = new DomElementSchemaRegistry();
|
||||||
const resolver = new CompileMetadataResolver(
|
const resolver = new CompileMetadataResolver(
|
||||||
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
||||||
new PipeResolver(staticReflector), elementSchemaRegistry, normalizer, staticReflector);
|
new PipeResolver(staticReflector), new AotSummaryResolver(host, staticReflector, options),
|
||||||
|
elementSchemaRegistry, normalizer, staticReflector);
|
||||||
|
|
||||||
// TODO(vicb): implicit tags & attributes
|
// TODO(vicb): implicit tags & attributes
|
||||||
const messageBundle = new MessageBundle(htmlParser, [], {});
|
const messageBundle = new MessageBundle(htmlParser, [], {});
|
||||||
|
@ -10,7 +10,6 @@ import * as html from '../ml_parser/ast';
|
|||||||
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||||
import {ParseTreeResult} from '../ml_parser/parser';
|
import {ParseTreeResult} from '../ml_parser/parser';
|
||||||
|
|
||||||
import {digestMessage} from './digest';
|
|
||||||
import * as i18n from './i18n_ast';
|
import * as i18n from './i18n_ast';
|
||||||
import {createI18nMessageFactory} from './i18n_parser';
|
import {createI18nMessageFactory} from './i18n_parser';
|
||||||
import {I18nError} from './parse_util';
|
import {I18nError} from './parse_util';
|
||||||
@ -214,8 +213,8 @@ class _Visitor implements html.Visitor {
|
|||||||
// Extract only top level nodes with the (implicit) "i18n" attribute if not in a block or an ICU
|
// Extract only top level nodes with the (implicit) "i18n" attribute if not in a block or an ICU
|
||||||
// message
|
// message
|
||||||
const i18nAttr = _getI18nAttr(el);
|
const i18nAttr = _getI18nAttr(el);
|
||||||
const isImplicit = this._implicitTags.some((tag: string): boolean => el.name === tag) &&
|
const isImplicit = this._implicitTags.some(tag => el.name === tag) && !this._inIcu &&
|
||||||
!this._inIcu && !this._isInTranslatableSection;
|
!this._isInTranslatableSection;
|
||||||
const isTopLevelImplicit = !wasInImplicitNode && isImplicit;
|
const isTopLevelImplicit = !wasInImplicitNode && isImplicit;
|
||||||
this._inImplicitNode = this._inImplicitNode || isImplicit;
|
this._inImplicitNode = this._inImplicitNode || isImplicit;
|
||||||
|
|
||||||
@ -348,14 +347,14 @@ class _Visitor implements html.Visitor {
|
|||||||
// 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) {
|
||||||
const id = digestMessage(message);
|
const nodes = this._translations.get(message);
|
||||||
const nodes = this._translations.get(id);
|
|
||||||
|
|
||||||
if (nodes) {
|
if (nodes) {
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._reportError(el, `Translation unavailable for message id="${id}"`);
|
this._reportError(
|
||||||
|
el, `Translation unavailable for message id="${this._translations.digest(message)}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
@ -384,19 +383,20 @@ 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 id = digestMessage(message);
|
const nodes = this._translations.get(message);
|
||||||
const nodes = this._translations.get(id);
|
|
||||||
if (nodes) {
|
if (nodes) {
|
||||||
if (nodes[0] instanceof html.Text) {
|
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 {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
el, `Unexpected translation for attribute "${attr.name}" (id="${id}")`);
|
el,
|
||||||
|
`Unexpected translation for attribute "${attr.name}" (id="${this._translations.digest(message)}")`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
el, `Translation unavailable for attribute "${attr.name}" (id="${id}")`);
|
el,
|
||||||
|
`Translation unavailable for attribute "${attr.name}" (id="${this._translations.digest(message)}")`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
translatedAttributes.push(attr);
|
translatedAttributes.push(attr);
|
||||||
|
@ -12,18 +12,20 @@ export class Message {
|
|||||||
/**
|
/**
|
||||||
* @param nodes message AST
|
* @param nodes message AST
|
||||||
* @param placeholders maps placeholder names to static content
|
* @param placeholders maps placeholder names to static content
|
||||||
* @param placeholderToMsgIds maps placeholder names to translatable message IDs (used for ICU
|
* @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages)
|
||||||
* messages)
|
|
||||||
* @param meaning
|
* @param meaning
|
||||||
* @param description
|
* @param description
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
public nodes: Node[], public placeholders: {[name: string]: string},
|
public nodes: Node[], public placeholders: {[phName: string]: string},
|
||||||
public placeholderToMsgIds: {[name: string]: string}, public meaning: string,
|
public placeholderToMessage: {[phName: string]: Message}, public meaning: string,
|
||||||
public description: string) {}
|
public description: string) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Node { visit(visitor: Visitor, context?: any): any; }
|
export interface Node {
|
||||||
|
sourceSpan: ParseSourceSpan;
|
||||||
|
visit(visitor: Visitor, context?: any): any;
|
||||||
|
}
|
||||||
|
|
||||||
export class Text implements Node {
|
export class Text implements Node {
|
||||||
constructor(public value: string, public sourceSpan: ParseSourceSpan) {}
|
constructor(public value: string, public sourceSpan: ParseSourceSpan) {}
|
||||||
@ -31,6 +33,7 @@ export class Text implements Node {
|
|||||||
visit(visitor: Visitor, context?: any): any { return visitor.visitText(this, context); }
|
visit(visitor: Visitor, context?: any): any { return visitor.visitText(this, context); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(vicb): do we really need this node (vs an array) ?
|
||||||
export class Container implements Node {
|
export class Container implements Node {
|
||||||
constructor(public children: Node[], public sourceSpan: ParseSourceSpan) {}
|
constructor(public children: Node[], public sourceSpan: ParseSourceSpan) {}
|
||||||
|
|
||||||
@ -38,6 +41,7 @@ export class Container implements Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Icu implements Node {
|
export class Icu implements Node {
|
||||||
|
public expressionPlaceholder: string;
|
||||||
constructor(
|
constructor(
|
||||||
public expression: string, public type: string, public cases: {[k: string]: Node},
|
public expression: string, public type: string, public cases: {[k: string]: Node},
|
||||||
public sourceSpan: ParseSourceSpan) {}
|
public sourceSpan: ParseSourceSpan) {}
|
||||||
@ -55,13 +59,13 @@ export class TagPlaceholder implements Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Placeholder implements Node {
|
export class Placeholder implements Node {
|
||||||
constructor(public value: string, public name: string = '', public sourceSpan: ParseSourceSpan) {}
|
constructor(public value: string, public name: string, public sourceSpan: ParseSourceSpan) {}
|
||||||
|
|
||||||
visit(visitor: Visitor, context?: any): any { return visitor.visitPlaceholder(this, context); }
|
visit(visitor: Visitor, context?: any): any { return visitor.visitPlaceholder(this, context); }
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IcuPlaceholder implements Node {
|
export class IcuPlaceholder implements Node {
|
||||||
constructor(public value: Icu, public name: string = '', public sourceSpan: ParseSourceSpan) {}
|
constructor(public value: Icu, public name: string, public sourceSpan: ParseSourceSpan) {}
|
||||||
|
|
||||||
visit(visitor: Visitor, context?: any): any { return visitor.visitIcuPlaceholder(this, context); }
|
visit(visitor: Visitor, context?: any): any { return visitor.visitIcuPlaceholder(this, context); }
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/in
|
|||||||
import {ParseTreeResult} from '../ml_parser/parser';
|
import {ParseTreeResult} from '../ml_parser/parser';
|
||||||
|
|
||||||
import {mergeTranslations} from './extractor_merger';
|
import {mergeTranslations} from './extractor_merger';
|
||||||
import {MessageBundle} from './message_bundle';
|
|
||||||
import {Serializer} from './serializers/serializer';
|
import {Serializer} from './serializers/serializer';
|
||||||
import {Xliff} from './serializers/xliff';
|
import {Xliff} from './serializers/xliff';
|
||||||
import {Xmb} from './serializers/xmb';
|
import {Xmb} from './serializers/xmb';
|
||||||
@ -41,32 +40,29 @@ export class I18NHtmlParser implements HtmlParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO(vicb): add support for implicit tags / attributes
|
// TODO(vicb): add support for implicit tags / attributes
|
||||||
const messageBundle = new MessageBundle(this._htmlParser, [], {});
|
|
||||||
const errors = messageBundle.updateFromTemplate(source, url, interpolationConfig);
|
|
||||||
|
|
||||||
if (errors && errors.length) {
|
if (parseResult.errors.length) {
|
||||||
return new ParseTreeResult(parseResult.rootNodes, parseResult.errors.concat(errors));
|
return new ParseTreeResult(parseResult.rootNodes, parseResult.errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
const serializer = this._createSerializer(interpolationConfig);
|
const serializer = this._createSerializer();
|
||||||
const translationBundle =
|
const translationBundle = TranslationBundle.load(this._translations, url, serializer);
|
||||||
TranslationBundle.load(this._translations, url, messageBundle, serializer);
|
|
||||||
|
|
||||||
return mergeTranslations(parseResult.rootNodes, translationBundle, interpolationConfig, [], {});
|
return mergeTranslations(parseResult.rootNodes, translationBundle, interpolationConfig, [], {});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createSerializer(interpolationConfig: InterpolationConfig): Serializer {
|
private _createSerializer(): Serializer {
|
||||||
const format = (this._translationsFormat || 'xlf').toLowerCase();
|
const format = (this._translationsFormat || 'xlf').toLowerCase();
|
||||||
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case 'xmb':
|
case 'xmb':
|
||||||
return new Xmb();
|
return new Xmb();
|
||||||
case 'xtb':
|
case 'xtb':
|
||||||
return new Xtb(this._htmlParser, interpolationConfig);
|
return new Xtb();
|
||||||
case 'xliff':
|
case 'xliff':
|
||||||
case 'xlf':
|
case 'xlf':
|
||||||
default:
|
default:
|
||||||
return new Xliff(this._htmlParser, interpolationConfig);
|
return new Xliff();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import * as html from '../ml_parser/ast';
|
|||||||
import {getHtmlTagDefinition} from '../ml_parser/html_tags';
|
import {getHtmlTagDefinition} from '../ml_parser/html_tags';
|
||||||
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||||
import {ParseSourceSpan} from '../parse_util';
|
import {ParseSourceSpan} from '../parse_util';
|
||||||
import {digestMessage} from './digest';
|
|
||||||
|
|
||||||
import * as i18n from './i18n_ast';
|
import * as i18n from './i18n_ast';
|
||||||
import {PlaceholderRegistry} from './serializers/placeholder';
|
import {PlaceholderRegistry} from './serializers/placeholder';
|
||||||
@ -34,8 +33,8 @@ class _I18nVisitor implements html.Visitor {
|
|||||||
private _isIcu: boolean;
|
private _isIcu: boolean;
|
||||||
private _icuDepth: number;
|
private _icuDepth: number;
|
||||||
private _placeholderRegistry: PlaceholderRegistry;
|
private _placeholderRegistry: PlaceholderRegistry;
|
||||||
private _placeholderToContent: {[name: string]: string};
|
private _placeholderToContent: {[phName: string]: string};
|
||||||
private _placeholderToIds: {[name: string]: string};
|
private _placeholderToMessage: {[phName: string]: i18n.Message};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _expressionParser: ExpressionParser,
|
private _expressionParser: ExpressionParser,
|
||||||
@ -46,12 +45,12 @@ class _I18nVisitor implements html.Visitor {
|
|||||||
this._icuDepth = 0;
|
this._icuDepth = 0;
|
||||||
this._placeholderRegistry = new PlaceholderRegistry();
|
this._placeholderRegistry = new PlaceholderRegistry();
|
||||||
this._placeholderToContent = {};
|
this._placeholderToContent = {};
|
||||||
this._placeholderToIds = {};
|
this._placeholderToMessage = {};
|
||||||
|
|
||||||
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._placeholderToIds, meaning, description);
|
i18nodes, this._placeholderToContent, this._placeholderToMessage, meaning, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitElement(el: html.Element, context: any): i18n.Node {
|
visitElement(el: html.Element, context: any): i18n.Node {
|
||||||
@ -99,7 +98,13 @@ class _I18nVisitor implements html.Visitor {
|
|||||||
this._icuDepth--;
|
this._icuDepth--;
|
||||||
|
|
||||||
if (this._isIcu || this._icuDepth > 0) {
|
if (this._isIcu || this._icuDepth > 0) {
|
||||||
// If the message (vs a part of the message) is an ICU message returns it
|
// Returns an ICU node when:
|
||||||
|
// - the message (vs a part of the message) is an ICU message, or
|
||||||
|
// - the ICU message is nested.
|
||||||
|
const expPh = this._placeholderRegistry.getUniquePlaceholder(`VAR_${icu.type}`);
|
||||||
|
i18nIcu.expressionPlaceholder = expPh;
|
||||||
|
this._placeholderToContent[expPh] = icu.switchValue;
|
||||||
|
|
||||||
return i18nIcu;
|
return i18nIcu;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +115,7 @@ class _I18nVisitor implements html.Visitor {
|
|||||||
// TODO(vicb): add a html.Node -> i18n.Message cache to avoid having to re-create the msg
|
// TODO(vicb): add a html.Node -> i18n.Message cache to avoid having to re-create the msg
|
||||||
const phName = this._placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString());
|
const phName = this._placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString());
|
||||||
const visitor = new _I18nVisitor(this._expressionParser, this._interpolationConfig);
|
const visitor = new _I18nVisitor(this._expressionParser, this._interpolationConfig);
|
||||||
this._placeholderToIds[phName] = digestMessage(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ import {HtmlParser} from '../ml_parser/html_parser';
|
|||||||
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||||
import {ParseError} from '../parse_util';
|
import {ParseError} from '../parse_util';
|
||||||
|
|
||||||
import {digestMessage} from './digest';
|
|
||||||
import {extractMessages} from './extractor_merger';
|
import {extractMessages} from './extractor_merger';
|
||||||
import {Message} from './i18n_ast';
|
import {Message} from './i18n_ast';
|
||||||
import {Serializer} from './serializers/serializer';
|
import {Serializer} from './serializers/serializer';
|
||||||
@ -19,7 +18,7 @@ import {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 _messageMap: {[id: string]: Message} = {};
|
private _messages: Message[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _htmlParser: HtmlParser, private _implicitTags: string[],
|
private _htmlParser: HtmlParser, private _implicitTags: string[],
|
||||||
@ -40,11 +39,10 @@ export class MessageBundle {
|
|||||||
return i18nParserResult.errors;
|
return i18nParserResult.errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
i18nParserResult.messages.forEach(
|
this._messages.push(...i18nParserResult.messages);
|
||||||
(message) => { this._messageMap[digestMessage(message)] = message; });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getMessageMap(): {[id: string]: Message} { return this._messageMap; }
|
getMessages(): Message[] { return this._messages; }
|
||||||
|
|
||||||
write(serializer: Serializer): string { return serializer.write(this._messageMap); }
|
write(serializer: Serializer): string { return serializer.write(this._messages); }
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,9 @@ const TAG_TO_PLACEHOLDER_NAMES: {[k: string]: string} = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates unique names for placeholder with different content
|
* Creates unique names for placeholder with different content.
|
||||||
|
*
|
||||||
|
* Returns the same placeholder name when the content is identical.
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
@ -93,6 +95,10 @@ export class PlaceholderRegistry {
|
|||||||
return uniqueName;
|
return uniqueName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getUniquePlaceholder(name: string): string {
|
||||||
|
return this._generateUniqueName(name.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
// Generate a hash for a tag - does not take attribute order into account
|
// Generate a hash for a tag - does not take attribute order into account
|
||||||
private _hashTag(tag: string, attrs: {[k: string]: string}, isVoid: boolean): string {
|
private _hashTag(tag: string, attrs: {[k: string]: string}, isVoid: boolean): string {
|
||||||
const start = `<${tag}`;
|
const start = `<${tag}`;
|
||||||
@ -105,18 +111,8 @@ export class PlaceholderRegistry {
|
|||||||
private _hashClosingTag(tag: string): string { return this._hashTag(`/${tag}`, {}, false); }
|
private _hashClosingTag(tag: string): string { return this._hashTag(`/${tag}`, {}, false); }
|
||||||
|
|
||||||
private _generateUniqueName(base: string): string {
|
private _generateUniqueName(base: string): string {
|
||||||
let name = base;
|
const next = this._placeHolderNameCounts[base];
|
||||||
let next = this._placeHolderNameCounts[name];
|
this._placeHolderNameCounts[base] = next ? next + 1 : 1;
|
||||||
|
return next ? `${base}_${next}` : base;
|
||||||
if (!next) {
|
|
||||||
next = 1;
|
|
||||||
} else {
|
|
||||||
name += `_${next}`;
|
|
||||||
next++;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._placeHolderNameCounts[base] = next;
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,36 +6,12 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as html from '../../ml_parser/ast';
|
|
||||||
import * as i18n from '../i18n_ast';
|
import * as i18n from '../i18n_ast';
|
||||||
import {MessageBundle} from '../message_bundle';
|
|
||||||
|
|
||||||
export interface Serializer {
|
export interface Serializer {
|
||||||
write(messageMap: {[id: string]: i18n.Message}): string;
|
write(messages: i18n.Message[]): string;
|
||||||
|
|
||||||
load(content: string, url: string, messageBundle: MessageBundle): {[id: string]: html.Node[]};
|
load(content: string, url: string): {[msgId: string]: i18n.Node[]};
|
||||||
}
|
|
||||||
|
digest(message: i18n.Message): string;
|
||||||
// Generate a map of placeholder to content indexed by message ids
|
|
||||||
export function extractPlaceholders(messageBundle: MessageBundle) {
|
|
||||||
const messageMap = messageBundle.getMessageMap();
|
|
||||||
const placeholders: {[id: string]: {[name: string]: string}} = {};
|
|
||||||
|
|
||||||
Object.keys(messageMap).forEach(msgId => {
|
|
||||||
placeholders[msgId] = messageMap[msgId].placeholders;
|
|
||||||
});
|
|
||||||
|
|
||||||
return placeholders;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a map of placeholder to message ids indexed by message ids
|
|
||||||
export function extractPlaceholderToIds(messageBundle: MessageBundle) {
|
|
||||||
const messageMap = messageBundle.getMessageMap();
|
|
||||||
const placeholderToIds: {[id: string]: {[name: string]: string}} = {};
|
|
||||||
|
|
||||||
Object.keys(messageMap).forEach(msgId => {
|
|
||||||
placeholderToIds[msgId] = messageMap[msgId].placeholderToMsgIds;
|
|
||||||
});
|
|
||||||
|
|
||||||
return placeholderToIds;
|
|
||||||
}
|
}
|
@ -7,15 +7,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as ml from '../../ml_parser/ast';
|
import * as ml from '../../ml_parser/ast';
|
||||||
import {HtmlParser} from '../../ml_parser/html_parser';
|
|
||||||
import {InterpolationConfig} from '../../ml_parser/interpolation_config';
|
|
||||||
import {XmlParser} from '../../ml_parser/xml_parser';
|
import {XmlParser} from '../../ml_parser/xml_parser';
|
||||||
import {ParseError} from '../../parse_util';
|
import {digest} from '../digest';
|
||||||
import * as i18n from '../i18n_ast';
|
import * as i18n from '../i18n_ast';
|
||||||
import {MessageBundle} from '../message_bundle';
|
|
||||||
import {I18nError} from '../parse_util';
|
import {I18nError} from '../parse_util';
|
||||||
|
|
||||||
import {Serializer, extractPlaceholderToIds, extractPlaceholders} from './serializer';
|
import {Serializer} from './serializer';
|
||||||
import * as xml from './xml_helper';
|
import * as xml from './xml_helper';
|
||||||
|
|
||||||
const _VERSION = '1.2';
|
const _VERSION = '1.2';
|
||||||
@ -23,6 +20,7 @@ const _XMLNS = 'urn:oasis:names:tc:xliff:document:1.2';
|
|||||||
// TODO(vicb): make this a param (s/_/-/)
|
// TODO(vicb): make this a param (s/_/-/)
|
||||||
const _SOURCE_LANG = 'en';
|
const _SOURCE_LANG = 'en';
|
||||||
const _PLACEHOLDER_TAG = 'x';
|
const _PLACEHOLDER_TAG = 'x';
|
||||||
|
|
||||||
const _SOURCE_TAG = 'source';
|
const _SOURCE_TAG = 'source';
|
||||||
const _TARGET_TAG = 'target';
|
const _TARGET_TAG = 'target';
|
||||||
const _UNIT_TAG = 'trans-unit';
|
const _UNIT_TAG = 'trans-unit';
|
||||||
@ -30,17 +28,19 @@ const _UNIT_TAG = 'trans-unit';
|
|||||||
// http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html
|
// http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html
|
||||||
// http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html
|
// http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html
|
||||||
export class Xliff implements Serializer {
|
export class Xliff implements Serializer {
|
||||||
constructor(private _htmlParser: HtmlParser, private _interpolationConfig: InterpolationConfig) {}
|
write(messages: i18n.Message[]): string {
|
||||||
|
|
||||||
write(messageMap: {[id: string]: i18n.Message}): string {
|
|
||||||
const visitor = new _WriteVisitor();
|
const visitor = new _WriteVisitor();
|
||||||
|
const visited: {[id: string]: boolean} = {};
|
||||||
const transUnits: xml.Node[] = [];
|
const transUnits: xml.Node[] = [];
|
||||||
|
|
||||||
Object.keys(messageMap).forEach((id) => {
|
messages.forEach(message => {
|
||||||
const message = messageMap[id];
|
const id = this.digest(message);
|
||||||
|
|
||||||
const transUnit = new xml.Tag(_UNIT_TAG, {id: 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));
|
||||||
@ -75,38 +75,28 @@ export class Xliff implements Serializer {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
load(content: string, url: string, messageBundle: MessageBundle): {[id: string]: ml.Node[]} {
|
load(content: string, url: string): {[msgId: string]: i18n.Node[]} {
|
||||||
// Parse the xtb file into xml nodes
|
// xliff to xml nodes
|
||||||
const result = new XmlParser().parse(content, url);
|
const xliffParser = new XliffParser();
|
||||||
|
const {mlNodesByMsgId, errors} = xliffParser.parse(content, url);
|
||||||
|
|
||||||
if (result.errors.length) {
|
// xml nodes to i18n nodes
|
||||||
throw new Error(`xtb parse errors:\n${result.errors.join('\n')}`);
|
const i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {};
|
||||||
}
|
const converter = new XmlToI18n();
|
||||||
|
Object.keys(mlNodesByMsgId).forEach(msgId => {
|
||||||
// Replace the placeholders, messages are now string
|
const {i18nNodes, errors: e} = converter.convert(mlNodesByMsgId[msgId]);
|
||||||
const {messages, errors} = new _LoadVisitor().parse(result.rootNodes, messageBundle);
|
errors.push(...e);
|
||||||
|
i18nNodesByMsgId[msgId] = i18nNodes;
|
||||||
if (errors.length) {
|
|
||||||
throw new Error(`xtb parse errors:\n${errors.join('\n')}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the string messages to html ast
|
|
||||||
// TODO(vicb): map error message back to the original message in xtb
|
|
||||||
const messageMap: {[id: string]: ml.Node[]} = {};
|
|
||||||
const parseErrors: ParseError[] = [];
|
|
||||||
|
|
||||||
Object.keys(messages).forEach((id) => {
|
|
||||||
const res = this._htmlParser.parse(messages[id], url, true, this._interpolationConfig);
|
|
||||||
parseErrors.push(...res.errors);
|
|
||||||
messageMap[id] = res.rootNodes;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (parseErrors.length) {
|
if (errors.length) {
|
||||||
throw new Error(`xtb parse errors:\n${parseErrors.join('\n')}`);
|
throw new Error(`xliff parse errors:\n${errors.join('\n')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return messageMap;
|
return i18nNodesByMsgId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
digest(message: i18n.Message): string { return digest(message); }
|
||||||
}
|
}
|
||||||
|
|
||||||
class _WriteVisitor implements i18n.Visitor {
|
class _WriteVisitor implements i18n.Visitor {
|
||||||
@ -166,75 +156,46 @@ class _WriteVisitor implements i18n.Visitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO(vicb): add error management (structure)
|
// TODO(vicb): add error management (structure)
|
||||||
// TODO(vicb): factorize (xtb) ?
|
// Extract messages as xml nodes from the xliff file
|
||||||
class _LoadVisitor implements ml.Visitor {
|
class XliffParser implements ml.Visitor {
|
||||||
private _messageNodes: [string, ml.Node[]][];
|
private _unitMlNodes: ml.Node[];
|
||||||
private _translatedMessages: {[id: string]: string};
|
|
||||||
private _msgId: string;
|
|
||||||
private _target: ml.Node[];
|
|
||||||
private _errors: I18nError[];
|
private _errors: I18nError[];
|
||||||
private _placeholders: {[name: string]: string};
|
private _mlNodesByMsgId: {[msgId: string]: ml.Node[]};
|
||||||
private _placeholderToIds: {[name: string]: string};
|
|
||||||
|
|
||||||
parse(nodes: ml.Node[], messageBundle: MessageBundle):
|
parse(xliff: string, url: string) {
|
||||||
{messages: {[k: string]: string}, errors: I18nError[]} {
|
this._unitMlNodes = [];
|
||||||
this._messageNodes = [];
|
this._mlNodesByMsgId = {};
|
||||||
this._translatedMessages = {};
|
|
||||||
this._msgId = '';
|
|
||||||
this._target = [];
|
|
||||||
this._errors = [];
|
|
||||||
|
|
||||||
// Find all messages
|
const xml = new XmlParser().parse(xliff, url, false);
|
||||||
ml.visitAll(this, nodes, null);
|
|
||||||
|
|
||||||
const messageMap = messageBundle.getMessageMap();
|
this._errors = xml.errors;
|
||||||
const placeholders = extractPlaceholders(messageBundle);
|
ml.visitAll(this, xml.rootNodes, null);
|
||||||
const placeholderToIds = extractPlaceholderToIds(messageBundle);
|
|
||||||
|
|
||||||
this._messageNodes
|
return {
|
||||||
.filter(message => {
|
mlNodesByMsgId: this._mlNodesByMsgId,
|
||||||
// Remove any messages that is not present in the source message bundle.
|
errors: this._errors,
|
||||||
return messageMap.hasOwnProperty(message[0]);
|
};
|
||||||
})
|
|
||||||
.sort((a, b) => {
|
|
||||||
// Because there could be no ICU placeholders inside an ICU message,
|
|
||||||
// we do not need to take into account the `placeholderToMsgIds` of the referenced
|
|
||||||
// messages, those would always be empty
|
|
||||||
// TODO(vicb): overkill - create 2 buckets and [...woDeps, ...wDeps].process()
|
|
||||||
if (Object.keys(messageMap[a[0]].placeholderToMsgIds).length == 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(messageMap[b[0]].placeholderToMsgIds).length == 0) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
})
|
|
||||||
.forEach(message => {
|
|
||||||
const id = message[0];
|
|
||||||
this._placeholders = placeholders[id] || {};
|
|
||||||
this._placeholderToIds = placeholderToIds[id] || {};
|
|
||||||
// TODO(vicb): make sure there is no `_TRANSLATIONS_TAG` nor `_TRANSLATION_TAG`
|
|
||||||
this._translatedMessages[id] = ml.visitAll(this, message[1]).join('');
|
|
||||||
});
|
|
||||||
|
|
||||||
return {messages: this._translatedMessages, errors: this._errors};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visitElement(element: ml.Element, context: any): any {
|
visitElement(element: ml.Element, context: any): any {
|
||||||
switch (element.name) {
|
switch (element.name) {
|
||||||
case _UNIT_TAG:
|
case _UNIT_TAG:
|
||||||
this._target = null;
|
this._unitMlNodes = null;
|
||||||
const msgId = element.attrs.find((attr) => attr.name === 'id');
|
const idAttr = element.attrs.find((attr) => attr.name === 'id');
|
||||||
if (!msgId) {
|
if (!idAttr) {
|
||||||
this._addError(element, `<${_UNIT_TAG}> misses the "id" attribute`);
|
this._addError(element, `<${_UNIT_TAG}> misses the "id" attribute`);
|
||||||
} else {
|
} else {
|
||||||
this._msgId = msgId.value;
|
const id = idAttr.value;
|
||||||
}
|
if (this._mlNodesByMsgId.hasOwnProperty(id)) {
|
||||||
|
this._addError(element, `Duplicated translations for msg ${id}`);
|
||||||
|
} else {
|
||||||
ml.visitAll(this, element.children, null);
|
ml.visitAll(this, element.children, null);
|
||||||
if (this._msgId !== null) {
|
if (this._unitMlNodes) {
|
||||||
this._messageNodes.push([this._msgId, this._target]);
|
this._mlNodesByMsgId[id] = this._unitMlNodes;
|
||||||
|
} else {
|
||||||
|
this._addError(element, `Message ${id} misses a translation`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -243,48 +204,65 @@ class _LoadVisitor implements ml.Visitor {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case _TARGET_TAG:
|
case _TARGET_TAG:
|
||||||
this._target = element.children;
|
this._unitMlNodes = element.children;
|
||||||
break;
|
|
||||||
|
|
||||||
case _PLACEHOLDER_TAG:
|
|
||||||
const idAttr = element.attrs.find((attr) => attr.name === 'id');
|
|
||||||
if (!idAttr) {
|
|
||||||
this._addError(element, `<${_PLACEHOLDER_TAG}> misses the "id" attribute`);
|
|
||||||
} else {
|
|
||||||
const id = idAttr.value;
|
|
||||||
if (this._placeholders.hasOwnProperty(id)) {
|
|
||||||
return this._placeholders[id];
|
|
||||||
}
|
|
||||||
if (this._placeholderToIds.hasOwnProperty(id) &&
|
|
||||||
this._translatedMessages.hasOwnProperty(this._placeholderToIds[id])) {
|
|
||||||
return this._translatedMessages[this._placeholderToIds[id]];
|
|
||||||
}
|
|
||||||
// TODO(vicb): better error message for when
|
|
||||||
// !this._translatedMessages.hasOwnProperty(this._placeholderToIds[id])
|
|
||||||
this._addError(element, `The placeholder "${id}" does not exists in the source message`);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
// TODO(vicb): assert file structure, xliff version
|
||||||
|
// For now only recurse on unhandled nodes
|
||||||
ml.visitAll(this, element.children, null);
|
ml.visitAll(this, element.children, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitAttribute(attribute: ml.Attribute, context: any): any {
|
visitAttribute(attribute: ml.Attribute, context: any): any {}
|
||||||
throw new Error('unreachable code');
|
|
||||||
|
visitText(text: ml.Text, context: any): any {}
|
||||||
|
|
||||||
|
visitComment(comment: ml.Comment, context: any): any {}
|
||||||
|
|
||||||
|
visitExpansion(expansion: ml.Expansion, context: any): any {}
|
||||||
|
|
||||||
|
visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any {}
|
||||||
|
|
||||||
|
private _addError(node: ml.Node, message: string): void {
|
||||||
|
this._errors.push(new I18nError(node.sourceSpan, message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert ml nodes (xliff syntax) to i18n nodes
|
||||||
|
class XmlToI18n implements ml.Visitor {
|
||||||
|
private _errors: I18nError[];
|
||||||
|
|
||||||
|
convert(nodes: ml.Node[]) {
|
||||||
|
this._errors = [];
|
||||||
|
return {
|
||||||
|
i18nNodes: ml.visitAll(this, nodes),
|
||||||
|
errors: this._errors,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
visitText(text: ml.Text, context: any): any { return text.value; }
|
visitText(text: ml.Text, context: any) { return new i18n.Text(text.value, text.sourceSpan); }
|
||||||
|
|
||||||
visitComment(comment: ml.Comment, context: any): any { return ''; }
|
visitElement(el: ml.Element, context: any): i18n.Placeholder {
|
||||||
|
if (el.name === _PLACEHOLDER_TAG) {
|
||||||
visitExpansion(expansion: ml.Expansion, context: any): any {
|
const nameAttr = el.attrs.find((attr) => attr.name === 'id');
|
||||||
throw new Error('unreachable code');
|
if (nameAttr) {
|
||||||
|
return new i18n.Placeholder('', nameAttr.value, el.sourceSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any {
|
this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "id" attribute`);
|
||||||
throw new Error('unreachable code');
|
} else {
|
||||||
|
this._addError(el, `Unexpected tag`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visitExpansion(icu: ml.Expansion, context: any) {}
|
||||||
|
|
||||||
|
visitExpansionCase(icuCase: ml.ExpansionCase, context: any): any {}
|
||||||
|
|
||||||
|
visitComment(comment: ml.Comment, context: any) {}
|
||||||
|
|
||||||
|
visitAttribute(attribute: ml.Attribute, context: any) {}
|
||||||
|
|
||||||
private _addError(node: ml.Node, message: string): void {
|
private _addError(node: ml.Node, message: string): void {
|
||||||
this._errors.push(new I18nError(node.sourceSpan, message));
|
this._errors.push(new I18nError(node.sourceSpan, message));
|
||||||
|
@ -6,9 +6,8 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as html from '../../ml_parser/ast';
|
import {decimalDigest} from '../digest';
|
||||||
import * as i18n from '../i18n_ast';
|
import * as i18n from '../i18n_ast';
|
||||||
import {MessageBundle} from '../message_bundle';
|
|
||||||
|
|
||||||
import {Serializer} from './serializer';
|
import {Serializer} from './serializer';
|
||||||
import * as xml from './xml_helper';
|
import * as xml from './xml_helper';
|
||||||
@ -39,12 +38,18 @@ const _DOCTYPE = `<!ELEMENT messagebundle (msg)*>
|
|||||||
<!ELEMENT ex (#PCDATA)>`;
|
<!ELEMENT ex (#PCDATA)>`;
|
||||||
|
|
||||||
export class Xmb implements Serializer {
|
export class Xmb implements Serializer {
|
||||||
write(messageMap: {[k: string]: i18n.Message}): string {
|
write(messages: i18n.Message[]): string {
|
||||||
const visitor = new _Visitor();
|
const visitor = new _Visitor();
|
||||||
const rootNode = new xml.Tag(_MESSAGES_TAG);
|
const visited: {[id: string]: boolean} = {};
|
||||||
|
let rootNode = new xml.Tag(_MESSAGES_TAG);
|
||||||
|
|
||||||
|
messages.forEach(message => {
|
||||||
|
const id = this.digest(message);
|
||||||
|
|
||||||
|
// deduplicate messages
|
||||||
|
if (visited[id]) return;
|
||||||
|
visited[id] = true;
|
||||||
|
|
||||||
Object.keys(messageMap).forEach((id) => {
|
|
||||||
const message = messageMap[id];
|
|
||||||
const attrs: {[k: string]: string} = {id};
|
const attrs: {[k: string]: string} = {id};
|
||||||
|
|
||||||
if (message.description) {
|
if (message.description) {
|
||||||
@ -71,9 +76,11 @@ export class Xmb implements Serializer {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
load(content: string, url: string, messageBundle: MessageBundle): {[id: string]: html.Node[]} {
|
load(content: string, url: string): {[msgId: string]: i18n.Node[]} {
|
||||||
throw new Error('Unsupported');
|
throw new Error('Unsupported');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
digest(message: i18n.Message): string { return digest(message); }
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Visitor implements i18n.Visitor {
|
class _Visitor implements i18n.Visitor {
|
||||||
@ -86,7 +93,7 @@ class _Visitor implements i18n.Visitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitIcu(icu: i18n.Icu, context?: any): xml.Node[] {
|
visitIcu(icu: i18n.Icu, context?: any): xml.Node[] {
|
||||||
const nodes = [new xml.Text(`{${icu.expression}, ${icu.type}, `)];
|
const nodes = [new xml.Text(`{${icu.expressionPlaceholder}, ${icu.type}, `)];
|
||||||
|
|
||||||
Object.keys(icu.cases).forEach((c: string) => {
|
Object.keys(icu.cases).forEach((c: string) => {
|
||||||
nodes.push(new xml.Text(`${c} {`), ...icu.cases[c].visit(this), new xml.Text(`} `));
|
nodes.push(new xml.Text(`${c} {`), ...icu.cases[c].visit(this), new xml.Text(`} `));
|
||||||
@ -123,3 +130,7 @@ class _Visitor implements i18n.Visitor {
|
|||||||
return [].concat(...nodes.map(node => node.visit(this)));
|
return [].concat(...nodes.map(node => node.visit(this)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function digest(message: i18n.Message): string {
|
||||||
|
return decimalDigest(message);
|
||||||
|
}
|
@ -7,112 +7,63 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as ml from '../../ml_parser/ast';
|
import * as ml from '../../ml_parser/ast';
|
||||||
import {HtmlParser} from '../../ml_parser/html_parser';
|
|
||||||
import {InterpolationConfig} from '../../ml_parser/interpolation_config';
|
|
||||||
import {XmlParser} from '../../ml_parser/xml_parser';
|
import {XmlParser} from '../../ml_parser/xml_parser';
|
||||||
import {ParseError} from '../../parse_util';
|
|
||||||
import * as i18n from '../i18n_ast';
|
import * as i18n from '../i18n_ast';
|
||||||
import {MessageBundle} from '../message_bundle';
|
|
||||||
import {I18nError} from '../parse_util';
|
import {I18nError} from '../parse_util';
|
||||||
|
|
||||||
import {Serializer, extractPlaceholderToIds, extractPlaceholders} from './serializer';
|
import {Serializer} from './serializer';
|
||||||
|
import {digest} from './xmb';
|
||||||
|
|
||||||
const _TRANSLATIONS_TAG = 'translationbundle';
|
const _TRANSLATIONS_TAG = 'translationbundle';
|
||||||
const _TRANSLATION_TAG = 'translation';
|
const _TRANSLATION_TAG = 'translation';
|
||||||
const _PLACEHOLDER_TAG = 'ph';
|
const _PLACEHOLDER_TAG = 'ph';
|
||||||
|
|
||||||
export class Xtb implements Serializer {
|
export class Xtb implements Serializer {
|
||||||
constructor(private _htmlParser: HtmlParser, private _interpolationConfig: InterpolationConfig) {}
|
write(messages: i18n.Message[]): string { throw new Error('Unsupported'); }
|
||||||
|
|
||||||
write(messageMap: {[id: string]: i18n.Message}): string { throw new Error('Unsupported'); }
|
load(content: string, url: string): {[msgId: string]: i18n.Node[]} {
|
||||||
|
// xtb to xml nodes
|
||||||
|
const xtbParser = new XtbParser();
|
||||||
|
const {mlNodesByMsgId, errors} = xtbParser.parse(content, url);
|
||||||
|
|
||||||
load(content: string, url: string, messageBundle: MessageBundle): {[id: string]: ml.Node[]} {
|
// xml nodes to i18n nodes
|
||||||
// Parse the xtb file into xml nodes
|
const i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {};
|
||||||
const result = new XmlParser().parse(content, url);
|
const converter = new XmlToI18n();
|
||||||
|
Object.keys(mlNodesByMsgId).forEach(msgId => {
|
||||||
if (result.errors.length) {
|
const {i18nNodes, errors: e} = converter.convert(mlNodesByMsgId[msgId]);
|
||||||
throw new Error(`xtb parse errors:\n${result.errors.join('\n')}`);
|
errors.push(...e);
|
||||||
}
|
i18nNodesByMsgId[msgId] = i18nNodes;
|
||||||
|
});
|
||||||
// Replace the placeholders, messages are now string
|
|
||||||
const {messages, errors} = new _Visitor().parse(result.rootNodes, messageBundle);
|
|
||||||
|
|
||||||
if (errors.length) {
|
if (errors.length) {
|
||||||
throw new Error(`xtb parse errors:\n${errors.join('\n')}`);
|
throw new Error(`xtb parse errors:\n${errors.join('\n')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the string messages to html ast
|
return i18nNodesByMsgId;
|
||||||
// TODO(vicb): map error message back to the original message in xtb
|
|
||||||
const messageMap: {[id: string]: ml.Node[]} = {};
|
|
||||||
const parseErrors: ParseError[] = [];
|
|
||||||
|
|
||||||
Object.keys(messages).forEach((id) => {
|
|
||||||
const res = this._htmlParser.parse(messages[id], url, true, this._interpolationConfig);
|
|
||||||
parseErrors.push(...res.errors);
|
|
||||||
messageMap[id] = res.rootNodes;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (parseErrors.length) {
|
|
||||||
throw new Error(`xtb parse errors:\n${parseErrors.join('\n')}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return messageMap;
|
digest(message: i18n.Message): string { return digest(message); }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Visitor implements ml.Visitor {
|
// Extract messages as xml nodes from the xtb file
|
||||||
private _messageNodes: [string, ml.Node[]][];
|
class XtbParser implements ml.Visitor {
|
||||||
private _translatedMessages: {[id: string]: string};
|
|
||||||
private _bundleDepth: number;
|
private _bundleDepth: number;
|
||||||
private _translationDepth: number;
|
|
||||||
private _errors: I18nError[];
|
private _errors: I18nError[];
|
||||||
private _placeholders: {[name: string]: string};
|
private _mlNodesByMsgId: {[msgId: string]: ml.Node[]};
|
||||||
private _placeholderToIds: {[name: string]: string};
|
|
||||||
|
|
||||||
parse(nodes: ml.Node[], messageBundle: MessageBundle):
|
parse(xtb: string, url: string) {
|
||||||
{messages: {[k: string]: string}, errors: I18nError[]} {
|
|
||||||
this._messageNodes = [];
|
|
||||||
this._translatedMessages = {};
|
|
||||||
this._bundleDepth = 0;
|
this._bundleDepth = 0;
|
||||||
this._translationDepth = 0;
|
this._mlNodesByMsgId = {};
|
||||||
this._errors = [];
|
|
||||||
|
|
||||||
// Find all messages
|
const xml = new XmlParser().parse(xtb, url, true);
|
||||||
ml.visitAll(this, nodes, null);
|
|
||||||
|
|
||||||
const messageMap = messageBundle.getMessageMap();
|
this._errors = xml.errors;
|
||||||
const placeholders = extractPlaceholders(messageBundle);
|
ml.visitAll(this, xml.rootNodes);
|
||||||
const placeholderToIds = extractPlaceholderToIds(messageBundle);
|
|
||||||
|
|
||||||
this._messageNodes
|
return {
|
||||||
.filter(message => {
|
mlNodesByMsgId: this._mlNodesByMsgId,
|
||||||
// Remove any messages that is not present in the source message bundle.
|
errors: this._errors,
|
||||||
return messageMap.hasOwnProperty(message[0]);
|
};
|
||||||
})
|
|
||||||
.sort((a, b) => {
|
|
||||||
// Because there could be no ICU placeholders inside an ICU message,
|
|
||||||
// we do not need to take into account the `placeholderToMsgIds` of the referenced
|
|
||||||
// messages, those would always be empty
|
|
||||||
// TODO(vicb): overkill - create 2 buckets and [...woDeps, ...wDeps].process()
|
|
||||||
if (Object.keys(messageMap[a[0]].placeholderToMsgIds).length == 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(messageMap[b[0]].placeholderToMsgIds).length == 0) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
})
|
|
||||||
.forEach(message => {
|
|
||||||
const id = message[0];
|
|
||||||
this._placeholders = placeholders[id] || {};
|
|
||||||
this._placeholderToIds = placeholderToIds[id] || {};
|
|
||||||
// TODO(vicb): make sure there is no `_TRANSLATIONS_TAG` nor `_TRANSLATION_TAG`
|
|
||||||
this._translatedMessages[id] = ml.visitAll(this, message[1]).join('');
|
|
||||||
});
|
|
||||||
|
|
||||||
return {messages: this._translatedMessages, errors: this._errors};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visitElement(element: ml.Element, context: any): any {
|
visitElement(element: ml.Element, context: any): any {
|
||||||
@ -127,40 +78,16 @@ class _Visitor implements ml.Visitor {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case _TRANSLATION_TAG:
|
case _TRANSLATION_TAG:
|
||||||
this._translationDepth++;
|
|
||||||
if (this._translationDepth > 1) {
|
|
||||||
this._addError(element, `<${_TRANSLATION_TAG}> elements can not be nested`);
|
|
||||||
}
|
|
||||||
const idAttr = element.attrs.find((attr) => attr.name === 'id');
|
const idAttr = element.attrs.find((attr) => attr.name === 'id');
|
||||||
if (!idAttr) {
|
if (!idAttr) {
|
||||||
this._addError(element, `<${_TRANSLATION_TAG}> misses the "id" attribute`);
|
this._addError(element, `<${_TRANSLATION_TAG}> misses the "id" attribute`);
|
||||||
} else {
|
} else {
|
||||||
// ICU placeholders are reference to other messages.
|
const id = idAttr.value;
|
||||||
// The referenced message might not have been decoded yet.
|
if (this._mlNodesByMsgId.hasOwnProperty(id)) {
|
||||||
// We need to have all messages available to make sure deps are decoded first.
|
this._addError(element, `Duplicated translations for msg ${id}`);
|
||||||
// TODO(vicb): report an error on duplicate id
|
|
||||||
this._messageNodes.push([idAttr.value, element.children]);
|
|
||||||
}
|
|
||||||
this._translationDepth--;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _PLACEHOLDER_TAG:
|
|
||||||
const nameAttr = element.attrs.find((attr) => attr.name === 'name');
|
|
||||||
if (!nameAttr) {
|
|
||||||
this._addError(element, `<${_PLACEHOLDER_TAG}> misses the "name" attribute`);
|
|
||||||
} else {
|
} else {
|
||||||
const name = nameAttr.value;
|
this._mlNodesByMsgId[id] = element.children;
|
||||||
if (this._placeholders.hasOwnProperty(name)) {
|
|
||||||
return this._placeholders[name];
|
|
||||||
}
|
}
|
||||||
if (this._placeholderToIds.hasOwnProperty(name) &&
|
|
||||||
this._translatedMessages.hasOwnProperty(this._placeholderToIds[name])) {
|
|
||||||
return this._translatedMessages[this._placeholderToIds[name]];
|
|
||||||
}
|
|
||||||
// TODO(vicb): better error message for when
|
|
||||||
// !this._translatedMessages.hasOwnProperty(this._placeholderToIds[name])
|
|
||||||
this._addError(
|
|
||||||
element, `The placeholder "${name}" does not exists in the source message`);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -169,23 +96,68 @@ class _Visitor implements ml.Visitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitAttribute(attribute: ml.Attribute, context: any): any {
|
visitAttribute(attribute: ml.Attribute, context: any): any {}
|
||||||
throw new Error('unreachable code');
|
|
||||||
}
|
|
||||||
|
|
||||||
visitText(text: ml.Text, context: any): any { return text.value; }
|
visitText(text: ml.Text, context: any): any {}
|
||||||
|
|
||||||
visitComment(comment: ml.Comment, context: any): any { return ''; }
|
visitComment(comment: ml.Comment, context: any): any {}
|
||||||
|
|
||||||
visitExpansion(expansion: ml.Expansion, context: any): any {
|
visitExpansion(expansion: ml.Expansion, context: any): any {}
|
||||||
const strCases = expansion.cases.map(c => c.visit(this, null));
|
|
||||||
|
|
||||||
return `{${expansion.switchValue}, ${expansion.type}, strCases.join(' ')}`;
|
visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any {}
|
||||||
}
|
|
||||||
|
private _addError(node: ml.Node, message: string): void {
|
||||||
visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any {
|
this._errors.push(new I18nError(node.sourceSpan, message));
|
||||||
return `${expansionCase.value} {${ml.visitAll(this, expansionCase.expression, null)}}`;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert ml nodes (xtb syntax) to i18n nodes
|
||||||
|
class XmlToI18n implements ml.Visitor {
|
||||||
|
private _errors: I18nError[];
|
||||||
|
|
||||||
|
convert(nodes: ml.Node[]) {
|
||||||
|
this._errors = [];
|
||||||
|
return {
|
||||||
|
i18nNodes: ml.visitAll(this, nodes),
|
||||||
|
errors: this._errors,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
visitText(text: ml.Text, context: any) { return new i18n.Text(text.value, text.sourceSpan); }
|
||||||
|
|
||||||
|
visitExpansion(icu: ml.Expansion, context: any) {
|
||||||
|
const caseMap: {[value: string]: i18n.Node} = {};
|
||||||
|
|
||||||
|
ml.visitAll(this, icu.cases).forEach(c => {
|
||||||
|
caseMap[c.value] = new i18n.Container(c.nodes, icu.sourceSpan);
|
||||||
|
});
|
||||||
|
|
||||||
|
return new i18n.Icu(icu.switchValue, icu.type, caseMap, icu.sourceSpan);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitExpansionCase(icuCase: ml.ExpansionCase, context: any): any {
|
||||||
|
return {
|
||||||
|
value: icuCase.value,
|
||||||
|
nodes: ml.visitAll(this, icuCase.expression),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
visitElement(el: ml.Element, context: any): i18n.Placeholder {
|
||||||
|
if (el.name === _PLACEHOLDER_TAG) {
|
||||||
|
const nameAttr = el.attrs.find((attr) => attr.name === 'name');
|
||||||
|
if (nameAttr) {
|
||||||
|
return new i18n.Placeholder('', nameAttr.value, el.sourceSpan);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "name" attribute`);
|
||||||
|
} else {
|
||||||
|
this._addError(el, `Unexpected tag`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visitComment(comment: ml.Comment, context: any) {}
|
||||||
|
|
||||||
|
visitAttribute(attribute: ml.Attribute, context: any) {}
|
||||||
|
|
||||||
private _addError(node: ml.Node, message: string): void {
|
private _addError(node: ml.Node, message: string): void {
|
||||||
this._errors.push(new I18nError(node.sourceSpan, message));
|
this._errors.push(new I18nError(node.sourceSpan, message));
|
||||||
|
@ -7,22 +7,120 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as html from '../ml_parser/ast';
|
import * as html from '../ml_parser/ast';
|
||||||
|
import {HtmlParser} from '../ml_parser/html_parser';
|
||||||
|
|
||||||
import {MessageBundle} from './message_bundle';
|
import * as i18n from './i18n_ast';
|
||||||
|
import {I18nError} from './parse_util';
|
||||||
import {Serializer} from './serializers/serializer';
|
import {Serializer} from './serializers/serializer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A container for translated messages
|
* A container for translated messages
|
||||||
*/
|
*/
|
||||||
export class TranslationBundle {
|
export class TranslationBundle {
|
||||||
constructor(private _messageMap: {[id: string]: html.Node[]} = {}) {}
|
private _i18nToHtml: I18nToHtmlVisitor;
|
||||||
|
|
||||||
static load(content: string, url: string, messageBundle: MessageBundle, serializer: Serializer):
|
constructor(
|
||||||
TranslationBundle {
|
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
|
||||||
return new TranslationBundle(serializer.load(content, url, messageBundle));
|
public digest: (m: i18n.Message) => string) {
|
||||||
|
this._i18nToHtml = new I18nToHtmlVisitor(_i18nNodesByMsgId, digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
get(id: string): html.Node[] { return this._messageMap[id]; }
|
static load(content: string, url: string, serializer: Serializer): TranslationBundle {
|
||||||
|
const i18nNodesByMsgId = serializer.load(content, url);
|
||||||
|
const digestFn = (m: i18n.Message) => serializer.digest(m);
|
||||||
|
return new TranslationBundle(i18nNodesByMsgId, digestFn);
|
||||||
|
}
|
||||||
|
|
||||||
has(id: string): boolean { return id in this._messageMap; }
|
get(srcMsg: i18n.Message): html.Node[] {
|
||||||
|
const html = this._i18nToHtml.convert(srcMsg);
|
||||||
|
|
||||||
|
if (html.errors.length) {
|
||||||
|
throw new Error(html.errors.join('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return html.nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
has(srcMsg: i18n.Message): boolean { return this.digest(srcMsg) in this._i18nNodesByMsgId; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class I18nToHtmlVisitor implements i18n.Visitor {
|
||||||
|
private _srcMsg: i18n.Message;
|
||||||
|
private _srcMsgStack: i18n.Message[] = [];
|
||||||
|
private _errors: I18nError[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
|
||||||
|
private _digest: (m: i18n.Message) => string) {}
|
||||||
|
|
||||||
|
convert(srcMsg: i18n.Message): {nodes: html.Node[], errors: I18nError[]} {
|
||||||
|
this._srcMsgStack.length = 0;
|
||||||
|
this._errors.length = 0;
|
||||||
|
// i18n to text
|
||||||
|
const text = this._convertToText(srcMsg);
|
||||||
|
|
||||||
|
// text to html
|
||||||
|
const url = srcMsg.nodes[0].sourceSpan.start.file.url;
|
||||||
|
const html = new HtmlParser().parse(text, url, true);
|
||||||
|
|
||||||
|
return {
|
||||||
|
nodes: html.rootNodes,
|
||||||
|
errors: [...this._errors, ...html.errors],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
visitText(text: i18n.Text, context?: any): string { return text.value; }
|
||||||
|
|
||||||
|
visitContainer(container: i18n.Container, context?: any): any {
|
||||||
|
return container.children.map(n => n.visit(this)).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
visitIcu(icu: i18n.Icu, context?: any): any {
|
||||||
|
const cases = Object.keys(icu.cases).map(k => `${k} {${icu.cases[k].visit(this)}}`);
|
||||||
|
|
||||||
|
// TODO(vicb): Once all format switch to using expression placeholders
|
||||||
|
// we should throw when the placeholder is not in the source message
|
||||||
|
const exp = this._srcMsg.placeholders.hasOwnProperty(icu.expression) ?
|
||||||
|
this._srcMsg.placeholders[icu.expression] :
|
||||||
|
icu.expression;
|
||||||
|
|
||||||
|
return `{${exp}, ${icu.type}, ${cases.join(' ')}}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitPlaceholder(ph: i18n.Placeholder, context?: any): string {
|
||||||
|
const phName = ph.name;
|
||||||
|
if (this._srcMsg.placeholders.hasOwnProperty(phName)) {
|
||||||
|
return this._srcMsg.placeholders[phName];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._srcMsg.placeholderToMessage.hasOwnProperty(phName)) {
|
||||||
|
return this._convertToText(this._srcMsg.placeholderToMessage[phName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._addError(ph, `Unknown placeholder`);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
visitTagPlaceholder(ph: i18n.TagPlaceholder, context?: any): any { throw 'unreachable code'; }
|
||||||
|
|
||||||
|
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any { throw 'unreachable code'; }
|
||||||
|
|
||||||
|
private _convertToText(srcMsg: i18n.Message): string {
|
||||||
|
const digest = this._digest(srcMsg);
|
||||||
|
if (this._i18nNodesByMsgId.hasOwnProperty(digest)) {
|
||||||
|
this._srcMsgStack.push(this._srcMsg);
|
||||||
|
this._srcMsg = srcMsg;
|
||||||
|
const nodes = this._i18nNodesByMsgId[digest];
|
||||||
|
const text = nodes.map(node => node.visit(this)).join('');
|
||||||
|
this._srcMsg = this._srcMsgStack.pop();
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._addError(srcMsg.nodes[0], `Missing translation for message ${digest}`);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private _addError(el: i18n.Node, msg: string) {
|
||||||
|
this._errors.push(new I18nError(el.sourceSpan, msg));
|
||||||
|
}
|
||||||
}
|
}
|
@ -8,8 +8,8 @@
|
|||||||
|
|
||||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, AnimationTransitionEvent, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
import {ANALYZE_FOR_ENTRY_COMPONENTS, AnimationTransitionEvent, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
||||||
|
|
||||||
import {StaticSymbol, isStaticSymbol} from './aot/static_symbol';
|
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_, UNINITIALIZED, 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');
|
||||||
@ -347,27 +347,25 @@ export function assetUrl(pkg: string, path: string = null, type: string = 'src')
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function resolveIdentifier(identifier: IdentifierSpec) {
|
export function resolveIdentifier(identifier: IdentifierSpec) {
|
||||||
let moduleUrl = identifier.moduleUrl;
|
return reflector.resolveIdentifier(identifier.name, identifier.moduleUrl, identifier.runtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createIdentifier(identifier: IdentifierSpec): CompileIdentifierMetadata {
|
||||||
const reference =
|
const reference =
|
||||||
reflector.resolveIdentifier(identifier.name, identifier.moduleUrl, identifier.runtime);
|
reflector.resolveIdentifier(identifier.name, identifier.moduleUrl, identifier.runtime);
|
||||||
if (isStaticSymbol(reference)) {
|
return {reference: reference};
|
||||||
moduleUrl = reference.filePath;
|
|
||||||
}
|
|
||||||
return new CompileIdentifierMetadata(
|
|
||||||
{name: identifier.name, moduleUrl: moduleUrl, reference: reference});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function identifierToken(identifier: CompileIdentifierMetadata): CompileTokenMetadata {
|
export function identifierToken(identifier: CompileIdentifierMetadata): CompileTokenMetadata {
|
||||||
return new CompileTokenMetadata({identifier: identifier});
|
return {identifier: identifier};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveIdentifierToken(identifier: IdentifierSpec): CompileTokenMetadata {
|
export function createIdentifierToken(identifier: IdentifierSpec): CompileTokenMetadata {
|
||||||
return identifierToken(resolveIdentifier(identifier));
|
return identifierToken(createIdentifier(identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveEnumIdentifier(
|
export function createEnumIdentifier(
|
||||||
enumType: CompileIdentifierMetadata, name: string): CompileIdentifierMetadata {
|
enumType: IdentifierSpec, name: string): CompileIdentifierMetadata {
|
||||||
const resolvedEnum = reflector.resolveEnum(enumType.reference, name);
|
const resolvedEnum = reflector.resolveEnum(resolveIdentifier(enumType), name);
|
||||||
return new CompileIdentifierMetadata(
|
return {reference: resolvedEnum};
|
||||||
{name: `${enumType.name}.${name}`, moduleUrl: enumType.moduleUrl, reference: resolvedEnum});
|
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import {Compiler, ComponentFactory, Injectable, Injector, ModuleWithComponentFac
|
|||||||
|
|
||||||
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} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta, identifierName} from '../compile_metadata';
|
||||||
import {CompilerConfig} from '../config';
|
import {CompilerConfig} from '../config';
|
||||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||||
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||||
@ -70,6 +70,14 @@ export class JitCompiler implements Compiler {
|
|||||||
return this._compileModuleAndAllComponents(moduleType, false).asyncResult;
|
return this._compileModuleAndAllComponents(moduleType, false).asyncResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNgContentSelectors(component: Type<any>): string[] {
|
||||||
|
const template = this._compiledTemplateCache.get(component);
|
||||||
|
if (!template) {
|
||||||
|
throw new Error(`The component ${stringify(component)} is not yet compiled!`);
|
||||||
|
}
|
||||||
|
return template.compMeta.template.ngContentSelectors;
|
||||||
|
}
|
||||||
|
|
||||||
private _compileModuleAndComponents<T>(moduleType: Type<T>, isSync: boolean):
|
private _compileModuleAndComponents<T>(moduleType: Type<T>, isSync: boolean):
|
||||||
SyncAsyncResult<NgModuleFactory<T>> {
|
SyncAsyncResult<NgModuleFactory<T>> {
|
||||||
const loadingPromise = this._loadModules(moduleType, isSync);
|
const loadingPromise = this._loadModules(moduleType, isSync);
|
||||||
@ -101,16 +109,14 @@ export class JitCompiler implements Compiler {
|
|||||||
|
|
||||||
private _loadModules(mainModule: any, isSync: boolean): Promise<any> {
|
private _loadModules(mainModule: any, isSync: boolean): Promise<any> {
|
||||||
const loadingPromises: Promise<any>[] = [];
|
const loadingPromises: Promise<any>[] = [];
|
||||||
const {ngModule, loading} = this._metadataResolver.loadNgModuleMetadata(mainModule, isSync);
|
const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule);
|
||||||
loadingPromises.push(loading);
|
|
||||||
// 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 loadNgModuleMetadata 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(
|
loadingPromises.push(this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
|
||||||
this._metadataResolver.loadNgModuleMetadata(localModuleMeta.type.reference, isSync)
|
localModuleMeta.reference, isSync));
|
||||||
.loading);
|
|
||||||
});
|
});
|
||||||
return Promise.all(loadingPromises);
|
return Promise.all(loadingPromises);
|
||||||
}
|
}
|
||||||
@ -126,14 +132,13 @@ export class JitCompiler implements Compiler {
|
|||||||
compileResult.dependencies.forEach((dep) => {
|
compileResult.dependencies.forEach((dep) => {
|
||||||
dep.placeholder.reference =
|
dep.placeholder.reference =
|
||||||
this._assertComponentKnown(dep.comp.reference, true).proxyComponentFactory;
|
this._assertComponentKnown(dep.comp.reference, true).proxyComponentFactory;
|
||||||
dep.placeholder.name = `compFactory_${dep.comp.name}`;
|
|
||||||
});
|
});
|
||||||
if (!this._compilerConfig.useJit) {
|
if (!this._compilerConfig.useJit) {
|
||||||
ngModuleFactory =
|
ngModuleFactory =
|
||||||
interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar);
|
interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar);
|
||||||
} else {
|
} else {
|
||||||
ngModuleFactory = jitStatements(
|
ngModuleFactory = jitStatements(
|
||||||
`/${moduleMeta.type.name}/module.ngfactory.js`, compileResult.statements,
|
`/${identifierName(moduleMeta.type)}/module.ngfactory.js`, compileResult.statements,
|
||||||
compileResult.ngModuleFactoryVar);
|
compileResult.ngModuleFactoryVar);
|
||||||
}
|
}
|
||||||
this._compiledNgModuleCache.set(moduleMeta.type.reference, ngModuleFactory);
|
this._compiledNgModuleCache.set(moduleMeta.type.reference, ngModuleFactory);
|
||||||
@ -151,7 +156,7 @@ export class JitCompiler implements Compiler {
|
|||||||
|
|
||||||
ngModule.transitiveModule.modules.forEach((localModuleSummary) => {
|
ngModule.transitiveModule.modules.forEach((localModuleSummary) => {
|
||||||
const localModuleMeta =
|
const localModuleMeta =
|
||||||
this._metadataResolver.getNgModuleMetadata(localModuleSummary.type.reference);
|
this._metadataResolver.getNgModuleMetadata(localModuleSummary.reference);
|
||||||
localModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
|
localModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
|
||||||
moduleByDirective.set(dirIdentifier.reference, localModuleMeta);
|
moduleByDirective.set(dirIdentifier.reference, localModuleMeta);
|
||||||
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
|
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
|
||||||
@ -169,7 +174,7 @@ export class JitCompiler implements Compiler {
|
|||||||
});
|
});
|
||||||
ngModule.transitiveModule.modules.forEach((localModuleSummary) => {
|
ngModule.transitiveModule.modules.forEach((localModuleSummary) => {
|
||||||
const localModuleMeta =
|
const localModuleMeta =
|
||||||
this._metadataResolver.getNgModuleMetadata(localModuleSummary.type.reference);
|
this._metadataResolver.getNgModuleMetadata(localModuleSummary.reference);
|
||||||
localModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
|
localModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
|
||||||
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
|
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
|
||||||
if (dirMeta.isComponent) {
|
if (dirMeta.isComponent) {
|
||||||
@ -215,7 +220,11 @@ export class JitCompiler implements Compiler {
|
|||||||
if (!compiledTemplate) {
|
if (!compiledTemplate) {
|
||||||
const compMeta = this._metadataResolver.getDirectiveMetadata(compType);
|
const compMeta = this._metadataResolver.getDirectiveMetadata(compType);
|
||||||
assertComponent(compMeta);
|
assertComponent(compMeta);
|
||||||
const hostMeta = createHostComponentMeta(compMeta);
|
|
||||||
|
const HostClass = function HostClass() {};
|
||||||
|
(<any>HostClass).overriddenName = `${identifierName(compMeta.type)}_Host`;
|
||||||
|
|
||||||
|
const hostMeta = createHostComponentMeta(HostClass, compMeta);
|
||||||
compiledTemplate = new CompiledTemplate(
|
compiledTemplate = new CompiledTemplate(
|
||||||
true, compMeta.selector, compMeta.type, hostMeta, ngModule, [compMeta.type]);
|
true, compMeta.selector, compMeta.type, hostMeta, ngModule, [compMeta.type]);
|
||||||
this._compiledHostTemplateCache.set(compType, compiledTemplate);
|
this._compiledHostTemplateCache.set(compType, compiledTemplate);
|
||||||
@ -264,8 +273,8 @@ export class JitCompiler implements Compiler {
|
|||||||
directiveWrapperClass = interpretStatements(statements, compileResult.dirWrapperClassVar);
|
directiveWrapperClass = interpretStatements(statements, compileResult.dirWrapperClassVar);
|
||||||
} else {
|
} else {
|
||||||
directiveWrapperClass = jitStatements(
|
directiveWrapperClass = jitStatements(
|
||||||
`/${moduleMeta.type.name}/${dirMeta.type.name}/wrapper.ngfactory.js`, statements,
|
`/${identifierName(moduleMeta.type)}/${identifierName(dirMeta.type)}/wrapper.ngfactory.js`,
|
||||||
compileResult.dirWrapperClassVar);
|
statements, compileResult.dirWrapperClassVar);
|
||||||
}
|
}
|
||||||
this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass);
|
this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass);
|
||||||
}
|
}
|
||||||
@ -288,9 +297,9 @@ export class JitCompiler implements Compiler {
|
|||||||
pipe => this._metadataResolver.getPipeSummary(pipe.reference));
|
pipe => this._metadataResolver.getPipeSummary(pipe.reference));
|
||||||
const parsedTemplate = this._templateParser.parse(
|
const parsedTemplate = this._templateParser.parse(
|
||||||
compMeta, compMeta.template.template, directives, pipes, template.ngModule.schemas,
|
compMeta, compMeta.template.template, directives, pipes, template.ngModule.schemas,
|
||||||
compMeta.type.name);
|
identifierName(compMeta.type));
|
||||||
const compiledAnimations =
|
const compiledAnimations =
|
||||||
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
|
this._animationCompiler.compile(identifierName(compMeta.type), parsedAnimations);
|
||||||
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);
|
||||||
@ -300,12 +309,10 @@ export class JitCompiler implements Compiler {
|
|||||||
const vfd = <ViewClassDependency>dep;
|
const vfd = <ViewClassDependency>dep;
|
||||||
depTemplate = this._assertComponentKnown(vfd.comp.reference, false);
|
depTemplate = this._assertComponentKnown(vfd.comp.reference, false);
|
||||||
vfd.placeholder.reference = depTemplate.proxyViewClass;
|
vfd.placeholder.reference = depTemplate.proxyViewClass;
|
||||||
vfd.placeholder.name = `View_${vfd.comp.name}`;
|
|
||||||
} else if (dep instanceof ComponentFactoryDependency) {
|
} else if (dep instanceof ComponentFactoryDependency) {
|
||||||
const cfd = <ComponentFactoryDependency>dep;
|
const cfd = <ComponentFactoryDependency>dep;
|
||||||
depTemplate = this._assertComponentKnown(cfd.comp.reference, true);
|
depTemplate = this._assertComponentKnown(cfd.comp.reference, true);
|
||||||
cfd.placeholder.reference = depTemplate.proxyComponentFactory;
|
cfd.placeholder.reference = depTemplate.proxyComponentFactory;
|
||||||
cfd.placeholder.name = `compFactory_${cfd.comp.name}`;
|
|
||||||
} else if (dep instanceof DirectiveWrapperDependency) {
|
} else if (dep instanceof DirectiveWrapperDependency) {
|
||||||
const dwd = <DirectiveWrapperDependency>dep;
|
const dwd = <DirectiveWrapperDependency>dep;
|
||||||
dwd.placeholder.reference = this._assertDirectiveWrapper(dwd.dir.reference);
|
dwd.placeholder.reference = this._assertDirectiveWrapper(dwd.dir.reference);
|
||||||
@ -319,7 +326,7 @@ export class JitCompiler implements Compiler {
|
|||||||
viewClass = interpretStatements(statements, compileResult.viewClassVar);
|
viewClass = interpretStatements(statements, compileResult.viewClassVar);
|
||||||
} else {
|
} else {
|
||||||
viewClass = jitStatements(
|
viewClass = jitStatements(
|
||||||
`/${template.ngModule.type.name}/${template.compType.name}/${template.isHost?'host':'component'}.ngfactory.js`,
|
`/${identifierName(template.ngModule.type)}/${identifierName(template.compType)}/${template.isHost?'host':'component'}.ngfactory.js`,
|
||||||
statements, compileResult.viewClassVar);
|
statements, compileResult.viewClassVar);
|
||||||
}
|
}
|
||||||
template.compiled(viewClass);
|
template.compiled(viewClass);
|
||||||
@ -332,7 +339,6 @@ export class JitCompiler implements Compiler {
|
|||||||
const nestedStylesArr = this._resolveAndEvalStylesCompileResult(
|
const nestedStylesArr = this._resolveAndEvalStylesCompileResult(
|
||||||
nestedCompileResult, externalStylesheetsByModuleUrl);
|
nestedCompileResult, externalStylesheetsByModuleUrl);
|
||||||
dep.valuePlaceholder.reference = nestedStylesArr;
|
dep.valuePlaceholder.reference = nestedStylesArr;
|
||||||
dep.valuePlaceholder.name = `importedStyles${i}`;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,7 +349,8 @@ export class JitCompiler implements Compiler {
|
|||||||
if (!this._compilerConfig.useJit) {
|
if (!this._compilerConfig.useJit) {
|
||||||
return interpretStatements(result.statements, result.stylesVar);
|
return interpretStatements(result.statements, result.stylesVar);
|
||||||
} else {
|
} else {
|
||||||
return jitStatements(`/${result.meta.moduleUrl}.css.js`, result.statements, result.stylesVar);
|
return jitStatements(
|
||||||
|
`/${result.meta.moduleUrl}.ngstyle.js`, result.statements, result.stylesVar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -380,7 +387,8 @@ class CompiledTemplate {
|
|||||||
|
|
||||||
function assertComponent(meta: CompileDirectiveMetadata) {
|
function assertComponent(meta: CompileDirectiveMetadata) {
|
||||||
if (!meta.isComponent) {
|
if (!meta.isComponent) {
|
||||||
throw new Error(`Could not compile '${meta.type.name}' because it is not a component.`);
|
throw new Error(
|
||||||
|
`Could not compile '${identifierName(meta.type)}' because it is not a component.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,6 +416,11 @@ class ModuleBoundCompiler implements Compiler {
|
|||||||
return this._delegate.compileModuleAndAllComponentsAsync(moduleType);
|
return this._delegate.compileModuleAndAllComponentsAsync(moduleType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNgContentSelectors(component: Type<any>): string[] {
|
||||||
|
return this._delegate.getNgContentSelectors(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all caches
|
* Clears all caches
|
||||||
*/
|
*/
|
||||||
|
@ -26,6 +26,7 @@ import {ResourceLoader} from '../resource_loader';
|
|||||||
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
|
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
|
||||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||||
import {StyleCompiler} from '../style_compiler';
|
import {StyleCompiler} from '../style_compiler';
|
||||||
|
import {SummaryResolver} from '../summary_resolver';
|
||||||
import {TemplateParser} from '../template_parser/template_parser';
|
import {TemplateParser} from '../template_parser/template_parser';
|
||||||
import {DEFAULT_PACKAGE_URL_PROVIDER, UrlResolver} from '../url_resolver';
|
import {DEFAULT_PACKAGE_URL_PROVIDER, UrlResolver} from '../url_resolver';
|
||||||
import {ViewCompiler} from '../view_compiler/view_compiler';
|
import {ViewCompiler} from '../view_compiler/view_compiler';
|
||||||
@ -46,6 +47,7 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
|
|||||||
{provide: Reflector, useValue: reflector},
|
{provide: Reflector, useValue: reflector},
|
||||||
{provide: ReflectorReader, useExisting: Reflector},
|
{provide: ReflectorReader, useExisting: Reflector},
|
||||||
{provide: ResourceLoader, useValue: _NO_RESOURCE_LOADER},
|
{provide: ResourceLoader, useValue: _NO_RESOURCE_LOADER},
|
||||||
|
SummaryResolver,
|
||||||
Console,
|
Console,
|
||||||
Lexer,
|
Lexer,
|
||||||
Parser,
|
Parser,
|
||||||
|
@ -6,25 +6,27 @@
|
|||||||
* 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, Host, Inject, Injectable, ModuleWithProviders, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
|
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, Directive, Host, Inject, Injectable, ModuleWithProviders, OpaqueToken, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
|
||||||
|
|
||||||
import {isStaticSymbol} from './aot/static_symbol';
|
import {StaticSymbol} from './aot/static_symbol';
|
||||||
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 {ListWrapper, StringMapWrapper} from './facade/collection';
|
||||||
import {isBlank, isPresent, stringify} from './facade/lang';
|
import {isBlank, isPresent, stringify} from './facade/lang';
|
||||||
import {Identifiers, resolveIdentifierToken} from './identifiers';
|
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
|
||||||
import {hasLifecycleHook} from './lifecycle_reflector';
|
import {hasLifecycleHook} from './lifecycle_reflector';
|
||||||
import {NgModuleResolver} from './ng_module_resolver';
|
import {NgModuleResolver} from './ng_module_resolver';
|
||||||
import {PipeResolver} from './pipe_resolver';
|
import {PipeResolver} from './pipe_resolver';
|
||||||
import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from './private_import_core';
|
import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from './private_import_core';
|
||||||
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
||||||
|
import {SummaryResolver} from './summary_resolver';
|
||||||
import {getUrlScheme} from './url_resolver';
|
import {getUrlScheme} from './url_resolver';
|
||||||
import {MODULE_SUFFIX, SyncAsyncResult, ValueTransformer, sanitizeIdentifier, visitValue} from './util';
|
import {MODULE_SUFFIX, SyncAsyncResult, ValueTransformer, visitValue} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
export type ErrorCollector = (error: any, type?: any) => void;
|
||||||
|
export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
|
||||||
|
|
||||||
// Design notes:
|
// Design notes:
|
||||||
// - don't lazily create metadata:
|
// - don't lazily create metadata:
|
||||||
@ -36,40 +38,24 @@ import {MODULE_SUFFIX, SyncAsyncResult, ValueTransformer, sanitizeIdentifier, vi
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class CompileMetadataResolver {
|
export class CompileMetadataResolver {
|
||||||
private _directiveCache = new Map<Type<any>, cpl.CompileDirectiveMetadata>();
|
private _directiveCache = new Map<Type<any>, cpl.CompileDirectiveMetadata>();
|
||||||
private _directiveSummaryCache = new Map<Type<any>, cpl.CompileDirectiveSummary>();
|
private _summaryCache = new Map<Type<any>, cpl.CompileTypeSummary>();
|
||||||
private _pipeCache = new Map<Type<any>, cpl.CompilePipeMetadata>();
|
private _pipeCache = new Map<Type<any>, cpl.CompilePipeMetadata>();
|
||||||
private _pipeSummaryCache = new Map<Type<any>, cpl.CompilePipeSummary>();
|
|
||||||
private _ngModuleCache = new Map<Type<any>, cpl.CompileNgModuleMetadata>();
|
private _ngModuleCache = new Map<Type<any>, cpl.CompileNgModuleMetadata>();
|
||||||
private _ngModuleOfTypes = new Map<Type<any>, Type<any>>();
|
private _ngModuleOfTypes = new Map<Type<any>, Type<any>>();
|
||||||
private _anonymousTypes = new Map<Object, number>();
|
|
||||||
private _anonymousTypeIndex = 0;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver,
|
private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver,
|
||||||
private _pipeResolver: PipeResolver, private _schemaRegistry: ElementSchemaRegistry,
|
private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver,
|
||||||
|
private _schemaRegistry: ElementSchemaRegistry,
|
||||||
private _directiveNormalizer: DirectiveNormalizer,
|
private _directiveNormalizer: DirectiveNormalizer,
|
||||||
private _reflector: ReflectorReader = reflector) {}
|
private _reflector: ReflectorReader = reflector,
|
||||||
|
@Optional() @Inject(ERROR_COLLECTOR_TOKEN) private _errorCollector?: ErrorCollector) {}
|
||||||
private sanitizeTokenName(token: any): string {
|
|
||||||
let identifier = stringify(token);
|
|
||||||
if (identifier.indexOf('(') >= 0) {
|
|
||||||
// case: anonymous functions!
|
|
||||||
let found = this._anonymousTypes.get(token);
|
|
||||||
if (!found) {
|
|
||||||
this._anonymousTypes.set(token, this._anonymousTypeIndex++);
|
|
||||||
found = this._anonymousTypes.get(token);
|
|
||||||
}
|
|
||||||
identifier = `anonymous_token_${found}_`;
|
|
||||||
}
|
|
||||||
return sanitizeIdentifier(identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
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._directiveSummaryCache.delete(type);
|
this._summaryCache.delete(type);
|
||||||
this._pipeCache.delete(type);
|
this._pipeCache.delete(type);
|
||||||
this._pipeSummaryCache.delete(type);
|
|
||||||
this._ngModuleOfTypes.delete(type);
|
this._ngModuleOfTypes.delete(type);
|
||||||
// Clear all of the NgModule as they contain transitive information!
|
// Clear all of the NgModule as they contain transitive information!
|
||||||
this._ngModuleCache.clear();
|
this._ngModuleCache.clear();
|
||||||
@ -80,9 +66,8 @@ export class CompileMetadataResolver {
|
|||||||
|
|
||||||
clearCache() {
|
clearCache() {
|
||||||
this._directiveCache.clear();
|
this._directiveCache.clear();
|
||||||
this._directiveSummaryCache.clear();
|
this._summaryCache.clear();
|
||||||
this._pipeCache.clear();
|
this._pipeCache.clear();
|
||||||
this._pipeSummaryCache.clear();
|
|
||||||
this._ngModuleCache.clear();
|
this._ngModuleCache.clear();
|
||||||
this._ngModuleOfTypes.clear();
|
this._ngModuleOfTypes.clear();
|
||||||
this._directiveNormalizer.clearCache();
|
this._directiveNormalizer.clearCache();
|
||||||
@ -142,55 +127,65 @@ export class CompileMetadataResolver {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _loadSummary(type: any, kind: cpl.CompileSummaryKind): cpl.CompileTypeSummary {
|
||||||
|
let summary = this._summaryCache.get(type);
|
||||||
|
if (!summary) {
|
||||||
|
summary = this._summaryResolver.resolveSummary(type);
|
||||||
|
this._summaryCache.set(type, summary);
|
||||||
|
}
|
||||||
|
return summary && summary.summaryKind === kind ? summary : null;
|
||||||
|
}
|
||||||
|
|
||||||
private _loadDirectiveMetadata(directiveType: any, isSync: boolean): Promise<any> {
|
private _loadDirectiveMetadata(directiveType: any, isSync: boolean): Promise<any> {
|
||||||
if (this._directiveCache.has(directiveType)) {
|
if (this._directiveCache.has(directiveType)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
directiveType = resolveForwardRef(directiveType);
|
directiveType = resolveForwardRef(directiveType);
|
||||||
const nonNormalizedMetadata = this.getNonNormalizedDirectiveMetadata(directiveType);
|
const {annotation, metadata} = this.getNonNormalizedDirectiveMetadata(directiveType);
|
||||||
|
|
||||||
const createDirectiveMetadata = (templateMetadata: cpl.CompileTemplateMetadata) => {
|
const createDirectiveMetadata = (templateMetadata: cpl.CompileTemplateMetadata) => {
|
||||||
const normalizedDirMeta = new cpl.CompileDirectiveMetadata({
|
const normalizedDirMeta = new cpl.CompileDirectiveMetadata({
|
||||||
type: nonNormalizedMetadata.type,
|
type: metadata.type,
|
||||||
isComponent: nonNormalizedMetadata.isComponent,
|
isComponent: metadata.isComponent,
|
||||||
selector: nonNormalizedMetadata.selector,
|
selector: metadata.selector,
|
||||||
exportAs: nonNormalizedMetadata.exportAs,
|
exportAs: metadata.exportAs,
|
||||||
changeDetection: nonNormalizedMetadata.changeDetection,
|
changeDetection: metadata.changeDetection,
|
||||||
inputs: nonNormalizedMetadata.inputs,
|
inputs: metadata.inputs,
|
||||||
outputs: nonNormalizedMetadata.outputs,
|
outputs: metadata.outputs,
|
||||||
hostListeners: nonNormalizedMetadata.hostListeners,
|
hostListeners: metadata.hostListeners,
|
||||||
hostProperties: nonNormalizedMetadata.hostProperties,
|
hostProperties: metadata.hostProperties,
|
||||||
hostAttributes: nonNormalizedMetadata.hostAttributes,
|
hostAttributes: metadata.hostAttributes,
|
||||||
providers: nonNormalizedMetadata.providers,
|
providers: metadata.providers,
|
||||||
viewProviders: nonNormalizedMetadata.viewProviders,
|
viewProviders: metadata.viewProviders,
|
||||||
queries: nonNormalizedMetadata.queries,
|
queries: metadata.queries,
|
||||||
viewQueries: nonNormalizedMetadata.viewQueries,
|
viewQueries: metadata.viewQueries,
|
||||||
entryComponents: nonNormalizedMetadata.entryComponents,
|
entryComponents: metadata.entryComponents,
|
||||||
template: templateMetadata
|
template: templateMetadata
|
||||||
});
|
});
|
||||||
this._directiveCache.set(directiveType, normalizedDirMeta);
|
this._directiveCache.set(directiveType, normalizedDirMeta);
|
||||||
this._directiveSummaryCache.set(directiveType, normalizedDirMeta.toSummary());
|
this._summaryCache.set(directiveType, normalizedDirMeta.toSummary());
|
||||||
return normalizedDirMeta;
|
return normalizedDirMeta;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (nonNormalizedMetadata.isComponent) {
|
if (metadata.isComponent) {
|
||||||
const templateMeta = this._directiveNormalizer.normalizeTemplate({
|
const templateMeta = this._directiveNormalizer.normalizeTemplate({
|
||||||
componentType: directiveType,
|
componentType: directiveType,
|
||||||
moduleUrl: nonNormalizedMetadata.type.moduleUrl,
|
moduleUrl: componentModuleUrl(this._reflector, directiveType, annotation),
|
||||||
encapsulation: nonNormalizedMetadata.template.encapsulation,
|
encapsulation: metadata.template.encapsulation,
|
||||||
template: nonNormalizedMetadata.template.template,
|
template: metadata.template.template,
|
||||||
templateUrl: nonNormalizedMetadata.template.templateUrl,
|
templateUrl: metadata.template.templateUrl,
|
||||||
styles: nonNormalizedMetadata.template.styles,
|
styles: metadata.template.styles,
|
||||||
styleUrls: nonNormalizedMetadata.template.styleUrls,
|
styleUrls: metadata.template.styleUrls,
|
||||||
animations: nonNormalizedMetadata.template.animations,
|
animations: metadata.template.animations,
|
||||||
interpolation: nonNormalizedMetadata.template.interpolation
|
interpolation: metadata.template.interpolation
|
||||||
});
|
});
|
||||||
if (templateMeta.syncResult) {
|
if (templateMeta.syncResult) {
|
||||||
createDirectiveMetadata(templateMeta.syncResult);
|
createDirectiveMetadata(templateMeta.syncResult);
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
if (isSync) {
|
if (isSync) {
|
||||||
throw new ComponentStillLoadingError(directiveType);
|
this._reportError(new ComponentStillLoadingError(directiveType), directiveType);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return templateMeta.asyncResult.then(createDirectiveMetadata);
|
return templateMeta.asyncResult.then(createDirectiveMetadata);
|
||||||
}
|
}
|
||||||
@ -201,18 +196,17 @@ export class CompileMetadataResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getNonNormalizedDirectiveMetadata(directiveType: any): cpl.CompileDirectiveMetadata {
|
getNonNormalizedDirectiveMetadata(directiveType: any):
|
||||||
|
{annotation: Directive, metadata: cpl.CompileDirectiveMetadata} {
|
||||||
directiveType = resolveForwardRef(directiveType);
|
directiveType = resolveForwardRef(directiveType);
|
||||||
const dirMeta = this._directiveResolver.resolve(directiveType);
|
const dirMeta = this._directiveResolver.resolve(directiveType);
|
||||||
if (!dirMeta) {
|
if (!dirMeta) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let moduleUrl = staticTypeModuleUrl(directiveType);
|
|
||||||
let nonNormalizedTemplateMetadata: cpl.CompileTemplateMetadata;
|
let nonNormalizedTemplateMetadata: cpl.CompileTemplateMetadata;
|
||||||
|
|
||||||
if (dirMeta instanceof Component) {
|
if (dirMeta instanceof Component) {
|
||||||
// component
|
// component
|
||||||
moduleUrl = componentModuleUrl(this._reflector, directiveType, dirMeta);
|
|
||||||
assertArrayOfStrings('styles', dirMeta.styles);
|
assertArrayOfStrings('styles', dirMeta.styles);
|
||||||
assertArrayOfStrings('styleUrls', dirMeta.styleUrls);
|
assertArrayOfStrings('styleUrls', dirMeta.styleUrls);
|
||||||
assertInterpolationSymbols('interpolation', dirMeta.interpolation);
|
assertInterpolationSymbols('interpolation', dirMeta.interpolation);
|
||||||
@ -233,7 +227,7 @@ export class CompileMetadataResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let changeDetectionStrategy: ChangeDetectionStrategy = null;
|
let changeDetectionStrategy: ChangeDetectionStrategy = null;
|
||||||
let viewProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
let viewProviders: cpl.CompileProviderMetadata[] = [];
|
||||||
let entryComponentMetadata: cpl.CompileIdentifierMetadata[] = [];
|
let entryComponentMetadata: cpl.CompileIdentifierMetadata[] = [];
|
||||||
let selector = dirMeta.selector;
|
let selector = dirMeta.selector;
|
||||||
|
|
||||||
@ -243,12 +237,11 @@ export class CompileMetadataResolver {
|
|||||||
if (dirMeta.viewProviders) {
|
if (dirMeta.viewProviders) {
|
||||||
viewProviders = this._getProvidersMetadata(
|
viewProviders = this._getProvidersMetadata(
|
||||||
dirMeta.viewProviders, entryComponentMetadata,
|
dirMeta.viewProviders, entryComponentMetadata,
|
||||||
`viewProviders for "${stringify(directiveType)}"`);
|
`viewProviders for "${stringify(directiveType)}"`, [], directiveType);
|
||||||
}
|
}
|
||||||
if (dirMeta.entryComponents) {
|
if (dirMeta.entryComponents) {
|
||||||
entryComponentMetadata =
|
entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents)
|
||||||
flattenAndDedupeArray(dirMeta.entryComponents)
|
.map((type) => this._getIdentifierMetadata(type))
|
||||||
.map((type) => this._getIdentifierMetadata(type, staticTypeModuleUrl(type)))
|
|
||||||
.concat(entryComponentMetadata);
|
.concat(entryComponentMetadata);
|
||||||
}
|
}
|
||||||
if (!selector) {
|
if (!selector) {
|
||||||
@ -257,14 +250,18 @@ export class CompileMetadataResolver {
|
|||||||
} else {
|
} else {
|
||||||
// Directive
|
// Directive
|
||||||
if (!selector) {
|
if (!selector) {
|
||||||
throw new Error(`Directive ${stringify(directiveType)} has no selector, please add it!`);
|
this._reportError(
|
||||||
|
new Error(`Directive ${stringify(directiveType)} has no selector, please add it!`),
|
||||||
|
directiveType);
|
||||||
|
selector = 'error';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let providers: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
let providers: cpl.CompileProviderMetadata[] = [];
|
||||||
if (isPresent(dirMeta.providers)) {
|
if (isPresent(dirMeta.providers)) {
|
||||||
providers = this._getProvidersMetadata(
|
providers = this._getProvidersMetadata(
|
||||||
dirMeta.providers, entryComponentMetadata, `providers for "${stringify(directiveType)}"`);
|
dirMeta.providers, entryComponentMetadata, `providers for "${stringify(directiveType)}"`,
|
||||||
|
[], directiveType);
|
||||||
}
|
}
|
||||||
let queries: cpl.CompileQueryMetadata[] = [];
|
let queries: cpl.CompileQueryMetadata[] = [];
|
||||||
let viewQueries: cpl.CompileQueryMetadata[] = [];
|
let viewQueries: cpl.CompileQueryMetadata[] = [];
|
||||||
@ -273,11 +270,11 @@ export class CompileMetadataResolver {
|
|||||||
viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType);
|
viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cpl.CompileDirectiveMetadata.create({
|
const metadata = cpl.CompileDirectiveMetadata.create({
|
||||||
selector: selector,
|
selector: selector,
|
||||||
exportAs: dirMeta.exportAs,
|
exportAs: dirMeta.exportAs,
|
||||||
isComponent: !!nonNormalizedTemplateMetadata,
|
isComponent: !!nonNormalizedTemplateMetadata,
|
||||||
type: this._getTypeMetadata(directiveType, moduleUrl),
|
type: this._getTypeMetadata(directiveType),
|
||||||
template: nonNormalizedTemplateMetadata,
|
template: nonNormalizedTemplateMetadata,
|
||||||
changeDetection: changeDetectionStrategy,
|
changeDetection: changeDetectionStrategy,
|
||||||
inputs: dirMeta.inputs,
|
inputs: dirMeta.inputs,
|
||||||
@ -289,6 +286,7 @@ export class CompileMetadataResolver {
|
|||||||
viewQueries: viewQueries,
|
viewQueries: viewQueries,
|
||||||
entryComponents: entryComponentMetadata
|
entryComponents: entryComponentMetadata
|
||||||
});
|
});
|
||||||
|
return {metadata, annotation: dirMeta};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -298,17 +296,22 @@ export class CompileMetadataResolver {
|
|||||||
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) {
|
||||||
throw new Error(
|
this._reportError(
|
||||||
`Illegal state: getDirectiveMetadata can only be called after loadNgModuleMetadata for a module that declares it. Directive ${stringify(directiveType)}.`);
|
new Error(
|
||||||
|
`Illegal state: getDirectiveMetadata can only be called after loadNgModuleMetadata for a module that declares it. Directive ${stringify(directiveType)}.`),
|
||||||
|
directiveType);
|
||||||
}
|
}
|
||||||
return dirMeta;
|
return dirMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirectiveSummary(dirType: any): cpl.CompileDirectiveSummary {
|
getDirectiveSummary(dirType: any): cpl.CompileDirectiveSummary {
|
||||||
const dirSummary = this._directiveSummaryCache.get(dirType);
|
const dirSummary =
|
||||||
|
<cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive);
|
||||||
if (!dirSummary) {
|
if (!dirSummary) {
|
||||||
throw new Error(
|
this._reportError(
|
||||||
`Illegal state: getDirectiveSummary can only be called after loadNgModuleMetadata for a module that imports it. Directive ${stringify(dirType)}.`);
|
new Error(
|
||||||
|
`Illegal state: Could not load the summary for directive ${stringify(dirType)}.`),
|
||||||
|
dirType);
|
||||||
}
|
}
|
||||||
return dirSummary;
|
return dirSummary;
|
||||||
}
|
}
|
||||||
@ -317,51 +320,39 @@ export class CompileMetadataResolver {
|
|||||||
|
|
||||||
isPipe(type: any) { return this._pipeResolver.isPipe(type); }
|
isPipe(type: any) { return this._pipeResolver.isPipe(type); }
|
||||||
|
|
||||||
/**
|
getNgModuleSummary(moduleType: any): cpl.CompileNgModuleSummary {
|
||||||
* Gets the metadata for the given module.
|
let moduleSummary =
|
||||||
* This assumes `loadNgModuleMetadata` has been called first.
|
<cpl.CompileNgModuleSummary>this._loadSummary(moduleType, cpl.CompileSummaryKind.NgModule);
|
||||||
*/
|
if (!moduleSummary) {
|
||||||
getNgModuleMetadata(moduleType: any): cpl.CompileNgModuleMetadata {
|
const moduleMeta = this.getNgModuleMetadata(moduleType, false);
|
||||||
const modMeta = this._ngModuleCache.get(moduleType);
|
moduleSummary = moduleMeta ? moduleMeta.toSummary() : null;
|
||||||
if (!modMeta) {
|
if (moduleSummary) {
|
||||||
throw new Error(
|
this._summaryCache.set(moduleType, moduleSummary);
|
||||||
`Illegal state: getNgModuleMetadata can only be called after loadNgModuleMetadata. Module ${stringify(moduleType)}.`);
|
|
||||||
}
|
}
|
||||||
return modMeta;
|
|
||||||
}
|
}
|
||||||
|
return moduleSummary;
|
||||||
private _loadNgModuleSummary(moduleType: any, isSync: boolean): cpl.CompileNgModuleSummary {
|
|
||||||
// TODO(tbosch): add logic to read summary files!
|
|
||||||
// - needs to add directive / pipe summaries to this._directiveSummaryCache /
|
|
||||||
// this._pipeSummaryCache as well!
|
|
||||||
const moduleMeta = this._loadNgModuleMetadata(moduleType, isSync, false);
|
|
||||||
return moduleMeta ? moduleMeta.toSummary() : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads an NgModule and all of its directives. This includes loading the exported directives of
|
* Loads the declared directives and pipes of an NgModule.
|
||||||
* imported modules,
|
|
||||||
* but not private directives of imported modules.
|
|
||||||
*/
|
*/
|
||||||
loadNgModuleMetadata(moduleType: any, isSync: boolean, throwIfNotFound = true):
|
loadNgModuleDirectiveAndPipeMetadata(moduleType: any, isSync: boolean, throwIfNotFound = true):
|
||||||
{ngModule: cpl.CompileNgModuleMetadata, loading: Promise<any>} {
|
Promise<any> {
|
||||||
const ngModule = this._loadNgModuleMetadata(moduleType, isSync, throwIfNotFound);
|
const ngModule = this.getNgModuleMetadata(moduleType, throwIfNotFound);
|
||||||
const loading = ngModule ?
|
const loading: Promise<any>[] = [];
|
||||||
Promise.all(ngModule.transitiveModule.directiveLoaders.map(loader => loader())) :
|
if (ngModule) {
|
||||||
Promise.resolve(null);
|
ngModule.declaredDirectives.forEach((id) => {
|
||||||
return {ngModule, loading};
|
const promise = this._loadDirectiveMetadata(id.reference, isSync);
|
||||||
|
if (promise) {
|
||||||
|
loading.push(promise);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ngModule.declaredPipes.forEach((id) => this._loadPipeMetadata(id.reference));
|
||||||
|
}
|
||||||
|
return Promise.all(loading);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
getNgModuleMetadata(moduleType: any, throwIfNotFound = true): cpl.CompileNgModuleMetadata {
|
||||||
* Get the NgModule metadata without loading the directives.
|
|
||||||
*/
|
|
||||||
getUnloadedNgModuleMetadata(moduleType: any, isSync: boolean, throwIfNotFound = true):
|
|
||||||
cpl.CompileNgModuleMetadata {
|
|
||||||
return this._loadNgModuleMetadata(moduleType, isSync, throwIfNotFound);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _loadNgModuleMetadata(moduleType: any, isSync: boolean, throwIfNotFound = true):
|
|
||||||
cpl.CompileNgModuleMetadata {
|
|
||||||
moduleType = resolveForwardRef(moduleType);
|
moduleType = resolveForwardRef(moduleType);
|
||||||
let compileMeta = this._ngModuleCache.get(moduleType);
|
let compileMeta = this._ngModuleCache.get(moduleType);
|
||||||
if (compileMeta) {
|
if (compileMeta) {
|
||||||
@ -376,7 +367,7 @@ export class CompileMetadataResolver {
|
|||||||
const declaredPipes: cpl.CompileIdentifierMetadata[] = [];
|
const declaredPipes: cpl.CompileIdentifierMetadata[] = [];
|
||||||
const importedModules: cpl.CompileNgModuleSummary[] = [];
|
const importedModules: cpl.CompileNgModuleSummary[] = [];
|
||||||
const exportedModules: cpl.CompileNgModuleSummary[] = [];
|
const exportedModules: cpl.CompileNgModuleSummary[] = [];
|
||||||
const providers: any[] = [];
|
const providers: cpl.CompileProviderMetadata[] = [];
|
||||||
const entryComponents: cpl.CompileIdentifierMetadata[] = [];
|
const entryComponents: cpl.CompileIdentifierMetadata[] = [];
|
||||||
const bootstrapComponents: cpl.CompileIdentifierMetadata[] = [];
|
const bootstrapComponents: cpl.CompileIdentifierMetadata[] = [];
|
||||||
const schemas: SchemaMetadata[] = [];
|
const schemas: SchemaMetadata[] = [];
|
||||||
@ -392,20 +383,26 @@ export class CompileMetadataResolver {
|
|||||||
if (moduleWithProviders.providers) {
|
if (moduleWithProviders.providers) {
|
||||||
providers.push(...this._getProvidersMetadata(
|
providers.push(...this._getProvidersMetadata(
|
||||||
moduleWithProviders.providers, entryComponents,
|
moduleWithProviders.providers, entryComponents,
|
||||||
`provider for the NgModule '${stringify(importedModuleType)}'`));
|
`provider for the NgModule '${stringify(importedModuleType)}'`, [], importedType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (importedModuleType) {
|
if (importedModuleType) {
|
||||||
const importedModuleSummary = this._loadNgModuleSummary(importedModuleType, isSync);
|
const importedModuleSummary = this.getNgModuleSummary(importedModuleType);
|
||||||
if (!importedModuleSummary) {
|
if (!importedModuleSummary) {
|
||||||
throw new Error(
|
this._reportError(
|
||||||
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`);
|
new Error(
|
||||||
|
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`),
|
||||||
|
moduleType);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
importedModules.push(importedModuleSummary);
|
importedModules.push(importedModuleSummary);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
this._reportError(
|
||||||
`Unexpected value '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`);
|
new Error(
|
||||||
|
`Unexpected value '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`),
|
||||||
|
moduleType);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -413,15 +410,17 @@ export class CompileMetadataResolver {
|
|||||||
if (meta.exports) {
|
if (meta.exports) {
|
||||||
flattenAndDedupeArray(meta.exports).forEach((exportedType) => {
|
flattenAndDedupeArray(meta.exports).forEach((exportedType) => {
|
||||||
if (!isValidType(exportedType)) {
|
if (!isValidType(exportedType)) {
|
||||||
throw new Error(
|
this._reportError(
|
||||||
`Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`);
|
new Error(
|
||||||
|
`Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`),
|
||||||
|
moduleType);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
const exportedModuleSummary = this._loadNgModuleSummary(exportedType, isSync);
|
const exportedModuleSummary = this.getNgModuleSummary(exportedType);
|
||||||
if (exportedModuleSummary) {
|
if (exportedModuleSummary) {
|
||||||
exportedModules.push(exportedModuleSummary);
|
exportedModules.push(exportedModuleSummary);
|
||||||
} else {
|
} else {
|
||||||
exportedNonModuleIdentifiers.push(
|
exportedNonModuleIdentifiers.push(this._getIdentifierMetadata(exportedType));
|
||||||
this._getIdentifierMetadata(exportedType, staticTypeModuleUrl(exportedType)));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -432,27 +431,28 @@ export class CompileMetadataResolver {
|
|||||||
if (meta.declarations) {
|
if (meta.declarations) {
|
||||||
flattenAndDedupeArray(meta.declarations).forEach((declaredType) => {
|
flattenAndDedupeArray(meta.declarations).forEach((declaredType) => {
|
||||||
if (!isValidType(declaredType)) {
|
if (!isValidType(declaredType)) {
|
||||||
throw new Error(
|
this._reportError(
|
||||||
`Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`);
|
new Error(
|
||||||
|
`Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`),
|
||||||
|
moduleType);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
const declaredIdentifier =
|
const declaredIdentifier = this._getIdentifierMetadata(declaredType);
|
||||||
this._getIdentifierMetadata(declaredType, staticTypeModuleUrl(declaredType));
|
|
||||||
if (this._directiveResolver.isDirective(declaredType)) {
|
if (this._directiveResolver.isDirective(declaredType)) {
|
||||||
transitiveModule.directivesSet.add(declaredType);
|
transitiveModule.addDirective(declaredIdentifier);
|
||||||
transitiveModule.directives.push(declaredIdentifier);
|
|
||||||
declaredDirectives.push(declaredIdentifier);
|
declaredDirectives.push(declaredIdentifier);
|
||||||
this._addTypeToModule(declaredType, moduleType);
|
this._addTypeToModule(declaredType, moduleType);
|
||||||
transitiveModule.directiveLoaders.push(
|
|
||||||
() => this._loadDirectiveMetadata(declaredType, isSync));
|
|
||||||
} else if (this._pipeResolver.isPipe(declaredType)) {
|
} else if (this._pipeResolver.isPipe(declaredType)) {
|
||||||
transitiveModule.pipesSet.add(declaredType);
|
transitiveModule.addPipe(declaredIdentifier);
|
||||||
transitiveModule.pipes.push(declaredIdentifier);
|
transitiveModule.pipes.push(declaredIdentifier);
|
||||||
declaredPipes.push(declaredIdentifier);
|
declaredPipes.push(declaredIdentifier);
|
||||||
this._addTypeToModule(declaredType, moduleType);
|
this._addTypeToModule(declaredType, moduleType);
|
||||||
this._loadPipeMetadata(declaredType);
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
this._reportError(
|
||||||
`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`);
|
new Error(
|
||||||
|
`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`),
|
||||||
|
moduleType);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -462,11 +462,15 @@ export class CompileMetadataResolver {
|
|||||||
exportedNonModuleIdentifiers.forEach((exportedId) => {
|
exportedNonModuleIdentifiers.forEach((exportedId) => {
|
||||||
if (transitiveModule.directivesSet.has(exportedId.reference)) {
|
if (transitiveModule.directivesSet.has(exportedId.reference)) {
|
||||||
exportedDirectives.push(exportedId);
|
exportedDirectives.push(exportedId);
|
||||||
|
transitiveModule.addExportedDirective(exportedId);
|
||||||
} else if (transitiveModule.pipesSet.has(exportedId.reference)) {
|
} else if (transitiveModule.pipesSet.has(exportedId.reference)) {
|
||||||
exportedPipes.push(exportedId);
|
exportedPipes.push(exportedId);
|
||||||
|
transitiveModule.addExportedPipe(exportedId);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
this._reportError(
|
||||||
`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringify(exportedId.reference)} from ${stringify(moduleType)} as it was neither declared nor imported!`);
|
new Error(
|
||||||
|
`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringify(exportedId.reference)} from ${stringify(moduleType)} as it was neither declared nor imported!`),
|
||||||
|
moduleType);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -474,24 +478,26 @@ export class CompileMetadataResolver {
|
|||||||
// so that they overwrite any other provider we already added.
|
// so that they overwrite any other provider we already added.
|
||||||
if (meta.providers) {
|
if (meta.providers) {
|
||||||
providers.push(...this._getProvidersMetadata(
|
providers.push(...this._getProvidersMetadata(
|
||||||
meta.providers, entryComponents, `provider for the NgModule '${stringify(moduleType)}'`));
|
meta.providers, entryComponents, `provider for the NgModule '${stringify(moduleType)}'`,
|
||||||
|
[], moduleType));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.entryComponents) {
|
if (meta.entryComponents) {
|
||||||
entryComponents.push(
|
entryComponents.push(
|
||||||
...flattenAndDedupeArray(meta.entryComponents)
|
...flattenAndDedupeArray(meta.entryComponents).map(type => this._getTypeMetadata(type)));
|
||||||
.map(type => this._getTypeMetadata(type, staticTypeModuleUrl(type))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.bootstrap) {
|
if (meta.bootstrap) {
|
||||||
const typeMetadata = flattenAndDedupeArray(meta.bootstrap).map(type => {
|
flattenAndDedupeArray(meta.bootstrap).forEach(type => {
|
||||||
if (!isValidType(type)) {
|
if (!isValidType(type)) {
|
||||||
throw new Error(
|
this._reportError(
|
||||||
`Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`);
|
new Error(
|
||||||
|
`Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`),
|
||||||
|
moduleType);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return this._getTypeMetadata(type, staticTypeModuleUrl(type));
|
bootstrapComponents.push(this._getTypeMetadata(type));
|
||||||
});
|
});
|
||||||
bootstrapComponents.push(...typeMetadata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entryComponents.push(...bootstrapComponents);
|
entryComponents.push(...bootstrapComponents);
|
||||||
@ -500,11 +506,8 @@ export class CompileMetadataResolver {
|
|||||||
schemas.push(...flattenAndDedupeArray(meta.schemas));
|
schemas.push(...flattenAndDedupeArray(meta.schemas));
|
||||||
}
|
}
|
||||||
|
|
||||||
transitiveModule.entryComponents.push(...entryComponents);
|
|
||||||
transitiveModule.providers.push(...providers);
|
|
||||||
|
|
||||||
compileMeta = new cpl.CompileNgModuleMetadata({
|
compileMeta = new cpl.CompileNgModuleMetadata({
|
||||||
type: this._getTypeMetadata(moduleType, staticTypeModuleUrl(moduleType)),
|
type: this._getTypeMetadata(moduleType),
|
||||||
providers,
|
providers,
|
||||||
entryComponents,
|
entryComponents,
|
||||||
bootstrapComponents,
|
bootstrapComponents,
|
||||||
@ -519,7 +522,9 @@ export class CompileMetadataResolver {
|
|||||||
id: meta.id,
|
id: meta.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
transitiveModule.modules.push(compileMeta.toInjectorSummary());
|
entryComponents.forEach((id) => transitiveModule.addEntryComponent(id));
|
||||||
|
providers.forEach((provider) => transitiveModule.addProvider(provider, compileMeta.type));
|
||||||
|
transitiveModule.addModule(compileMeta.type);
|
||||||
this._ngModuleCache.set(moduleType, compileMeta);
|
this._ngModuleCache.set(moduleType, compileMeta);
|
||||||
return compileMeta;
|
return compileMeta;
|
||||||
}
|
}
|
||||||
@ -548,10 +553,12 @@ export class CompileMetadataResolver {
|
|||||||
private _addTypeToModule(type: Type<any>, moduleType: Type<any>) {
|
private _addTypeToModule(type: Type<any>, moduleType: Type<any>) {
|
||||||
const oldModule = this._ngModuleOfTypes.get(type);
|
const oldModule = this._ngModuleOfTypes.get(type);
|
||||||
if (oldModule && oldModule !== moduleType) {
|
if (oldModule && oldModule !== moduleType) {
|
||||||
throw new Error(
|
this._reportError(
|
||||||
|
new Error(
|
||||||
`Type ${stringify(type)} is part of the declarations of 2 modules: ${stringify(oldModule)} and ${stringify(moduleType)}! ` +
|
`Type ${stringify(type)} is part of the declarations of 2 modules: ${stringify(oldModule)} and ${stringify(moduleType)}! ` +
|
||||||
`Please consider moving ${stringify(type)} to a higher module that imports ${stringify(oldModule)} and ${stringify(moduleType)}. ` +
|
`Please consider moving ${stringify(type)} to a higher module that imports ${stringify(oldModule)} and ${stringify(moduleType)}. ` +
|
||||||
`You can also create a new NgModule that exports and includes ${stringify(type)} then import that NgModule in ${stringify(oldModule)} and ${stringify(moduleType)}.`);
|
`You can also create a new NgModule that exports and includes ${stringify(type)} then import that NgModule in ${stringify(oldModule)} and ${stringify(moduleType)}.`),
|
||||||
|
moduleType);
|
||||||
}
|
}
|
||||||
this._ngModuleOfTypes.set(type, moduleType);
|
this._ngModuleOfTypes.set(type, moduleType);
|
||||||
}
|
}
|
||||||
@ -560,50 +567,59 @@ export class CompileMetadataResolver {
|
|||||||
importedModules: cpl.CompileNgModuleSummary[],
|
importedModules: cpl.CompileNgModuleSummary[],
|
||||||
exportedModules: cpl.CompileNgModuleSummary[]): cpl.TransitiveCompileNgModuleMetadata {
|
exportedModules: cpl.CompileNgModuleSummary[]): cpl.TransitiveCompileNgModuleMetadata {
|
||||||
// collect `providers` / `entryComponents` from all imported and all exported modules
|
// collect `providers` / `entryComponents` from all imported and all exported modules
|
||||||
const transitiveModules = getTransitiveImportedModules(importedModules.concat(exportedModules));
|
const result = new cpl.TransitiveCompileNgModuleMetadata();
|
||||||
const providers = flattenArray(transitiveModules.map((ngModule) => ngModule.providers));
|
const modulesByToken = new Map<any, Set<any>>();
|
||||||
const entryComponents =
|
importedModules.concat(exportedModules).forEach((modSummary) => {
|
||||||
flattenArray(transitiveModules.map((ngModule) => ngModule.entryComponents));
|
modSummary.modules.forEach((mod) => result.addModule(mod));
|
||||||
|
modSummary.entryComponents.forEach((comp) => result.addEntryComponent(comp));
|
||||||
const transitiveExportedModules = getTransitiveExportedModules(importedModules);
|
const addedTokens = new Set<any>();
|
||||||
const directives =
|
modSummary.providers.forEach((entry) => {
|
||||||
flattenArray(transitiveExportedModules.map((ngModule) => ngModule.exportedDirectives));
|
const tokenRef = cpl.tokenReference(entry.provider.token);
|
||||||
const pipes = flattenArray(transitiveExportedModules.map((ngModule) => ngModule.exportedPipes));
|
let prevModules = modulesByToken.get(tokenRef);
|
||||||
const directiveLoaders =
|
if (!prevModules) {
|
||||||
ListWrapper.flatten(transitiveExportedModules.map(ngModule => ngModule.directiveLoaders));
|
prevModules = new Set<any>();
|
||||||
return new cpl.TransitiveCompileNgModuleMetadata(
|
modulesByToken.set(tokenRef, prevModules);
|
||||||
transitiveModules, providers, entryComponents, directives, pipes, directiveLoaders);
|
}
|
||||||
|
const moduleRef = entry.module.reference;
|
||||||
|
// Note: the providers of one module may still contain multiple providers
|
||||||
|
// per token (e.g. for multi providers), and we need to preserve these.
|
||||||
|
if (addedTokens.has(tokenRef) || !prevModules.has(moduleRef)) {
|
||||||
|
prevModules.add(moduleRef);
|
||||||
|
addedTokens.add(tokenRef);
|
||||||
|
result.addProvider(entry.provider, entry.module);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
exportedModules.forEach((modSummary) => {
|
||||||
|
modSummary.exportedDirectives.forEach((id) => result.addExportedDirective(id));
|
||||||
|
modSummary.exportedPipes.forEach((id) => result.addExportedPipe(id));
|
||||||
|
});
|
||||||
|
importedModules.forEach((modSummary) => {
|
||||||
|
modSummary.exportedDirectives.forEach((id) => result.addDirective(id));
|
||||||
|
modSummary.exportedPipes.forEach((id) => result.addPipe(id));
|
||||||
|
});
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getIdentifierMetadata(type: Type<any>, moduleUrl: string):
|
private _getIdentifierMetadata(type: Type<any>): cpl.CompileIdentifierMetadata {
|
||||||
cpl.CompileIdentifierMetadata {
|
|
||||||
type = resolveForwardRef(type);
|
type = resolveForwardRef(type);
|
||||||
return new cpl.CompileIdentifierMetadata(
|
return {reference: type};
|
||||||
{name: this.sanitizeTokenName(type), moduleUrl, reference: type});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getTypeMetadata(type: Type<any>, moduleUrl: string, dependencies: any[] = null):
|
private _getTypeMetadata(type: Type<any>, dependencies: any[] = null): cpl.CompileTypeMetadata {
|
||||||
cpl.CompileTypeMetadata {
|
const identifier = this._getIdentifierMetadata(type);
|
||||||
const identifier = this._getIdentifierMetadata(type, moduleUrl);
|
return {
|
||||||
return new cpl.CompileTypeMetadata({
|
|
||||||
name: identifier.name,
|
|
||||||
moduleUrl: identifier.moduleUrl,
|
|
||||||
reference: identifier.reference,
|
reference: identifier.reference,
|
||||||
diDeps: this._getDependenciesMetadata(identifier.reference, dependencies),
|
diDeps: this._getDependenciesMetadata(identifier.reference, dependencies),
|
||||||
lifecycleHooks:
|
lifecycleHooks:
|
||||||
LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, identifier.reference)),
|
LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, identifier.reference)),
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getFactoryMetadata(factory: Function, moduleUrl: string, dependencies: any[] = null):
|
private _getFactoryMetadata(factory: Function, dependencies: any[] = null):
|
||||||
cpl.CompileFactoryMetadata {
|
cpl.CompileFactoryMetadata {
|
||||||
factory = resolveForwardRef(factory);
|
factory = resolveForwardRef(factory);
|
||||||
return new cpl.CompileFactoryMetadata({
|
return {reference: factory, diDeps: this._getDependenciesMetadata(factory, dependencies)};
|
||||||
name: this.sanitizeTokenName(factory),
|
|
||||||
moduleUrl,
|
|
||||||
reference: factory,
|
|
||||||
diDeps: this._getDependenciesMetadata(factory, dependencies)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -613,17 +629,21 @@ export class CompileMetadataResolver {
|
|||||||
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) {
|
||||||
throw new Error(
|
this._reportError(
|
||||||
`Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringify(pipeType)}.`);
|
new Error(
|
||||||
|
`Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringify(pipeType)}.`),
|
||||||
|
pipeType);
|
||||||
}
|
}
|
||||||
return pipeMeta;
|
return pipeMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPipeSummary(pipeType: any): cpl.CompilePipeSummary {
|
getPipeSummary(pipeType: any): cpl.CompilePipeSummary {
|
||||||
const pipeSummary = this._pipeSummaryCache.get(pipeType);
|
const pipeSummary =
|
||||||
|
<cpl.CompilePipeSummary>this._loadSummary(pipeType, cpl.CompileSummaryKind.Pipe);
|
||||||
if (!pipeSummary) {
|
if (!pipeSummary) {
|
||||||
throw new Error(
|
this._reportError(
|
||||||
`Illegal state: getPipeSummary can only be called after loadNgModuleMetadata for a module that imports it. Pipe ${stringify(pipeType)}.`);
|
new Error(`Illegal state: Could not load the summary for pipe ${stringify(pipeType)}.`),
|
||||||
|
pipeType);
|
||||||
}
|
}
|
||||||
return pipeSummary;
|
return pipeSummary;
|
||||||
}
|
}
|
||||||
@ -641,12 +661,12 @@ export class CompileMetadataResolver {
|
|||||||
const pipeAnnotation = this._pipeResolver.resolve(pipeType);
|
const pipeAnnotation = this._pipeResolver.resolve(pipeType);
|
||||||
|
|
||||||
const pipeMeta = new cpl.CompilePipeMetadata({
|
const pipeMeta = new cpl.CompilePipeMetadata({
|
||||||
type: this._getTypeMetadata(pipeType, staticTypeModuleUrl(pipeType)),
|
type: this._getTypeMetadata(pipeType),
|
||||||
name: pipeAnnotation.name,
|
name: pipeAnnotation.name,
|
||||||
pure: pipeAnnotation.pure
|
pure: pipeAnnotation.pure
|
||||||
});
|
});
|
||||||
this._pipeCache.set(pipeType, pipeMeta);
|
this._pipeCache.set(pipeType, pipeMeta);
|
||||||
this._pipeSummaryCache.set(pipeType, pipeMeta.toSummary());
|
this._summaryCache.set(pipeType, pipeMeta.toSummary());
|
||||||
return pipeMeta;
|
return pipeMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -689,22 +709,23 @@ export class CompileMetadataResolver {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new cpl.CompileDiDependencyMetadata({
|
return {
|
||||||
isAttribute,
|
isAttribute,
|
||||||
isHost,
|
isHost,
|
||||||
isSelf,
|
isSelf,
|
||||||
isSkipSelf,
|
isSkipSelf,
|
||||||
isOptional,
|
isOptional,
|
||||||
token: this._getTokenMetadata(token)
|
token: this._getTokenMetadata(token)
|
||||||
});
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (hasUnknownDeps) {
|
if (hasUnknownDeps) {
|
||||||
const depsTokens =
|
const depsTokens =
|
||||||
dependenciesMetadata.map((dep) => dep ? stringify(dep.token) : '?').join(', ');
|
dependenciesMetadata.map((dep) => dep ? stringify(dep.token) : '?').join(', ');
|
||||||
throw new Error(
|
this._reportError(
|
||||||
`Can't resolve all parameters for ${stringify(typeOrFunc)}: (${depsTokens}).`);
|
new Error(`Can't resolve all parameters for ${stringify(typeOrFunc)}: (${depsTokens}).`),
|
||||||
|
typeOrFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
return dependenciesMetadata;
|
return dependenciesMetadata;
|
||||||
@ -714,41 +735,27 @@ export class CompileMetadataResolver {
|
|||||||
token = resolveForwardRef(token);
|
token = resolveForwardRef(token);
|
||||||
let compileToken: cpl.CompileTokenMetadata;
|
let compileToken: cpl.CompileTokenMetadata;
|
||||||
if (typeof token === 'string') {
|
if (typeof token === 'string') {
|
||||||
compileToken = new cpl.CompileTokenMetadata({value: token});
|
compileToken = {value: token};
|
||||||
} else {
|
} else {
|
||||||
compileToken = new cpl.CompileTokenMetadata({
|
compileToken = {identifier: {reference: token}};
|
||||||
identifier: new cpl.CompileIdentifierMetadata({
|
|
||||||
reference: token,
|
|
||||||
name: this.sanitizeTokenName(token),
|
|
||||||
moduleUrl: staticTypeModuleUrl(token)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return compileToken;
|
return compileToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getProvidersMetadata(
|
private _getProvidersMetadata(
|
||||||
providers: Provider[], targetEntryComponents: cpl.CompileIdentifierMetadata[],
|
providers: Provider[], targetEntryComponents: cpl.CompileIdentifierMetadata[],
|
||||||
debugInfo?: string): Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> {
|
debugInfo?: string, compileProviders: cpl.CompileProviderMetadata[] = [],
|
||||||
const compileProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
type?: any): cpl.CompileProviderMetadata[] {
|
||||||
providers.forEach((provider: any, providerIdx: number) => {
|
providers.forEach((provider: any, providerIdx: number) => {
|
||||||
provider = resolveForwardRef(provider);
|
|
||||||
if (provider && typeof provider == 'object' && provider.hasOwnProperty('provide')) {
|
|
||||||
provider = new cpl.ProviderMeta(provider.provide, provider);
|
|
||||||
}
|
|
||||||
let compileProvider: cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[];
|
|
||||||
if (Array.isArray(provider)) {
|
if (Array.isArray(provider)) {
|
||||||
compileProvider = this._getProvidersMetadata(provider, targetEntryComponents, debugInfo);
|
this._getProvidersMetadata(provider, targetEntryComponents, debugInfo, compileProviders);
|
||||||
} else if (provider instanceof cpl.ProviderMeta) {
|
|
||||||
const tokenMeta = this._getTokenMetadata(provider.token);
|
|
||||||
if (tokenMeta.reference ===
|
|
||||||
resolveIdentifierToken(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS).reference) {
|
|
||||||
targetEntryComponents.push(...this._getEntryComponentsFromProvider(provider));
|
|
||||||
} else {
|
} else {
|
||||||
compileProvider = this.getProviderMetadata(provider);
|
provider = resolveForwardRef(provider);
|
||||||
}
|
let providerMeta: cpl.ProviderMeta;
|
||||||
|
if (provider && typeof provider == 'object' && provider.hasOwnProperty('provide')) {
|
||||||
|
providerMeta = new cpl.ProviderMeta(provider.provide, provider);
|
||||||
} else if (isValidType(provider)) {
|
} else if (isValidType(provider)) {
|
||||||
compileProvider = this._getTypeMetadata(provider, staticTypeModuleUrl(provider));
|
providerMeta = new cpl.ProviderMeta(provider, {useClass: provider});
|
||||||
} else {
|
} else {
|
||||||
const providersInfo =
|
const providersInfo =
|
||||||
(<string[]>providers.reduce(
|
(<string[]>providers.reduce(
|
||||||
@ -764,31 +771,39 @@ export class CompileMetadataResolver {
|
|||||||
},
|
},
|
||||||
[]))
|
[]))
|
||||||
.join(', ');
|
.join(', ');
|
||||||
|
this._reportError(
|
||||||
throw new Error(
|
new Error(
|
||||||
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`);
|
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`),
|
||||||
|
type);
|
||||||
|
}
|
||||||
|
if (providerMeta.token === resolveIdentifier(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS)) {
|
||||||
|
targetEntryComponents.push(...this._getEntryComponentsFromProvider(providerMeta, type));
|
||||||
|
} else {
|
||||||
|
compileProviders.push(this.getProviderMetadata(providerMeta));
|
||||||
}
|
}
|
||||||
if (compileProvider) {
|
|
||||||
compileProviders.push(compileProvider);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return compileProviders;
|
return compileProviders;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta):
|
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta, type?: any):
|
||||||
cpl.CompileIdentifierMetadata[] {
|
cpl.CompileIdentifierMetadata[] {
|
||||||
const components: cpl.CompileIdentifierMetadata[] = [];
|
const components: cpl.CompileIdentifierMetadata[] = [];
|
||||||
const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
|
const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
|
||||||
|
|
||||||
if (provider.useFactory || provider.useExisting || provider.useClass) {
|
if (provider.useFactory || provider.useExisting || provider.useClass) {
|
||||||
throw new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`);
|
this._reportError(
|
||||||
|
new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`), type);
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!provider.multi) {
|
if (!provider.multi) {
|
||||||
throw new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`);
|
this._reportError(
|
||||||
|
new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`), type);
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
convertToCompileValue(provider.useValue, collectedIdentifiers);
|
extractIdentifiers(provider.useValue, collectedIdentifiers);
|
||||||
collectedIdentifiers.forEach((identifier) => {
|
collectedIdentifiers.forEach((identifier) => {
|
||||||
if (this._directiveResolver.isDirective(identifier.reference)) {
|
if (this._directiveResolver.isDirective(identifier.reference)) {
|
||||||
components.push(identifier);
|
components.push(identifier);
|
||||||
@ -801,26 +816,29 @@ export class CompileMetadataResolver {
|
|||||||
let compileDeps: cpl.CompileDiDependencyMetadata[];
|
let compileDeps: cpl.CompileDiDependencyMetadata[];
|
||||||
let compileTypeMetadata: cpl.CompileTypeMetadata = null;
|
let compileTypeMetadata: cpl.CompileTypeMetadata = null;
|
||||||
let compileFactoryMetadata: cpl.CompileFactoryMetadata = null;
|
let compileFactoryMetadata: cpl.CompileFactoryMetadata = null;
|
||||||
|
let token: cpl.CompileTokenMetadata = this._getTokenMetadata(provider.token);
|
||||||
|
|
||||||
if (provider.useClass) {
|
if (provider.useClass) {
|
||||||
compileTypeMetadata = this._getTypeMetadata(
|
compileTypeMetadata = this._getTypeMetadata(provider.useClass, provider.dependencies);
|
||||||
provider.useClass, staticTypeModuleUrl(provider.useClass), provider.dependencies);
|
|
||||||
compileDeps = compileTypeMetadata.diDeps;
|
compileDeps = compileTypeMetadata.diDeps;
|
||||||
|
if (provider.token === provider.useClass) {
|
||||||
|
// use the compileTypeMetadata as it contains information about lifecycleHooks...
|
||||||
|
token = {identifier: compileTypeMetadata};
|
||||||
|
}
|
||||||
} else if (provider.useFactory) {
|
} else if (provider.useFactory) {
|
||||||
compileFactoryMetadata = this._getFactoryMetadata(
|
compileFactoryMetadata = this._getFactoryMetadata(provider.useFactory, provider.dependencies);
|
||||||
provider.useFactory, staticTypeModuleUrl(provider.useFactory), provider.dependencies);
|
|
||||||
compileDeps = compileFactoryMetadata.diDeps;
|
compileDeps = compileFactoryMetadata.diDeps;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new cpl.CompileProviderMetadata({
|
return {
|
||||||
token: this._getTokenMetadata(provider.token),
|
token: token,
|
||||||
useClass: compileTypeMetadata,
|
useClass: compileTypeMetadata,
|
||||||
useValue: convertToCompileValue(provider.useValue, []),
|
useValue: provider.useValue,
|
||||||
useFactory: compileFactoryMetadata,
|
useFactory: compileFactoryMetadata,
|
||||||
useExisting: provider.useExisting ? this._getTokenMetadata(provider.useExisting) : null,
|
useExisting: provider.useExisting ? this._getTokenMetadata(provider.useExisting) : null,
|
||||||
deps: compileDeps,
|
deps: compileDeps,
|
||||||
multi: provider.multi
|
multi: provider.multi
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getQueriesMetadata(
|
private _getQueriesMetadata(
|
||||||
@ -848,52 +866,32 @@ export class CompileMetadataResolver {
|
|||||||
this._queryVarBindings(q.selector).map(varName => this._getTokenMetadata(varName));
|
this._queryVarBindings(q.selector).map(varName => this._getTokenMetadata(varName));
|
||||||
} else {
|
} else {
|
||||||
if (!q.selector) {
|
if (!q.selector) {
|
||||||
throw new Error(
|
this._reportError(
|
||||||
`Can't construct a query for the property "${propertyName}" of "${stringify(typeOrFunc)}" since the query selector wasn't defined.`);
|
new Error(
|
||||||
|
`Can't construct a query for the property "${propertyName}" of "${stringify(typeOrFunc)}" since the query selector wasn't defined.`),
|
||||||
|
typeOrFunc);
|
||||||
}
|
}
|
||||||
selectors = [this._getTokenMetadata(q.selector)];
|
selectors = [this._getTokenMetadata(q.selector)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return new cpl.CompileQueryMetadata({
|
return {
|
||||||
selectors,
|
selectors,
|
||||||
first: q.first,
|
first: q.first,
|
||||||
descendants: q.descendants, propertyName,
|
descendants: q.descendants, propertyName,
|
||||||
read: q.read ? this._getTokenMetadata(q.read) : null
|
read: q.read ? this._getTokenMetadata(q.read) : null
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function getTransitiveExportedModules(
|
private _reportError(error: any, type?: any, otherType?: any) {
|
||||||
modules: cpl.CompileNgModuleDirectiveSummary[],
|
if (this._errorCollector) {
|
||||||
targetModules: cpl.CompileNgModuleDirectiveSummary[] = [],
|
this._errorCollector(error, type);
|
||||||
visitedModules = new Set<Type<any>>()): cpl.CompileNgModuleDirectiveSummary[] {
|
if (otherType) {
|
||||||
modules.forEach((ngModule) => {
|
this._errorCollector(error, otherType);
|
||||||
if (!visitedModules.has(ngModule.type.reference)) {
|
}
|
||||||
visitedModules.add(ngModule.type.reference);
|
} else {
|
||||||
getTransitiveExportedModules(ngModule.exportedModules, targetModules, visitedModules);
|
throw error;
|
||||||
// Add after recursing so imported/exported modules are before the module itself.
|
|
||||||
// This is important for overwriting providers of imported modules!
|
|
||||||
targetModules.push(ngModule);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
return targetModules;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTransitiveImportedModules(
|
|
||||||
modules: cpl.CompileNgModuleInjectorSummary[],
|
|
||||||
targetModules: cpl.CompileNgModuleInjectorSummary[] = [],
|
|
||||||
visitedModules = new Set<Type<any>>()): cpl.CompileNgModuleInjectorSummary[] {
|
|
||||||
modules.forEach((ngModule) => {
|
|
||||||
if (!visitedModules.has(ngModule.type.reference)) {
|
|
||||||
visitedModules.add(ngModule.type.reference);
|
|
||||||
const nestedModules = ngModule.importedModules.concat(ngModule.exportedModules);
|
|
||||||
getTransitiveImportedModules(nestedModules, targetModules, visitedModules);
|
|
||||||
// Add after recursing so imported/exported modules are before the module itself.
|
|
||||||
// This is important for overwriting providers of imported modules!
|
|
||||||
targetModules.push(ngModule);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
return targetModules;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenArray(tree: any[], out: Array<any> = []): Array<any> {
|
function flattenArray(tree: any[], out: Array<any> = []): Array<any> {
|
||||||
@ -922,17 +920,13 @@ function flattenAndDedupeArray(tree: any[]): Array<any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isValidType(value: any): boolean {
|
function isValidType(value: any): boolean {
|
||||||
return isStaticSymbol(value) || (value instanceof Type);
|
return (value instanceof StaticSymbol) || (value instanceof Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
function staticTypeModuleUrl(value: any): string {
|
export function componentModuleUrl(
|
||||||
return isStaticSymbol(value) ? value.filePath : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function componentModuleUrl(
|
|
||||||
reflector: ReflectorReader, type: Type<any>, cmpMetadata: Component): string {
|
reflector: ReflectorReader, type: Type<any>, cmpMetadata: Component): string {
|
||||||
if (isStaticSymbol(type)) {
|
if (type instanceof StaticSymbol) {
|
||||||
return staticTypeModuleUrl(type);
|
return type.filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
const moduleId = cmpMetadata.moduleId;
|
const moduleId = cmpMetadata.moduleId;
|
||||||
@ -949,21 +943,12 @@ function componentModuleUrl(
|
|||||||
return reflector.importUri(type);
|
return reflector.importUri(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertToCompileValue(
|
function extractIdentifiers(value: any, targetIdentifiers: cpl.CompileIdentifierMetadata[]) {
|
||||||
value: any, targetIdentifiers: cpl.CompileIdentifierMetadata[]): any {
|
visitValue(value, new _CompileValueConverter(), targetIdentifiers);
|
||||||
return visitValue(value, new _CompileValueConverter(), targetIdentifiers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CompileValueConverter extends ValueTransformer {
|
class _CompileValueConverter extends ValueTransformer {
|
||||||
visitOther(value: any, targetIdentifiers: cpl.CompileIdentifierMetadata[]): any {
|
visitOther(value: any, targetIdentifiers: cpl.CompileIdentifierMetadata[]): any {
|
||||||
let identifier: cpl.CompileIdentifierMetadata;
|
targetIdentifiers.push({reference: value});
|
||||||
if (isStaticSymbol(value)) {
|
|
||||||
identifier = new cpl.CompileIdentifierMetadata(
|
|
||||||
{name: value.name, moduleUrl: value.filePath, reference: value});
|
|
||||||
} else {
|
|
||||||
identifier = new cpl.CompileIdentifierMetadata({reference: value});
|
|
||||||
}
|
|
||||||
targetIdentifiers.push(identifier);
|
|
||||||
return identifier;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,10 @@
|
|||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata} from './compile_metadata';
|
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, tokenName, tokenReference} from './compile_metadata';
|
||||||
import {createDiTokenExpression} from './compiler_util/identifier_util';
|
import {createDiTokenExpression} from './compiler_util/identifier_util';
|
||||||
import {isPresent} from './facade/lang';
|
import {isPresent} from './facade/lang';
|
||||||
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
|
import {Identifiers, createIdentifier, createIdentifierToken, resolveIdentifier} from './identifiers';
|
||||||
import {ClassBuilder, createClassStmt} from './output/class_builder';
|
import {ClassBuilder, createClassStmt} from './output/class_builder';
|
||||||
import * as o from './output/output_ast';
|
import * as o from './output/output_ast';
|
||||||
import {convertValueToOutputAst} from './output/value_util';
|
import {convertValueToOutputAst} from './output/value_util';
|
||||||
@ -35,9 +35,10 @@ export class NgModuleCompileResult {
|
|||||||
export class NgModuleCompiler {
|
export class NgModuleCompiler {
|
||||||
compile(ngModuleMeta: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[]):
|
compile(ngModuleMeta: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[]):
|
||||||
NgModuleCompileResult {
|
NgModuleCompileResult {
|
||||||
const sourceFileName = isPresent(ngModuleMeta.type.moduleUrl) ?
|
const moduleUrl = identifierModuleUrl(ngModuleMeta.type);
|
||||||
`in NgModule ${ngModuleMeta.type.name} in ${ngModuleMeta.type.moduleUrl}` :
|
const sourceFileName = isPresent(moduleUrl) ?
|
||||||
`in NgModule ${ngModuleMeta.type.name}`;
|
`in NgModule ${identifierName(ngModuleMeta.type)} in ${moduleUrl}` :
|
||||||
|
`in NgModule ${identifierName(ngModuleMeta.type)}`;
|
||||||
const sourceFile = new ParseSourceFile('', sourceFileName);
|
const sourceFile = new ParseSourceFile('', sourceFileName);
|
||||||
const sourceSpan = new ParseSourceSpan(
|
const sourceSpan = new ParseSourceSpan(
|
||||||
new ParseLocation(sourceFile, null, null, null),
|
new ParseLocation(sourceFile, null, null, null),
|
||||||
@ -46,8 +47,9 @@ 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 = new CompileIdentifierMetadata({name: entryComponent.name});
|
const id: CompileIdentifierMetadata = {reference: null};
|
||||||
if (ngModuleMeta.bootstrapComponents.indexOf(entryComponent) > -1) {
|
if (ngModuleMeta.bootstrapComponents.some(
|
||||||
|
(id) => id.reference === entryComponent.reference)) {
|
||||||
bootstrapComponentFactories.push(id);
|
bootstrapComponentFactories.push(id);
|
||||||
}
|
}
|
||||||
deps.push(new ComponentFactoryDependency(entryComponent, id));
|
deps.push(new ComponentFactoryDependency(entryComponent, id));
|
||||||
@ -59,21 +61,21 @@ export class NgModuleCompiler {
|
|||||||
const providerParser = new NgModuleProviderAnalyzer(ngModuleMeta, extraProviders, sourceSpan);
|
const providerParser = new NgModuleProviderAnalyzer(ngModuleMeta, extraProviders, sourceSpan);
|
||||||
providerParser.parse().forEach((provider) => builder.addProvider(provider));
|
providerParser.parse().forEach((provider) => builder.addProvider(provider));
|
||||||
const injectorClass = builder.build();
|
const injectorClass = builder.build();
|
||||||
const ngModuleFactoryVar = `${ngModuleMeta.type.name}NgFactory`;
|
const ngModuleFactoryVar = `${identifierName(ngModuleMeta.type)}NgFactory`;
|
||||||
const ngModuleFactoryStmt =
|
const ngModuleFactoryStmt =
|
||||||
o.variable(ngModuleFactoryVar)
|
o.variable(ngModuleFactoryVar)
|
||||||
.set(o.importExpr(resolveIdentifier(Identifiers.NgModuleFactory))
|
.set(o.importExpr(createIdentifier(Identifiers.NgModuleFactory))
|
||||||
.instantiate(
|
.instantiate(
|
||||||
[o.variable(injectorClass.name), o.importExpr(ngModuleMeta.type)],
|
[o.variable(injectorClass.name), o.importExpr(ngModuleMeta.type)],
|
||||||
o.importType(
|
o.importType(
|
||||||
resolveIdentifier(Identifiers.NgModuleFactory),
|
createIdentifier(Identifiers.NgModuleFactory),
|
||||||
[o.importType(ngModuleMeta.type)], [o.TypeModifier.Const])))
|
[o.importType(ngModuleMeta.type)], [o.TypeModifier.Const])))
|
||||||
.toDeclStmt(null, [o.StmtModifier.Final]);
|
.toDeclStmt(null, [o.StmtModifier.Final]);
|
||||||
|
|
||||||
const stmts: o.Statement[] = [injectorClass, ngModuleFactoryStmt];
|
const stmts: o.Statement[] = [injectorClass, ngModuleFactoryStmt];
|
||||||
if (ngModuleMeta.id) {
|
if (ngModuleMeta.id) {
|
||||||
const registerFactoryStmt =
|
const registerFactoryStmt =
|
||||||
o.importExpr(resolveIdentifier(Identifiers.RegisterModuleFactoryFn))
|
o.importExpr(createIdentifier(Identifiers.RegisterModuleFactoryFn))
|
||||||
.callFn([o.literal(ngModuleMeta.id), o.variable(ngModuleFactoryVar)])
|
.callFn([o.literal(ngModuleMeta.id), o.variable(ngModuleFactoryVar)])
|
||||||
.toStmt();
|
.toStmt();
|
||||||
stmts.push(registerFactoryStmt);
|
stmts.push(registerFactoryStmt);
|
||||||
@ -102,7 +104,7 @@ class _InjectorBuilder implements ClassBuilder {
|
|||||||
addProvider(resolvedProvider: ProviderAst) {
|
addProvider(resolvedProvider: ProviderAst) {
|
||||||
const providerValueExpressions =
|
const providerValueExpressions =
|
||||||
resolvedProvider.providers.map((provider) => this._getProviderValue(provider));
|
resolvedProvider.providers.map((provider) => this._getProviderValue(provider));
|
||||||
const propName = `_${resolvedProvider.token.name}_${this._instances.size}`;
|
const propName = `_${tokenName(resolvedProvider.token)}_${this._instances.size}`;
|
||||||
const instance = this._createProviderProperty(
|
const instance = this._createProviderProperty(
|
||||||
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
|
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
|
||||||
resolvedProvider.eager);
|
resolvedProvider.eager);
|
||||||
@ -110,12 +112,12 @@ class _InjectorBuilder implements ClassBuilder {
|
|||||||
this._destroyStmts.push(instance.callMethod('ngOnDestroy', []).toStmt());
|
this._destroyStmts.push(instance.callMethod('ngOnDestroy', []).toStmt());
|
||||||
}
|
}
|
||||||
this._tokens.push(resolvedProvider.token);
|
this._tokens.push(resolvedProvider.token);
|
||||||
this._instances.set(resolvedProvider.token.reference, instance);
|
this._instances.set(tokenReference(resolvedProvider.token), instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
build(): o.ClassStmt {
|
build(): o.ClassStmt {
|
||||||
const getMethodStmts: o.Statement[] = this._tokens.map((token) => {
|
const getMethodStmts: o.Statement[] = this._tokens.map((token) => {
|
||||||
const providerExpr = this._instances.get(token.reference);
|
const providerExpr = this._instances.get(tokenReference(token));
|
||||||
return new o.IfStmt(
|
return new o.IfStmt(
|
||||||
InjectMethodVars.token.identical(createDiTokenExpression(token)),
|
InjectMethodVars.token.identical(createDiTokenExpression(token)),
|
||||||
[new o.ReturnStatement(providerExpr)]);
|
[new o.ReturnStatement(providerExpr)]);
|
||||||
@ -143,13 +145,13 @@ class _InjectorBuilder implements ClassBuilder {
|
|||||||
o.literalArr(this._bootstrapComponentFactories.map(
|
o.literalArr(this._bootstrapComponentFactories.map(
|
||||||
(componentFactory) => o.importExpr(componentFactory)))
|
(componentFactory) => o.importExpr(componentFactory)))
|
||||||
];
|
];
|
||||||
const injClassName = `${this._ngModuleMeta.type.name}Injector`;
|
const injClassName = `${identifierName(this._ngModuleMeta.type)}Injector`;
|
||||||
return createClassStmt({
|
return createClassStmt({
|
||||||
name: injClassName,
|
name: injClassName,
|
||||||
ctorParams: [new o.FnParam(
|
ctorParams: [new o.FnParam(
|
||||||
InjectorProps.parent.name, o.importType(resolveIdentifier(Identifiers.Injector)))],
|
InjectorProps.parent.name, o.importType(createIdentifier(Identifiers.Injector)))],
|
||||||
parent: o.importExpr(
|
parent: o.importExpr(
|
||||||
resolveIdentifier(Identifiers.NgModuleInjector), [o.importType(this._ngModuleMeta.type)]),
|
createIdentifier(Identifiers.NgModuleInjector), [o.importType(this._ngModuleMeta.type)]),
|
||||||
parentArgs: parentArgs,
|
parentArgs: parentArgs,
|
||||||
builders: [{methods}, this]
|
builders: [{methods}, this]
|
||||||
});
|
});
|
||||||
@ -158,7 +160,7 @@ class _InjectorBuilder implements ClassBuilder {
|
|||||||
private _getProviderValue(provider: CompileProviderMetadata): o.Expression {
|
private _getProviderValue(provider: CompileProviderMetadata): o.Expression {
|
||||||
let result: o.Expression;
|
let result: o.Expression;
|
||||||
if (isPresent(provider.useExisting)) {
|
if (isPresent(provider.useExisting)) {
|
||||||
result = this._getDependency(new CompileDiDependencyMetadata({token: provider.useExisting}));
|
result = this._getDependency({token: provider.useExisting});
|
||||||
} else if (isPresent(provider.useFactory)) {
|
} else if (isPresent(provider.useFactory)) {
|
||||||
const deps = provider.deps || provider.useFactory.diDeps;
|
const deps = provider.deps || provider.useFactory.diDeps;
|
||||||
const depsExpr = deps.map((dep) => this._getDependency(dep));
|
const depsExpr = deps.map((dep) => this._getDependency(dep));
|
||||||
@ -215,13 +217,12 @@ class _InjectorBuilder implements ClassBuilder {
|
|||||||
}
|
}
|
||||||
if (!dep.isSkipSelf) {
|
if (!dep.isSkipSelf) {
|
||||||
if (dep.token &&
|
if (dep.token &&
|
||||||
(dep.token.reference === resolveIdentifierToken(Identifiers.Injector).reference ||
|
(tokenReference(dep.token) === resolveIdentifier(Identifiers.Injector) ||
|
||||||
dep.token.reference ===
|
tokenReference(dep.token) === resolveIdentifier(Identifiers.ComponentFactoryResolver))) {
|
||||||
resolveIdentifierToken(Identifiers.ComponentFactoryResolver).reference)) {
|
|
||||||
result = o.THIS_EXPR;
|
result = o.THIS_EXPR;
|
||||||
}
|
}
|
||||||
if (!result) {
|
if (!result) {
|
||||||
result = this._instances.get(dep.token.reference);
|
result = this._instances.get(tokenReference(dep.token));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import {Injectable, NgModule, Type} from '@angular/core';
|
import {Injectable, NgModule, Type} from '@angular/core';
|
||||||
|
|
||||||
|
import {ListWrapper} from './facade/collection';
|
||||||
import {isPresent, stringify} from './facade/lang';
|
import {isPresent, stringify} from './facade/lang';
|
||||||
import {ReflectorReader, reflector} from './private_import_core';
|
import {ReflectorReader, reflector} from './private_import_core';
|
||||||
|
|
||||||
@ -25,7 +26,8 @@ export class NgModuleResolver {
|
|||||||
isNgModule(type: any) { return this._reflector.annotations(type).some(_isNgModuleMetadata); }
|
isNgModule(type: any) { return this._reflector.annotations(type).some(_isNgModuleMetadata); }
|
||||||
|
|
||||||
resolve(type: Type<any>, throwIfNotFound = true): NgModule {
|
resolve(type: Type<any>, throwIfNotFound = true): NgModule {
|
||||||
const ngModuleMeta: NgModule = this._reflector.annotations(type).find(_isNgModuleMetadata);
|
const ngModuleMeta: NgModule =
|
||||||
|
ListWrapper.findLast(this._reflector.annotations(type), _isNgModuleMetadata);
|
||||||
|
|
||||||
if (isPresent(ngModuleMeta)) {
|
if (isPresent(ngModuleMeta)) {
|
||||||
return ngModuleMeta;
|
return ngModuleMeta;
|
||||||
|
@ -367,8 +367,8 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
|
|||||||
ctx.print(`{`, useNewLine);
|
ctx.print(`{`, useNewLine);
|
||||||
ctx.incIndent();
|
ctx.incIndent();
|
||||||
this.visitAllObjects(entry => {
|
this.visitAllObjects(entry => {
|
||||||
ctx.print(`${escapeIdentifier(entry[0], this._escapeDollarInStrings, false)}: `);
|
ctx.print(`${escapeIdentifier(entry.key, this._escapeDollarInStrings, entry.quoted)}: `);
|
||||||
entry[1].visitExpression(this, ctx);
|
entry.value.visitExpression(this, ctx);
|
||||||
}, ast.entries, ctx, ',', useNewLine);
|
}, ast.entries, ctx, ',', useNewLine);
|
||||||
ctx.decIndent();
|
ctx.decIndent();
|
||||||
ctx.print(`}`, useNewLine);
|
ctx.print(`}`, useNewLine);
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import {identifierModuleUrl, identifierName} from '../compile_metadata';
|
||||||
import {isBlank, isPresent} from '../facade/lang';
|
import {isBlank, isPresent} from '../facade/lang';
|
||||||
|
|
||||||
import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
|
import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
|
||||||
@ -38,18 +39,21 @@ class JsEmitterVisitor extends AbstractJsEmitterVisitor {
|
|||||||
constructor(private _moduleUrl: string) { super(); }
|
constructor(private _moduleUrl: string) { super(); }
|
||||||
|
|
||||||
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
|
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
|
||||||
if (isBlank(ast.value.name)) {
|
const name = identifierName(ast.value);
|
||||||
|
const moduleUrl = identifierModuleUrl(ast.value);
|
||||||
|
if (isBlank(name)) {
|
||||||
|
console.error('>>>', ast.value);
|
||||||
throw new Error(`Internal error: unknown identifier ${ast.value}`);
|
throw new Error(`Internal error: unknown identifier ${ast.value}`);
|
||||||
}
|
}
|
||||||
if (isPresent(ast.value.moduleUrl) && ast.value.moduleUrl != this._moduleUrl) {
|
if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) {
|
||||||
let prefix = this.importsWithPrefixes.get(ast.value.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(ast.value.moduleUrl, prefix);
|
this.importsWithPrefixes.set(moduleUrl, prefix);
|
||||||
}
|
}
|
||||||
ctx.print(`${prefix}.`);
|
ctx.print(`${prefix}.`);
|
||||||
}
|
}
|
||||||
ctx.print(ast.value.name);
|
ctx.print(name);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
|
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
@ -43,14 +43,14 @@ export class BuiltinType extends Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ExternalType extends Type {
|
export class ExpressionType extends Type {
|
||||||
constructor(
|
constructor(
|
||||||
public value: CompileIdentifierMetadata, public typeParams: Type[] = null,
|
public value: Expression, public typeParams: Type[] = null,
|
||||||
modifiers: TypeModifier[] = null) {
|
modifiers: TypeModifier[] = null) {
|
||||||
super(modifiers);
|
super(modifiers);
|
||||||
}
|
}
|
||||||
visitType(visitor: TypeVisitor, context: any): any {
|
visitType(visitor: TypeVisitor, context: any): any {
|
||||||
return visitor.visitExternalType(this, context);
|
return visitor.visitExpressionType(this, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ export var NULL_TYPE = new BuiltinType(BuiltinTypeName.Null);
|
|||||||
|
|
||||||
export interface TypeVisitor {
|
export interface TypeVisitor {
|
||||||
visitBuiltintType(type: BuiltinType, context: any): any;
|
visitBuiltintType(type: BuiltinType, context: any): any;
|
||||||
visitExternalType(type: ExternalType, context: any): any;
|
visitExpressionType(type: ExpressionType, context: any): any;
|
||||||
visitArrayType(type: ArrayType, context: any): any;
|
visitArrayType(type: ArrayType, context: any): any;
|
||||||
visitMapType(type: MapType, context: any): any;
|
visitMapType(type: MapType, context: any): any;
|
||||||
}
|
}
|
||||||
@ -413,10 +413,13 @@ export class LiteralArrayExpr extends Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class LiteralMapEntry {
|
||||||
|
constructor(public key: string, public value: Expression, public quoted: boolean = false) {}
|
||||||
|
}
|
||||||
|
|
||||||
export class LiteralMapExpr extends Expression {
|
export class LiteralMapExpr extends Expression {
|
||||||
public valueType: Type = null;
|
public valueType: Type = null;
|
||||||
constructor(public entries: [string, Expression][], type: MapType = null) {
|
constructor(public entries: LiteralMapEntry[], type: MapType = null) {
|
||||||
super(type);
|
super(type);
|
||||||
if (isPresent(type)) {
|
if (isPresent(type)) {
|
||||||
this.valueType = type.valueType;
|
this.valueType = type.valueType;
|
||||||
@ -677,7 +680,8 @@ export class ExpressionTransformer implements StatementVisitor, ExpressionVisito
|
|||||||
|
|
||||||
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any {
|
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any {
|
||||||
const entries = ast.entries.map(
|
const entries = ast.entries.map(
|
||||||
(entry): [string, Expression] => [entry[0], entry[1].visitExpression(this, context), ]);
|
(entry): LiteralMapEntry => new LiteralMapEntry(
|
||||||
|
entry.key, entry.value.visitExpression(this, context), entry.quoted));
|
||||||
return new LiteralMapExpr(entries);
|
return new LiteralMapExpr(entries);
|
||||||
}
|
}
|
||||||
visitAllExpressions(exprs: Expression[], context: any): Expression[] {
|
visitAllExpressions(exprs: Expression[], context: any): Expression[] {
|
||||||
@ -791,7 +795,7 @@ export class RecursiveExpressionVisitor implements StatementVisitor, ExpressionV
|
|||||||
return ast;
|
return ast;
|
||||||
}
|
}
|
||||||
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any {
|
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any {
|
||||||
ast.entries.forEach((entry) => (<Expression>entry[1]).visitExpression(this, context));
|
ast.entries.forEach((entry) => entry.value.visitExpression(this, context));
|
||||||
return ast;
|
return ast;
|
||||||
}
|
}
|
||||||
visitAllExpressions(exprs: Expression[], context: any): void {
|
visitAllExpressions(exprs: Expression[], context: any): void {
|
||||||
@ -876,8 +880,14 @@ export function importExpr(id: CompileIdentifierMetadata, typeParams: Type[] = n
|
|||||||
|
|
||||||
export function importType(
|
export function importType(
|
||||||
id: CompileIdentifierMetadata, typeParams: Type[] = null,
|
id: CompileIdentifierMetadata, typeParams: Type[] = null,
|
||||||
typeModifiers: TypeModifier[] = null): ExternalType {
|
typeModifiers: TypeModifier[] = null): ExpressionType {
|
||||||
return isPresent(id) ? new ExternalType(id, typeParams, typeModifiers) : null;
|
return isPresent(id) ? expressionType(importExpr(id), typeParams, typeModifiers) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function expressionType(
|
||||||
|
expr: Expression, typeParams: Type[] = null,
|
||||||
|
typeModifiers: TypeModifier[] = null): ExpressionType {
|
||||||
|
return isPresent(expr) ? new ExpressionType(expr, typeParams, typeModifiers) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function literalArr(values: Expression[], type: Type = null): LiteralArrayExpr {
|
export function literalArr(values: Expression[], type: Type = null): LiteralArrayExpr {
|
||||||
@ -885,7 +895,7 @@ export function literalArr(values: Expression[], type: Type = null): LiteralArra
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function literalMap(values: [string, Expression][], type: MapType = null): LiteralMapExpr {
|
export function literalMap(values: [string, Expression][], type: MapType = null): LiteralMapExpr {
|
||||||
return new LiteralMapExpr(values, type);
|
return new LiteralMapExpr(values.map(entry => new LiteralMapEntry(entry[0], entry[1])), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function not(expr: Expression): NotExpr {
|
export function not(expr: Expression): NotExpr {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user